总结一下前两天刚尝试的socket编程-使用AsyncSocket

说来惭愧,搞了两年ios居然木有用过socket...初学ios的时候倒是了解过,但是两年不用,之前学的内容已经完全忘光光.于是又开始网上各种查.

用cf的socket貌似显得很拽的样子,但是实在不适合我这种领导紧逼着出项目的情况.搜了下发现目前最常用的socket库应该就是AsyncSocket了.嗯,看起来很简单,搞it~

这个库有基于runloop和GCD两种,据我一哥们说runloop版本是基于timer机制实现异步处理,会跟scroller的滚动动画冲突.我暂时还没有验证他的说法,不过保险起见还是用了GCD.因为服务端那里用的是tcp,所以最终我只导入了GCDAsyncSocket.h/.m两个文件.

socket的数据处理放在一个JLYSocketManager类进行.因为我们是程序在就一直保持长链,所以直接搞成单例.

以下正题:

首先要进行连接:

 1     if (!_asynSocket)

 2     {

 3         _asynSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];

 4     }

 5     

 6     NSError *err = nil;

 7     if(![_asynSocket connectToHost:kSocketHost onPort:kSocketPort error:&err])

 8     {

 9         //handle error

10         NSLog(@"Error: %@", err);

11         _retryTime++;

12         [self connentServer];

13     }

14     else

15     {

16         //do sth after connect

17     }

 

成功连接之后就可以推数据或者读服务器的数据了.

1 //get data from server

2 [_asynSocket readDataWithTimeout:-1 tag:0];

3 

4 //send data to server

5 [_asynSocket writeData:data withTimeout:-1 tag:0];

 

然后要设置一些相应的委托方法,我主要用了下面几个:

1 #pragma mark -

2 #pragma mark -----socket delegate-----

3 - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port;

4 

5 - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag;

6 

7 - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;

8 

9 - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err;

不用多说,看名字就能明白这几个方法是干嘛的.

值得注意的是,你需要在"适当的地方"设置数据读取的监听,其实就是调用上面说的readDataWithTimeout方法.比如在didConnectToHost里,接收建立信道后服务端传来的链接状态信息.或者接受了一次数据,然后在didReadData里面设置继续监听.总之就是你需要保持一致向服务端索取数据.

so easy吧~

tcp通信必然伴随着粘包或者拆包的情况.而每次通过didReadData这个委托方法收到的是数据都是以包为单位.拆包粘包说白了就是服务器一次给你发了很多条信息,但是这些信息不是一条信息一个包,而是一条信息分了几个包发来,或者几条信息的数据合到一个包发来了.

java貌似自己有框架,闭着眼就能完成拆包合包的工作.但是oc上貌似没找到.只好手动.那首先要跟服务器有个协议,说明一下以什么作为一条信息的节点.比如我们,每条信息的头四个字节是用来保存这条信息的应有长度的.接到信息首先要读取长度,然后再跟收到的数据包长度对比.如果收到的长度刚刚好,说明这是一条完整的信息,直接封起来就行.如果收到的长度比预计长度短,说明信息被拆包发送,那就要继续跟下一个包进行拼装,再检验长度.如果收到的长度比预计的长,说明发生了粘包,那就要根据预计长度对数据包的数据进行拆分,对拆出的两部分数据继续进行处理.

说起来很啰嗦,其实简单,大概就是这么个方法:

 1 /**

 2  *  处理_bufferData

 3  *  主要处理拆包粘包问题.获得每段数据的长度之后,和当前buffer比较,如果刚好说明接收完全;如果buffer过大说明服务端粘包,需要拆包;如果buffer小说明服务端拆包,需要合包

 4  *

 5  *  @param newData 获取的新数据,如果只是本地拆包则传nil

 6  */

 7 - (void)handleBufferData:(NSData *)newData

 8 {

 9     if (newData)

10     {

11         //这是一个缓冲区,因为是全局的,并且是单例中,所以在数据取走的时候一定要清空

12         [_bufferData appendData:newData];

13     }

14     

15     //首先取前四位

16     int i = 0;

17     [_bufferData getBytes:&i range:NSMakeRange(0, 4)];

18     //因为oc和java字节数组的高地位顺序是反的,所以要翻转一下顺序

19     Byte *fix = [self overturnIntByteArr:i];

20     memcpy(&i, fix, 4);

21     free(fix);

22 

23     //刚好

24     if (i == [_bufferData length]-4)

25     {

26         NSData *contentData = [_bufferData subdataWithRange:NSMakeRange(4, i)];

27         //完整接收了一条信息的处理

28         [self finishGetWholeData:contentData];

29         

30         _bufferData = [NSMutableData data];

31     }

32     //需要拆包

33     else if (i < [_bufferData length]-4)

34     {

35         NSData *contentData = [_bufferData subdataWithRange:NSMakeRange(4, i)];

36         //这是完成了第一个包的数据的接收

37         [self finishGetWholeData:contentData];

38         

39         NSData *buffer = [_bufferData subdataWithRange:NSMakeRange(i+4, _bufferData.length-i-4)];

40         _bufferData = [NSMutableData data];

41         [_bufferData appendData:buffer];

42         

43         //继续进行判断处理

44         [self handleBufferData:nil];

45     }

46 }

47     
View Code

大概就是酱紫.思路是这样,但是估摸着是会有一些问题的,以后使用中再慢慢完善吧~

 

你可能感兴趣的:(socket编程)