大家知道计算机的网络唤醒功能需要网卡支持,如果如果支持的话必须先开启

有很多方法开启这个功能,有的需要进入 本地连接 的属性----配置就可以开启,看一下我的电脑

 

有的电脑 需要 进入COMS设置BIOS才可以开启网络唤醒。

 

废话少说,先说一下网络唤醒的原理:在关机状态下,如果网卡收到一连串的特殊的脉冲,就会点亮电源。对于每个网卡来说,脉冲是不一样的。发送网络数据包的时候,网卡就会在发出一系列的脉冲到网线上。因此,特殊的脉冲就是特殊的数据包。

这个数据包长度是108 Bytes,其中前6个字节 是0xFF填充

后面96个字节是用要唤醒网卡对应的MAC地址填充,96=16x6 就是说 MAC地址重复16次

最后6个字节是密码,没有密码的话可以0x00填充

就像这个样子
ff ff ff ff ff ff//前面6个ff
00 16 e6 93 23 45//16次要唤醒网卡的MAC地址
00 16 e6 93 23 45
00 16 e6 93 23 45
00 16 e6 93 23 45
00 16 e6 93 23 45
00 16 e6 93 23 45
00 16 e6 93 23 45
00 16 e6 93 23 45
00 16 e6 93 23 45
00 16 e6 93 23 45
00 16 e6 93 23 45
00 16 e6 93 23 45
00 16 e6 93 23 45
00 16 e6 93 23 45
00 16 e6 93 23 45
00 16 e6 93 23 45
00 00 d0 0d 6f 00//密码

并且这个特殊的数据包可以封装在任何协议之上,802.3协议,ip 协议,tcp协议,udp协议,ipx等等

也就是说不管怎么着,网络中只要出现了 这个数据包对应的脉冲就会唤醒主机。

有一个问题 就是 如果 LAN 中使用了交换机,那么这个数据包就有可能不会发送给那个主机,那么也就不会收的脉冲,解决的办法就是使用广播。

802.3 协议封包。就是以太网协议方法封包,只要把目的MAC地址赋值FFFFFFFFFFFF,这个MAC地址实际就是广播地址。(其实,在以太网中,所有的广 播型数据报,都是把目的MAC地址赋值FFFFFFFFFFFF,前提是使用255.255.255.255这个广播IP地址,写源码的时候会介绍)然后 把上面特殊的数据包作为数据发送出去就可以了,但是 windows 本身 并没有提供发送 以太网封包的接口,需要第三方软件,如winpcap,这个方法不可取。。有些人就是利用winpcap,发送以太网封包来敛财,如狗屁执法官,垃圾剪刀 手,ARP工具类软件.....

 

IP 协议封包。 使用IP协议的原始套接字方法 ,这是个 可以考虑的办法,就像 就造一个ping 命令的ICMP报文一样。(注意:ping命令使用的是ICMP协议,而ICMP封包是直接作为数据封装在IP协议包里的,与TCP,UDP是并列的关 系,都是IP协议的子集,而大多数的书籍把 IP和ICMP并列,说是工作在网络层,也是没有错的,我们是从编程的角度出发,他们是从OSI7层的角度出发的)但是要想把上面说的唤醒网络封包直接作 为数据封装在IP协议里,需要原始套接字编程,要构造IP头,(ICMP协议不需要构造IP头,为什么,因为构造套接字的时候有关于ICMP的选项 IPPROTO_ICMP),并且windows sp2对原始套接字编程做了限制,方法 不可取。

TCP协议,TCP协议编程方法简单,也不需要什么校验和 之类的过程,只要把 网路唤醒的封包

作为数据发送就可以了,但是有一点,TCP协议不支持广播,也就是说即使你把255.255.255.255作为目的IP,就算套接字创建成功了,那也发送失败。。。

 

UDP协议,对,我们就用udp协议,可以广播,实现简单

方法如下:

CAsyncSocket s;           
 s.Create(atol(40000),SOCK_DGRAM); //创建一个UDP端口随便这里是40000
 BOOL bOptVal = TRUE;
 if (s.SetSockOpt(SO_BROADCAST,(char*)&bOptVal,sizeof(BOOL))==
                                 SOCKET_ERROR) //这个地方是把套接字变成广播型的
  return;
 
 s.SendTo(magicP,MAGIC_PACKET_LENGTH,atol(40000));
 s.Close();
 上面得magicp就是网络唤醒包的数据包
对于不喜欢使用CAsyncSocket类  的朋友可以使用下面方法发送一个广播
 SOCKET s;
 BOOL bBroadcast=TRUE;
 char *pack="this is a test";
 SOCKADDR_IN bcast;
 WSADATA lpata;
 WSAStartup(MAKEWORD(2,2),&lpata);
 bcast.sin_addr.S_un.S_addr=inet_addr("255.255.255.255");//广播的条件1,广播地址
 bcast.sin_family=AF_INET;
 bcast.sin_port=htons(5050);
 s=socket(AF_INET,SOCK_DGRAM,0);
 setsockopt(s,SOL_SOCKET,SO_BROADCAST,(char *)&bBroadcast,sizeof(BOOL));//没有本句,就不会发送广播报文
 sendto(s,pack,strlen(pack),0,(SOCKADDR *)&bcast,sizeof(bcast));
 WSACleanup();
  ---------------------------------------
还是说一下pack包的构建吧,本来打算省略的~~~!!!
例如某个网卡MAC地址为 00 16 e6 93 23 45
CString macAddr="0016e6932345";
CString pass="010203040506"
BYTE pack[108];
int i;
 for (int i=0;i<6;i++) //填充6个0xff     
 { 
    pack[i] = 0xff;
 }
 for (i=0;i<6;i++) 
 { 
    pack[i+6] = HexStrToInt(macAddr.Mid(i*2,2));//HexStrToInt是个自定义函数 后面给出
 }
 for (i=0;i<15;i++)
  {
     memcpy(&pack[(i+2)*6],&pack[6],6);
  }
if(有密码)
  {
     for (i=0;i<6;i++) 
     {
        pack[i+102] = HexStrToInt(pass.Mid(i*2,2));//
     }
 }
sendto(s,(const char *)pack,108,0,(SOCKADDR *)&bcast,sizeof(bcast));//第二个参数要强制转换类型
---------------------------------------------------------------
UINT HexStrToInt(CString hexStr)//自定义函数
{
 char *stop;
 char  num[3];
 UINT res = 0;
 if (hexStr.GetLength()>2) {
  TRACE(_T("(Length) Invalid Input!"));
  return 0;  
 }
 memset(num,'\0',3);
 strcpy(num,hexStr);
 res = strtol(num,&stop,16);
 if (res==LONG_MAX || res==LONG_MIN || res==0) {
  TRACE(_T("(OverFlow) Invalid Input!"));
  return 0;  
 }
  return res;//返回一个无符号整型.
}