GCDAsyncSocket
GCDAsyncSocket 的读写都是异步的不会阻塞线程。
socket读写都是数据流,这个流一直存在,心跳不是必须的。如果不断开这个流通道就一直存在,读取和写入都通过这个流通道。
tag不参与流数据的传递,只是为了本地管理方便,是GCDAsyncSocket内部实现数据调度的一个东西,方便用户给数据打标记,方便读取。
发送信息在底层都是会被拆分成很细小的数据包发出去的,因此接受到的数据可以不是一次性接受完的。
每次可能收到一部分数据。读取时我们可能指定条件触发读取数据的方法。比如读取多长的数据就触发,又比如碰到回车断行数据就触发。
也可以收到数据就触发,然后处理这部分数据。
连接socket不必多说,主动调用才可以。链接socket会触发代理方法。
发送消息,会触发写消息的代理方法。每发送一个消息,就应该有一个读取消息。
读消息有很多种,指定读取的条件会根据条件触发读消息的代理。
可以这么理解。主动调用读消息,它就一直在那里可能是在后台一直读,当读到指定的条件时,触发代理方法。
如果处理完一次消息还要继续读取下次进来的数据,就要继续主动调用读数据的方法,会根据读数据的条件触发代理方法。
用到的代理方法大致如下:
//socket成功连接到才会服务器调用
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port;
//接受到新的socket连接才会调用
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket;
//读取数据,有数据就会调用
- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag;
//直到读到这个长度的数据,才会触发代理
- (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
//直到读到data这个边界,才会触发代理
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
//有socket断开连接调用
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err;
发消息时,通常我们有确保我们没有和服务器断开,如果断开了根据需要重连。
客户端要维持自己的心跳,来让服务器知道我们还连着。所谓心跳包就是开一个计时器,定时发送指定的信息。
服务器判断 时间间隔,如果超过一定的时间,服务器没收到来自客户端的信息,服务端就会端口链接。
我们在socket链接之后,就可以发送心跳包了。
[sock readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:0];
是读取到边界的意思。当读取到边界时会触发代理方法。
封包处理:
数据包的封装,是需要一定的格式的,这个要和服务端协商一致。封装数据包时,要根据格式封装。
比如这个,先封装一个数据头,指定实际发送的数据内容的大小,自身的一些标志信息。以及一些分界标志等。后面是具体的数据。
像这里的数据报头,先转成json 再转成data 而不是直接转data发出去的。因为服务端要对报头进行json解析。
当然也可能是其他数据格式,比如protobuf消息数据,对象数据等。
/*封装报文**/
- (void)sendData:(NSData *)data :(NSString *)type toClinet:(NSString *)target;
{
NSUInteger size = data.length;
NSMutableDictionary *headDic = [NSMutableDictionary dictionary];
[headDic setObject:type forKey:@"type"];
[headDic setObject:@"CinentA" forKey:@"CinentID"];
[headDic setObject:target forKey:@"targetID"];
[headDic setObject:[NSString stringWithFormat:@"%ld",size] forKey:@"size"];
NSString *jsonStr = [self dictionaryToJson:headDic];
NSData *lengthData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *mData = [NSMutableData dataWithData:lengthData];
//分界
[mData appendData:[GCDAsyncSocket CRLFData]];
[mData appendData:data];
//第二个参数,请求超时时间
[self.clinetSocket writeData:mData withTimeout:-1 tag:0];
}
//字典转为Json字符串
- (NSString *)dictionaryToJson:(NSDictionary *)dic
{
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&error];
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
客户端要做的主要是:
- 链接服务器,并维持心跳 维持心跳不是必须的
- 封包,给服务器发送消息
- 接受处理服务器的信息
服务端要做的主要是:
- 数据包的拆解,
- 数据的分发
- 用户的调用
- 每个用户的心跳刷新 维持心跳不是必须的
网友评论