iOS在做即时通讯类应用或游戏一般都是用sokect通讯,CFSocket是苹果提供给我们的使用Socket的方式,用起来比较麻烦,里面很多东西要自己处理。平时开发项目中首选 AsyncSocket 开源库,它帮助我们封装了很多东西。我们只需要 建立连接 设置代理、发送数据、接受数据,使用起来很方便。如果是tcp连接,那就不可避免的会遇到粘包与半包的问题。来看看如何处理:
在设计上,每一个包带上一个length的字段,表示包的长度。这样服务器、客户端才好处理粘包问题。客户端接受到数据先把这些数据放到缓冲区,根据length字段来取正确的长度,如果数据不够这个长度就等下一个数据的到来,如果还不够 继续...要是数据多于这个长度 我们只取length的长度,剩下的放缓冲区 这个肯定是下一个包的数据,同理处理。看代码:
1.我这里每个包前面都带了一个int型length,int型migid,
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSLog(@"接受到消息");
[self.socket readDataWithTimeout:-1 tag:0];
//将接受到的数据data 保留到缓冲区_bufData
[_bufData appendData:data];
//拿到包长,缓冲区的前4个字节为包长
int len ;
[_bufData getBytes:&len range:NSMakeRange(0, 4)];
//够一个包长的数据的话 就从缓冲区取出来,这样就能拿到一个正确完整的包,不够一个包长就等下一 次接收数据放于缓冲区
while (len<= _bufData.length) {
//得到每个包的数据
NSData *tmp = [_bufData subdataWithRange:NSMakeRange(0, len)];
// NSData * sercetData = [self getSecret:[tmp mutableCopy] key:self.secretKey];
int msgid;
[tmp getBytes:&msgid range:NSMakeRange(4, 4)];
NSLog(@"msgid:%d",msgid);
if (msgid <= 0) break;
//转化 数据
[[PGSocketTransmit share] transmitMessageWith:tmp];
//取完这段数据,把缓冲区后面的数据移到最前面,保证每次取前4个字节 都是一个int类型的包长
_bufData = [_bufData subdataWithRange:NSMakeRange(len, _bufData.length -len)].mutableCopy;
//如果刚好取完,跳出循环
if (_bufData.length == 0) {
break;
}
else{//得到下一个包的长度,继续判断
[_bufData getBytes:&len range:NSMakeRange(0, 4)];
}
}
}
2.数据传输一般是要求加密的,这里给出简单的 异或 加密,每一次连接服务器返回一个随机的int类型的key,用这个key跟发送的数据异或,当然服务器返回你的数据也是需要 用这个key异或处理的 才会得到正确的数据。把我们习惯的NSData转成byte,用指针 操作地址来处理,如下,向服务器发送数据NSdata 从服务器拿到数据NSdata 调这个方法 返回 异或 后的数据。
- (NSData *)getSecret:(NSMutableData*)data key:(int32_t)key{
void* bytes = data.mutableBytes;
int length = (int)data.length;
for (int i = 0; i < length; i += 4) {
int32_t value = *(int32_t*)(bytes + i);
value ^= key;
memcpy(bytes + i, &value, MIN(i + 4, length) - i);
}
return [[NSData alloc]initWithBytes:bytes length:length];
}