重大更新前的严肃解释
简书里的好友,私信我说好久没有看我更新了,这段时间不是忙,只是感觉自己工作上的很多东西,似乎没有什么精华拿来分享(这似乎是一个看起来比较正常的借口),严肃脸.jpg。
懒癌该治
公司做的智能家居,一开始的时候是用wifi将设备入网,然后用wifi或者4G来控制硬件设备,但是用wifi的话对网络的要求又是极高的。由于受到三大网络运营商的限制,wifi在一定程度上是没有那么好的网速的。于是物联网在前几年就开始寻找更好的协议去控制硬件,一开始的socket还是很流行的,至今为止还是用的不错。现在用的比较好的就是mqtt协议,由于这方面的开源还比较少,小公司用这个协议的还是少数。说了这么多,还是回归主题,直接在局域网内如何用UDP发送数据包给服务端,从而高效快速的控制设备。
某百科上的UDP是这样解释的:
其实UDP,在第三方的基础上写是十分简单的,因为底层的都已经写好了,我们只需要专注于自己的业务逻辑上即可。github上下载量多的主要是CocoaAsyncSocket,他有基于Runloop的还有一个是基于GCD的,大家看自己需求来。我这里是基于GCD的里面,大家根据github上的操作,直接将三方拉入自己的项目或者用pods导入均可。我在工程里用的就是
GCDAsyncUdpSocket。
下面我直接贴上代码:
#import <Foundation/Foundation.h>
#import "GCDAsyncUdpSocket.h"
@protocol UDPClientSocketDelegate <NSObject>
- (void)clientSocketDidReceiveMessage:(NSString *)message andPort:(uint16_t)port withHost:(NSString *)hostIP;
@end
@interface UDPClientSocket : NSObject <GCDAsyncUdpSocketDelegate>
@property (nonatomic,assign)NSInteger times;
@property (nonatomic, weak)id <UDPClientSocketDelegate>delegate;
@property (nonatomic, strong)GCDAsyncUdpSocket * udpSocket;
@property (nonatomic, copy) NSString *mHost;
@property (nonatomic, assign) int mPort;
+(UDPClientSocket *)shareInstance;
- (void)getAddressAndPort:(NSString *)hostID;
- (void)sendMessageToHost:(NSString *)host WithPort:(uint16_t)port transData:(NSDictionary *)dataPackage;
//- (void)sendMessage:(NSString *)message;
- (void)disconnect;
@end
因为在主机通信里,只要有控制设备或者查询设备的状态就会有UDP的发送,所以这在这个.h文件里写了一个代理,可以直接把服务端返回的包直接在自己需要的地方获取。
下面看.m文件:
#import "UDPClientSocket.h"
@implementation UDPClientSocket
+(UDPClientSocket *)shareInstance{
static UDPClientSocket * clientUdp = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
clientUdp = [[self alloc]init];
});
return clientUdp;
}
// 探测是否连通
- (void)getAddressAndPort:(NSString *)hostID{
NSString *url = [NSString stringWithFormat:@"%@/host/GetHostLocation",getIP()];
NSDictionary *params = @{kSessionID:getMySessionID(),@"HostID":hostID};
[SZRequest PostURLForNetRequest:url AndParams:params block:^(id result) {
NSLog(@"主机IP:%@",result);
if (Kerrcode==0) {
_mHost = result[@"HostIP"];
_mPort = [result[@"HostPort"] intValue];
// 探测数据包
NSDictionary *params = @{@"devID":@"",@"requestCode":@"8900",@"userName":[mUserDefaults objectForKey:UserName]};
[self sendMessageToHost:_mHost WithPort:(uint16_t)_mPort transData:params];
[mUserDefaults setObject:_mHost forKey:KhostIP];
[mUserDefaults setInteger:_mPort forKey:KhostPort];
[mUserDefaults synchronize];
}
} andBlock:^(NSError *error) {
NSLog(@"error = %@",error);
}];
}
- (void)sendMessageToHost:(NSString *)host WithPort:(uint16_t)port transData:(NSDictionary *)dataPackage{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.udpSocket = [[GCDAsyncUdpSocket alloc]initWithDelegate:self delegateQueue:queue];
NSLog(@"data=%@ %@ %d",dataPackage,host,port);
NSData *data = [[CJSONDataSerializer serializer] serializeDictionary:dataPackage];
[self.udpSocket sendData:data toHost:host port:port withTimeout:-1 tag:0];
[self.udpSocket receiveOnce:nil];
}
//- (void)sendMessage:(NSString *)message{
// NSData * data = [message dataUsingEncoding:NSUTF8StringEncoding];
// [self.udpSocket sendData:data toHost:@"192.168.7.113" port:8011 withTimeout:-1 tag:0];
//}
- (void)disconnect{
[self.udpSocket closeAfterSending];
}
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext{
NSString * ip = [GCDAsyncUdpSocket hostFromAddress:address];
uint16_t port = [GCDAsyncUdpSocket portFromAddress:address];
NSString * message = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"接收到服务端的消息:ip:%@ port:%d message:%@",ip,port,message);
if ([self.delegate respondsToSelector:@selector(clientSocketDidReceiveMessage:andPort:withHost:)]) {
[self.delegate clientSocketDidReceiveMessage:message andPort:port withHost:ip];
}
[self.udpSocket receiveOnce:nil];
// [self.udpSocket beginReceiving:nil];
}
先来说*+(UDPClientSocket )shareInstance这个,这个大家都知道是单例,因为在项目里多处使用,为了避免在别的文件写更多的冗余代码,所以用单例是很方便的。
// 探测是否连通
**- (void)getAddressAndPort:(NSString *)hostID ** 这个之所以写到这里,是因为自己项目的需求,每次去发控制请求时,应该先去探测自己是不是和主机咋同一个局域网内,如果收到回包,就说明是在同一个局域网,这样就可以本地控制主机了。
**- (void)sendMessageToHost:(NSString *)host WithPort:(uint16_t)port transData:(NSDictionary *)dataPackage **这个就是发送数据包自己写的方法了,调用这个方法时,需要传进来服务器端的ip和服务端的port,还有就是需要传给服务端的数据包。我这里之所以传字典进来,是因为我这边传的数据就是json字符串,然后将字典转成NSData格式,服务端就能收到我这边发送的数据了。 *NSData data = [[CJSONDataSerializer serializer] serializeDictionary:dataPackage];这个是NSDictionary转NSData,这个方法也是用的第三方库,也很好找。
这个代理方法是写在收到服务端回包之后,就可以把收到的数据传到自己调用这个代理方法的类里了。
UDPClientSocket使用
在自己要用到udp的类里,先import "UDPClientSocket.h"文件,然后遵循UDPClientSocketDelegate,将代理方法写进去,大家就可以很愉快的使用啦。
后续会更新一篇服务端,结合自己的客户端,可以用电脑端做服务端,手机做客户端,相互通信。当然,我会继续把自己的业务逻辑这一块写完之后,再来写总结。
最后,希望看到这篇文章的小伙伴给我推荐一些自己觉得不错的书籍或者大神的博客或者有趣好玩的一切。
来吧,一起玩代码吧!!!
附上demo:
链接: https://pan.baidu.com/s/1cvQPw6 密码: jtu7
要是有不明白的敬请留言呀~~~