下载AsyncSockethttps://github.com/robbiehanson/CocoaAsyncSocket类库,将RunLoop文件夹下的AsyncSocket.h, AsyncSocket.m, AsyncUdpSocket.h, AsyncUdpSocket.m 文件拷贝到自己的project中
添加CFNetwork.framework, 在使用socket的文件头
#import
#import "AsyncUdpSocket.h"
#import
typedef void (^dataBlock)(NSDictionary *response, NSString *responseString);
@interface UDPSocketSingleton : NSObject<AsyncUdpSocketDelegate>
@property (nonatomic, strong) AsyncUdpSocket *socket; // socket
@property (nonatomic, copy) NSString *socketHost; // socket的Host
@property (nonatomic, assign) UInt16 socketPort; // socket的prot
@property (nonatomic, strong) NSTimer *connectTimer; // 计时器
@property (nonatomic, strong) NSDictionary *socketResult;
@property (nonatomic, strong) dataBlock receiveData;
/**
* block回调方法,为了回调的时候少写代码
*/
- (void)receiveData:(dataBlock)block;
+ (UDPSocketSingleton *)sharedInstance; // 单例
- (void)socketConnectHost; // socket连接
-(void)cutOffSocket; // 断开socket连接
@end
通信中,涉及到byte字节流的解析,这个就需要根据实际情况的字节流数组中各个字节的要求和代表的数据内容来进行解析了。这些需要跟后台服务器沟通,确定每个字节代表了什么
#import "UDPSocketSingleton.h"
static UDPSocketSingleton *sharedInstance = nil;
@implementation UDPSocketSingleton
+ (UDPSocketSingleton *)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
#pragma mark - 连接host
- (void)socketConnectHost {
/**
* 初始化
*/
self.socket = [[AsyncUdpSocket alloc] initIPv4];
_socket.delegate = self;
NSError *error = nil;
/**
* 所连接的服务器ip地址 socketHost 和 端口号:socketPort
*/
[self.socket connectToHost:self.socketHost onPort:self.socketPort error:&error];
/**
* 允许广播信息
*/
[self.socket enableBroadcast:YES error:&error];
//启动接收线程
[self.socket receiveWithTimeout:-1 tag:1];
[self sendMessage];
/**
* 建立定时器,每隔50s像服务器发送心跳包
*
* longConnectToSocket:心跳包调用方法,在longConnectToSocket方法中进行长连接,并向服务器发送的讯息
*
* TimeInterval:心跳包执行间隔时间
*
*/
self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:50 target:self selector:@selector(longConnectToSocket) userInfo:nil repeats:YES];// 在longConnectToSocket方法中进行长连接需要向服务器发送的讯息
/**
* 启动定时器
*/
[self.connectTimer fire];
}
#pragma mark - 心跳包调用方法
- (void)longConnectToSocket {
// NSLog(@"心跳包信息发送");
[self sendMessage];
}
#pragma mark - AsyncUDPSocketDelegate
#pragma mark 信息发送成功成功回调
- (void)onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag{
// NSLog(@"已发送消息tag = %ld",tag);
}
#pragma mark 接收信息回调
- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port
{
// NSLog(@"接收到消息");
if (data.length < 5) {
return NO;
}
/**
* 解析数据
*/
[self dataToString:data];
return YES;
}
#pragma mark - 接收到数据进行回调
- (void)receiveData:(dataBlock)block {
self.receiveData = block;
}
- (void)onUdpSocket:(AsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error
{
//无法发送时,返回的异常提示信息 do something
// NSLog(@"error1");
}
-(void)onUdpSocket:(AsyncUdpSocket *)sock didNotReceiveDataWithTag:(long)tag dueToError:(NSError *)error
{
//无法接收时,返回异常提示信息 do something
// NSLog(@"error2,%@",error);
if (error.code == 57) {
[self cutOffSocket];
[self socketConnectHost];
}
}
- (void)sendMessage {
[self.socket receiveWithTimeout:-1 tag:1];
/**
* 获取通过Keychain保存的唯一不可变的UUID,否则UUID在删除程序重新装入后会变化
*/
NSString *uuidStr = [[NSUserDefaults standardUserDefaults] objectForKey:@"clientId"];
NSMutableData *writer=[[NSMutableData alloc]init];
// NSLog(@"%@",uuidStr);
int version = 1;
int num = 1;
int cmd = 0;
int length= 0000;
NSData *data = [uuidStr dataUsingEncoding:NSUTF8StringEncoding];
Byte *byte = (Byte *)[data bytes];
Byte by[16];
CC_MD5(byte, (int)data.length, by);//md5加密
[writer appendBytes:&version length:1];
[writer appendBytes:&num length:1];
[writer appendBytes:&cmd length:1];
[writer appendBytes:by length:16];
[writer appendBytes:&length length:2];
[self.socket sendData:writer withTimeout:-1 tag:1];
}
#pragma mark - 数据解析
- (void)dataToString:(NSData *)data {
//已经处理完毕
int responseBytes = 32;
[data getBytes:&responseBytes range:NSMakeRange(2, 1)];
// NSLog(@"\n\n----------------------接收信息:%d------------------\n\n",responseBytes);
/**
* 根据服务器返回的字节流的第3字节判断服务器信息,并给服务器返回响应数据,告知服务器已接收到信息并停止发送重复数据,否则服务器将持续不简单的发送数据
*/
if (responseBytes == 16) {
/**
* 字节流数据解析,从第5个字节开始解析
*/
// int responseLength;
// [data getBytes:&responseLength range:NSMakeRange(4, 1)];
// NSData *strData = [data subdataWithRange:NSMakeRange(5, responseLength)];
// NSString *string = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
// NSLog(@"response16 == %d, %@",responseLength, string);
NSMutableData *writer=[[NSMutableData alloc]init];
NSString *uuidStr = [SSKeychain passwordForService:@"com.ilincar.ilincar" account:@"ilincar"];
int version = 1;
int num = 1;
int cmd = 16;
int length = 0000;
//uuid 进行MD5加密
NSData *UUIDData = [uuidStr dataUsingEncoding:NSUTF8StringEncoding];
Byte *byte = (Byte *)[UUIDData bytes];
Byte UUIDByte[16];
CC_MD5(byte, (int)UUIDData.length, UUIDByte);//md5 加密
[writer appendBytes:&version length:1];
[writer appendBytes:&num length:1];
[writer appendBytes:&cmd length:1];
[writer appendBytes:UUIDByte length:16];
[writer appendBytes:&length length:2];
[self.socket receiveWithTimeout:-1 tag:1];
[self.socket sendData:writer withTimeout:-1 tag:1];
} else if (responseBytes == 17) {
/**
* 字节流数据解析,从第5个字节开始解析
*/
// int responseLength;
// [data getBytes:&responseLength range:NSMakeRange(4, 1)];
// NSData *strData = [data subdataWithRange:NSMakeRange(5, responseLength)];
// NSString *string = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
// NSLog(@"response17 == %d, %@",responseLength, string);
NSMutableData *writer=[[NSMutableData alloc]init];
NSString *uuidStr = [SSKeychain passwordForService:@"com.ilincar.ilincar" account:@"ilincar"];
int version = 1;
int num = 1;
int cmd = 17;
int aaa = 0;
int bbb = 8;
//uuid 进行MD5加密
NSData *UUIDData = [uuidStr dataUsingEncoding:NSUTF8StringEncoding];
Byte *byte = (Byte *)[UUIDData bytes];
Byte UUIDByte[16];
CC_MD5(byte, (int)UUIDData.length, UUIDByte);//md5 加密
//服务器所需的指定尾部字节需求
Byte *lastByte = (Byte *)[[data subdataWithRange:NSMakeRange(5, 8)] bytes];
[writer appendBytes:&version length:1];
[writer appendBytes:&num length:1];
[writer appendBytes:&cmd length:1];
[writer appendBytes:UUIDByte length:16];
[writer appendBytes:&aaa length:1];
[writer appendBytes:&bbb length:1];
[writer appendBytes:lastByte length:8];
[self.socket receiveWithTimeout:-1 tag:1];
[self.socket sendData:writer withTimeout:-1 tag:1];
} else if (responseBytes == 32) {
/**
* 字节流数据解析,从第5个字节开始解析
*/
int responseLength;
[data getBytes:&responseLength range:NSMakeRange(4, 1)];//获取所取范围的字节流的长度
NSData *strData = [data subdataWithRange:NSMakeRange(5, responseLength)];
NSString *string = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:strData options:NSJSONReadingMutableContainers error:nil];
// NSLog(@"UDPrespond32 = str:%@",string);
NSDictionary *orderDic = [dic valueForKey:@"order"];
NSString *code = [orderDic valueForKey:@"code"];
if ([code isEqualToString:@"003"]) {
NSUserDefaults *userDfaults = [NSUserDefaults standardUserDefaults];
[userDfaults removeObjectForKey:@"account"];
[userDfaults removeObjectForKey:@"bindState"];
[UIAppDelegate goToLoginView];
}
#pragma mark - 将结果回调给主页面
if (self.receiveData != nil) {
self.receiveData(dic, string);
}
self.socketResult = [[NSDictionary alloc] initWithDictionary:dic];
NSMutableData *writer=[[NSMutableData alloc]init];
NSString *uuidStr = [SSKeychain passwordForService:@"com.ilincar.ilincar" account:@"ilincar"];
int version = 1;
int num = 1;
int cmd = 32;
int length = 0000;
//uuid 进行MD5加密
NSData *UUIDData = [uuidStr dataUsingEncoding:NSUTF8StringEncoding];
Byte *byte = (Byte *)[UUIDData bytes];
Byte UUIDByte[16];
CC_MD5(byte, (int)UUIDData.length, UUIDByte);//md5加密
[writer appendBytes:&version length:1];
[writer appendBytes:&num length:1];
[writer appendBytes:&cmd length:1];
[writer appendBytes:UUIDByte length:16];
[writer appendBytes:&length length:2];
[self.socket receiveWithTimeout:-1 tag:1];
[self.socket sendData:writer withTimeout:-1 tag:1];
}
}
-(void)cutOffSocket {
[self.socket close];
}