iOS udp socket通信-远程唤醒windows电脑主机
一、原理说明
1、远程开机Wake onLAN(WOL)简介:
俗称远程唤醒,是现在很多网卡都支持的功能。而远程唤醒的实现,主要是向目标主机发送特殊格式的数据包,是AMD公司制作的MagicPacket这套软件以生成网络唤醒所需要的特殊数据包,俗称魔术包(Magic Packet)。MagicPacket格式虽然只是AMD公司开发推广的技术,并非世界公认的标准,但是仍然受到很多网卡制造商的支持,因此许多具有网络唤醒功能的网卡都能与之兼容。原理上我们不用深入,实现上是发一个BroadCast包,包的内容包括以下数据就可以了。FF FF FF FF FF FF,6个FF是数据的开始,紧跟着16次MAC地址就可以了。我们采用<a href="http://baike.baidu.com/link?url=S-2pRxOGTT6z4-Ibl4rY2y4uxXlVw6Y9ilkgSjQTkZYsbXAw4dk5ARXQFAVlFI__dt-NI6mcdcpyUsQ1pMs1mq">UDP协议</a>发送广播包的形式来实现对电脑主机的唤醒
2、设置pc-B 首先需要进行BIOS和网卡设置,启动计算机,进入BIOS参数设置。选择电源管理设置“Power Management Setup”选项,将“Wake up on LAN”项和“Wake on PCI Card”项均设置为“Enable”,启用该计算机的远程唤醒功能。 (各个主板不一样,可以在百度上搜索怎么开启你家电脑的主板的远程唤醒功能)
3、获取pc-B的<a href="http://baike.baidu.com/link?url=vSrHHlYjyTWDmS_RWuUIz0fNNt2ro8SbM_OCVkj0mGy-3-JIqDnYdCzeMNfvigUuBAcnion-VpVGB_1ZXbxUQa">Mac地址</a> 通过命令行输入ipconfig/all可以得到pc-B的地址 00 0B 2F 70 40 9E
二、代码实现
我们需要用到一个socket的三方AsyncSocket,这个三方比较老了,还是非ARC的代码,因为感觉用起来还可以,所以就一直在使用。
https://github.com/roustem/AsyncSocket
下载此三方,导入到项目中
备注:由于是非ARC模式的三方,我的项目是ARC模式,所以需要手动改成支持ARC模式
1、新建一个类 继承NSObject即可 我们命名为"SocketConnect"
导入AsyncSocket 的
#import <Foundation/Foundation.h>
#import "AsyncUdpSocket.h"
定义SocketConnect.h的属性和方法
//ip地址
@property (strong,nonatomic)NSString *hostIpAddress;
//端口号
@property (assign,nonatomic)NSInteger hostPort;
//udp Socket
@property (strong,nonatomic)AsyncUdpSocket *udpSocket;
/**
* 唤醒状态的回调
*/
@property (strong,nonatomic)void(^wakeState)(BOOL result);
//socket单例
+ (instancetype)sharedInstance;
/**
* 远程唤醒电脑
*
* @param sendData data数据
* @param result 返回唤醒的状态
*/
- (void)wakeUp:(NSData *)sendData result:(void(^)(BOOL result))result;
在SocketConnect.m实现方法
#pragma mark - 懒加载
- (AsyncUdpSocket *)udpSocket{
if (!_udpSocket) {
_udpSocket = [[AsyncUdpSocket alloc]initWithDelegate:self];
NSError *error = nil;
//开启广播模式
[_udpSocket enableBroadcast:YES error:&error];
NSLog(@"%@",error);
}
return _udpSocket;
}
//懒加载结束
#pragma mark - 初始化单例对象
+ (instancetype)sharedInstance{
static SocketConnect *socketConnect = nil;
static dispatch_once_t oneToken;
dispatch_once(&oneToken, ^{
socketConnect = [[SocketConnect alloc]init];
});
return socketConnect;
}
- (void)wakeUp:(NSData *)sendData result:(void (^)(BOOL))result{
self.wakeState = result;
//由于是局域网,我直接发送洪的广播包,如果做远程唤醒,需要替换这里的ip地址
[self.udpSocket sendData:sendData toHost:@"255.255.255.255" port:self.hostPort withTimeout:-1 tag:0];
}
#pragma mark - socket协议方法
//已接收到消息
- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port{
return YES;
}
//没有接受到消息
-(void)onUdpSocket:(AsyncUdpSocket *)sock didNotReceiveDataWithTag:(long)tag dueToError:(NSError *)error{
}
//没有发送出消息
-(void)onUdpSocket:(AsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error{
self.wakeState(NO);
}
//已发送出消息
-(void)onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag{
self.wakeState(YES);
}
//断开连接
-(void)onUdpSocketDidClose:(AsyncUdpSocket *)sock{
}
2、使用方法
我们在demo 中默认的ViewController.m文件中viewDidLoad,添加一个UITextField和button来模仿下udp通信,我习惯用<a href ="https://github.com/SnapKit/Masonry">Masonry</a>来实现页面的布局,如果你对此不是很了解,可以使用拖控件来实现,界面很简单,笔者就不细说了
@interface ViewController (){
UITextField *inputTextField;
UIButton *sendBtn;
}
- (void)viewDidLoad {
[super viewDidLoad];
inputTextField = [[UITextField alloc]init];
inputTextField.placeholder = @"发一个";
inputTextField.borderStyle = UITextBorderStyleRoundedRect;
[self.view addSubview:inputTextField];
sendBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[sendBtn setTitle:@"发送" forState:UIControlStateNormal];
[sendBtn addTarget:self action:@selector(sendMessage) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:sendBtn];
[inputTextField mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.view);
make.height.equalTo(@30);
make.left.equalTo(@20);
make.right.equalTo(self.view.mas_right).with.offset(-20);
}];
[sendBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(inputTextField.mas_bottom);
make.centerX.equalTo(inputTextField.mas_centerX);
make.width.height.equalTo(@50);
}];
}
- (void)sendMessage{
NSData *data = [inputTextField.text dataUsingEncoding:NSUTF8StringEncoding];
SocketConnect *socket = [SocketConnect sharedInstance];
//设置端口号为8888
socket.hostPort = 8888;
if (inputTextField.text.length != 0 ) {
[socket wakeUp:data result:^(BOOL result) {
//消息是否发送成功的回调
NSLog(@"%d",result);
}];
}
}
UDP通信的基本代码写完了,在运行之前,我们下找一个小工具来测试下吧,笔者用到的工具SocketTool 是一个TCP/IP测试工具,我已经上传到百度云,读者可放心下载
http://pan.baidu.com/s/1pKpSyj5
显示可以运行我们的项目了,来看下运行的效果
运行效果.gif
如果你运行出来也是这样子的效果,说明你的UDP通信就没问题了,下面我们来实现电脑主机的网络唤醒,我们只需要在sendMessage里面的方法做下修改即可,大概思路就是拼接魔术包
- (void)sendMessage{
//被控制的电脑主机的mac地址
Byte mac[6] = {0x60,0xf8,0x1d,0xad,0x3a,0xf0};
Byte packet[17 * 6] = {};
for (int i = 0 ; i < 6; i++)
packet[i] = 0xFF;
for (int i = 1; i <= 16; i++) {
for (int j = 0; j < 6; j++) {
packet[i * 6 + j] = mac[j];
}
}
SocketConnect *socket = [SocketConnect sharedInstance];
socket.hostPort = 8888;
NSData *data = [NSData dataWithBytes:packet length:sizeof(packet)];
[socket wakeUp:data result:^(BOOL result) {
NSLog(@"%d",result);
}];
}
OK,全部搞定,运行项目就可以唤醒被控制的电脑的开机了,如果需要做远程唤醒,则需要在路由器上做下端口映射,并把以下位置修改为你的ip地址或者域名地址即可
[self.udpSocket sendData:sendData toHost:@"IP地址" port:self.hostPort withTimeout:-1 tag:0];
了解更多请访问iOS udp socket通信-远程唤醒windows电脑主机!