美文网首页socket通信good
CocoaAsyncSocket使用

CocoaAsyncSocket使用

作者: F麦子 | 来源:发表于2017-06-27 11:49 被阅读389次

    转载:http://www.cocoachina.com/ios/20170615/19529.html

    参考:http://www.jb51.net/article/83941.htm

    参考:http://www.jianshu.com/p/321bc95d077f

    http://www.cocoachina.com/ios/20170206/18619.html

    本文介绍了CocoaAsyncSocket库中GCDAsyncSocket类的使用、粘包处理以及时间延迟测试.

    一.CocoaAsyncSocket介绍

    CocoaAsyncSocket中主要包含两个类:

    1.GCDAsyncSocket.

    1

    2用GCD搭建的基于TCP/IP协议的socket网络库

    GCDAsyncSocket is a TCP/IP socket networking library built atop Grand Central Dispatch. -- 引自CocoaAsyncSocket.

    2.GCDAsyncUdpSocket.

    1

    2用GCD搭建的基于UDP/IP协议的socket网络库.

    GCDAsyncUdpSocket is a UDP/IP socket networking library built atop Grand Central Dispatch..-- 引自CocoaAsyncSocket.

    二.下载CocoaAsyncSocket

    首先,需要到这里下载CocoaAsyncSocket.

    下载后可以看到文件所在位置.

    文件路径

    这里只要拷贝以下两个文件到项目中.

    TCP开发使用的文件

    三.客户端

    因为,大部分项目已经有服务端socket,所以,先讲解客户端创建过程.

    步骤:

    1.继承GCDAsyncSocketDelegate协议.

    2.声明属性

    1

    2// 客户端socket

    @property (strong, nonatomic) GCDAsyncSocket *clientSocket;

    3.创建socket并指定代理对象为self,代理队列必须为主队列.

    1

    self.clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];

    4.连接指定主机的对应端口.

    1

    2NSError *error = nil;

    self.connected = [self.clientSocket connectToHost:self.addressTF.text onPort:[self.portTF.text integerValue] viaInterface:nil withTimeout:-1 error:&error];

    5.成功连接主机对应端口号.

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

    {

    //    NSLog(@"连接主机对应端口%@", sock);

    [self showMessageWithStr:@"链接成功"];

    [self showMessageWithStr:[NSString stringWithFormat:@"服务器IP: %@-------端口: %d", host,port]];

    // 连接成功开启定时器

    [self addTimer];

    // 连接后,可读取服务端的数据

    [self.clientSocket readDataWithTimeout:- 1 tag:0];

    self.connected = YES;

    }

    注意:

    The host parameter will be an IP address, not a DNS name. -- 引自GCDAsyncSocket

    连接的主机为IP地址,并非DNS名称.

    6.发送数据给服务端

    // 发送数据

    - (IBAction)sendMessageAction:(id)sender

    {

    NSData *data = [self.messageTextF.text dataUsingEncoding:NSUTF8StringEncoding];

    // withTimeout -1 : 无穷大,一直等

    // tag : 消息标记

    [self.clientSocket writeData:data withTimeout:- 1 tag:0];

    }

    注意:

    发送数据主要通过- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag写入数据的.

    .读取服务端数据

    /**

    读取数据

    @param sock 客户端socket

    @param data 读取到的数据

    @param tag 本次读取的标记

    */

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

    {

    NSString *text = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];

    [self showMessageWithStr:text];

    // 读取到服务端数据值后,能再次读取

    [self.clientSocket readDataWithTimeout:- 1 tag:0];

    }

    注意:

    有的人写好代码,而且第一次能够读取到数据,之后,再也接收不到数据.那是因为,在读取到数据的代理方法中,需要再次调用[self.clientSocket readDataWithTimeout:- 1 tag:0];方法,框架本身就是这么设计的.

    .客户端socket断开连接.

    /**

    客户端socket断开

    @param sock 客户端socket

    @param err 错误描述

    */

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

    {

    [self showMessageWithStr:@"断开连接"];

    self.clientSocket.delegate = nil;

    self.clientSocket = nil;

    self.connected = NO;

    [self.connectTimer invalidate];

    }

    注意:

    sokect断开连接时,需要清空代理和客户端本身的socket.

    1

    2self.clientSocket.delegate = nil;

    self.clientSocket = nil;

    .建立心跳连接.

    // 计时器

    @property (nonatomic, strong) NSTimer *connectTimer;

    // 添加定时器

    - (void)addTimer

    {

    // 长连接定时器

    self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(longConnectToSocket) userInfo:nil repeats:YES];

    // 把定时器添加到当前运行循环,并且调为通用模式

    [[NSRunLoop currentRunLoop] addTimer:self.connectTimer forMode:NSRunLoopCommonModes];

    }

    // 心跳连接

    - (void)longConnectToSocket

    {

    // 发送固定格式的数据,指令@"longConnect"

    float version = [[UIDevice currentDevice] systemVersion].floatValue;

    NSString *longConnect = [NSString stringWithFormat:@"123%f",version];

    NSData  *data = [longConnect dataUsingEncoding:NSUTF8StringEncoding];

    [self.clientSocket writeData:data withTimeout:- 1 tag:0];

    }

    注意:

    心跳连接中发送给服务端的数据只是作为测试代码,根据你们公司需求,或者和后台商定好心跳包的数据以及发送心跳的时间间隔.因为这个项目的服务端socket也是我写的,所以,我自定义心跳包协议.客户端发送心跳包,服务端也需要有对应的心跳检测,以此检测客户端是否在线.

    四.服务端

    步骤:

    1.继承GCDAsyncSocketDelegate协议.

    2.声明属性

    1

    2// 服务端socket(开放端口,监听客户端socket的连接)

    @property (strong, nonatomic) GCDAsyncSocket *serverSocket;

    3.创建socket并指定代理对象为self,代理队列必须为主队列.

    1

    2// 初始化服务端socket

    self.serverSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];

    4.开放服务端的指定端口.

    1

    BOOL result = [self.serverSocket acceptOnPort:[self.portF.text integerValue] error:&error];

    .连接上新的客户端socket

    // 连接上新的客户端socket

    - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(nonnull GCDAsyncSocket *)newSocket

    {

    // 保存客户端的socket

    [self.clientSockets addObject: newSocket];

    // 添加定时器

    [self addTimer];

    [self showMessageWithStr:@"链接成功"];

    [self showMessageWithStr:[NSString stringWithFormat:@"客户端的地址: %@ -------端口: %d", newSocket.connectedHost, newSocket.connectedPort]];

    [newSocket readDataWithTimeout:- 1 tag:0];

    }

    .发送数据给客户端

    // socket是保存的客户端socket, 表示给这个客户端socket发送消息

    - (IBAction)sendMessage:(id)sender

    {

    if(self.clientSockets == nil)return;

    NSData *data = [self.messageTextF.text dataUsingEncoding:NSUTF8StringEncoding];

    // withTimeout -1 : 无穷大,一直等

    // tag : 消息标记

    [self.clientSockets enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

    [obj writeData:data withTimeout:-1 tag:0];

    }];

    }

    .读取客户端的数据

    /**

    读取客户端发送的数据

    @param sock 客户端的Socket

    @param data 客户端发送的数据

    @param tag 当前读取的标记

    */

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

    {

    NSString *text = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];

    [self showMessageWithStr:text];

    // 第一次读取到的数据直接添加

    if(self.clientPhoneTimeDicts.count == 0)

    {

    [self.clientPhoneTimeDicts setObject:[self getCurrentTime] forKey:text];

    }

    else

    {

    // 键相同,直接覆盖,值改变

    [self.clientPhoneTimeDicts enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {

    [self.clientPhoneTimeDicts setObject:[self getCurrentTime] forKey:text];

    }];

    }

    [sock readDataWithTimeout:- 1 tag:0];

    }

    .建立检测心跳连接.

    // 检测心跳计时器

    @property (nonatomic, strong) NSTimer *checkTimer;

    // 添加计时器

    - (void)addTimer

    {

    // 长连接定时器

    self.checkTimer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:@selector(checkLongConnect) userInfo:nil repeats:YES];

    // 把定时器添加到当前运行循环,并且调为通用模式

    [[NSRunLoop currentRunLoop] addTimer:self.checkTimer forMode:NSRunLoopCommonModes];

    }

    // 检测心跳

    - (void)checkLongConnect

    {

    [self.clientPhoneTimeDicts enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {

    // 获取当前时间

    NSString *currentTimeStr = [self getCurrentTime];

    // 延迟超过10秒判断断开

    if(([currentTimeStr doubleValue] - [obj doubleValue]) > 10.0)

    {

    [self showMessageWithStr:[NSString stringWithFormat:@"%@已经断开,连接时差%f",key,[currentTimeStr doubleValue] - [obj doubleValue]]];

    [self showMessageWithStr:[NSString stringWithFormat:@"移除%@",key]];

    [self.clientPhoneTimeDicts removeObjectForKey:key];

    }

    else

    {

    [self showMessageWithStr:[NSString stringWithFormat:@"%@处于连接状态,连接时差%f",key,[currentTimeStr doubleValue] - [obj doubleValue]]];

    }

    }];

    }

    心跳检测方法只提供部分思路:

    1.懒加载一个可变字典,字典的键作为客户端的标识.如:客户端标识为13123456789.

    2.在- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag方法中,将读取到的数据或者数据中的部分字符串作为键.字典的值为系统当前时间.服务端第一次读取数据时,字典中没有数据,所以,直接添加到可变字典中,之后每次读取数据时,都用字典的setObject: forKey:方法添加字典,若存储的键相同,即客户端标识相同,键会被覆盖,再使用系统的当前时间作为值.

    3.在- (void)checkLongConnect中,获取此时的当前时间,遍历字典,将每个键的值和当前时间进行比较即可.判断的延迟时间可以写8秒.时间自定.之后,再根据自己的需求进行后续处理.

    五.数据粘包处理.

    1.粘包情况.

    例如:包数据为:abcd.

    2.粘包解决思路.

    思路1:

    发送方将数据包加上包头和包尾,包头、包体以及包尾用字典形式包装成json字符串,接收方,通过解析获取json字符串中的包体,便可进行进一步处理.

    例如:

    {

    // head:包头,body:包体,end:包尾

    NSDictionary *dict = @{

    @"head": @"phoneNum",

    @"body": @(13133334444),

    @"end": @(11)};

    }

    思路2:

    添加前缀.和包内容拼接成同一个字符串.

    例如:当发送数据是13133334444,如果出现粘包情况只属于完整型:

    13133334444

    1313333444413133334444

    131333344441313333444413133334444...

    可以将ab作为前缀.则接收到的数据出现的粘包情况:

    ab13133334444

    ab13133334444ab13133334444

    ab13133334444ab13133334444ab13133334444...

    使用componentsSeparatedByString:方法,以ab为分隔符,将每个包内容存入数组中,再取对应数组中的数据操作即可.

    思路3:

    如果最终要得到的数据的长度是个固定长度,用一个字符串作为缓冲池,每次收到数据,都用字符串拼接对应数据,每当字符串的长度和固定长度相同时,便得到一个完整数据,处理完这个数据并清空字符串,再进行下一轮的字符拼接.

    例如:处理上面的不完整型.创建一个长度是4的tempData字符串作为数据缓冲池.第1次收到数据,数据是:ab,tempData拼接上ab,tempData中只能再存储2个字符,第2次收到数据,将数据长度和2进行比较,第2次的数据是:cda,截取前两位字符,即cd,tempData继续拼接cd,此时,tempData为abcd,就是我们想要的数据,我们可以处理这个数据,处理之后并清空tempData,将第2次收到数据的剩余数据,即cda中的a,再与tempData拼接.之后,再进行类似操作.

    核心代码

    /**

    处理数据粘包

    @param readData 读取到的数据

    */

    - (void)dealStickPackageWithData:(NSString *)readData

    {

    // 缓冲池还需要存储的数据个数

    NSInteger tempCount;

    if(readData.length > 0)

    {

    // 还差tempLength个数填满缓冲池

    tempCount = 4 - self.tempData.length;

    if(readData.length <= tempCount)

    {

    self.tempData = [self.tempData stringByAppendingString:readData];

    if(self.tempData.length == 4)

    {

    [self.mutArr addObject:self.tempData];

    self.tempData = @"";

    }

    }

    else

    {

    // 下一次的数据个数比要填满缓冲池的数据个数多,一定能拼接成完整数据,剩余的继续

    self.tempData = [self.tempData stringByAppendingString:[readData substringToIndex:tempCount]];

    [self.mutArr addObject:self.tempData];

    self.tempData = @"";

    // 多余的再执行一次方法

    [self dealStickPackageWithData:[readData substringFromIndex:tempCount]];

    }

    }

    }

    调用

    // 存储处理后的每次返回数据

    @property (nonatomic, strong) NSMutableArray *mutArr;

    // 数据缓冲池

    @property (nonatomic, copy) NSString *tempData;

    /** 第四次测试 -- 混合型**/

    self.mutArr = nil;

    /*

    第1次 : abc

    第2次 : da

    第3次 : bcdabcd

    第4次 : abcdabcd

    第5次 : abcdabcdab

    */

    // 数组中的数据代表每次接收的数据

    NSArray *testArr4 = [NSArray arrayWithObjects:@"abc",@"da",@"bcdabcd",@"abcdabcd",@"abcdabcdab", nil];

    self.tempData = @"";

    for(NSInteger i = 0; i < testArr4.count; i++)

    {

    [self dealStickPackageWithData:testArr4[i]];

    }

    NSLog(@"testArr4 = %@",self.mutArr);

    结果:

    92017-06-09 00:49:12.932976+0800 StickPackageDealDemo[10063:3430118] testArr4 = (

    abcd,

    abcd,

    abcd,

    abcd,

    abcd,

    abcd,

    abcd

    )

    数据粘包处理Demo在文末.

    六.测试.

    1.测试配置.

    测试时,两端需要处于同一WiFi下.客户端中的IP地址为服务端的IP地址,具体信息进入Wifi设置中查看.

    IP和端口描述

    2.测试所需环境.

    将客户端程序安装在每个客户端,让一台服务端测试机和一台客户端测试机连接mac并运行,这两台测试机可以看到打印结果,所有由服务端发送到客户端的数据,通过客户端再回传给服务端,在服务端看打印结果.

    当年的图

    3.进行延迟差测试.

    延迟差即服务端发送数据到第一台客户端和服务端发送数据到最后一台客户端的时间差.根据服务端发送数据给不同数量的客户端进行测试.而且,发送数据时,是随机发送.

    延迟差测试结果:

    延迟差测试

    由图所知,延迟差在200毫秒以内的比例基本保持在99%以上.所以符合开发需求(延迟在200毫秒以内).

    4.单次信息收发测试.

    让服务端给每个客户端随机发送200次数据.并计算服务端发送数据到某一客户端,完整的一次收发时间情况.

    单次信息收发测试结果:

    单次信息收发测试

    由图所知,一次收发时间基本在95%以上,这个时间会根据网络状态和数据包大小波动.不过,可以直观看到数据从服务端到客户端的时间.

    GitHub:

    数据粘包处理Demo

    CocoaAsyncSocket客户端Demo

    CocoaAsyncSocket服务端Demo

    CocoaAsyncSocket客户端Demo(含粘包解决和测试)

    CocoaAsyncSocket服务端Demo(含粘包解决和测试)

    iOS应用中使用AsyncSocket库处理Socket通信的用法讲解

    这篇文章主要介绍了iOS应用中使用AsyncSocket库处理Socket通信的用法讲解,AsyncSocket同时支持TCP和UDP,文中展示了其建立断开连接及发送接收消息的操作,very好用,需要的朋友可以参考下

    用socket可以实现像QQ那样发送即时消息的功能。客户端和服务端需要建立长连接,在长连接的情况下,发送消息。客户端可以发送心跳包来检测长连接。

    在iOS开发中使用socket,一般都是用第三方库AsyncSocket,不得不承认这个库确实很强大。下载地址CocoaAsyncSocket

    特性

    AsyncSocket类是支持TCP的。

    AsyncUdpSocket是支持UDP的。

    AsyncSocket是封装了CFSocket和CFSteam的TCP/IP socket网络库。它提供了异步操作,本地cocoa类的基于delegate的完整支持。主要有以下特性:

    队列的非阻塞的读和写,而且可选超时。你可以调用它读取和写入,它会当完成后告知你。

    自动的socket接收。如果你调用它接收连接,它将为每个连接启动新的实例,当然,也可以立即关闭这些连接。

    委托(delegate)支持。错误、连接、接收、完整的读取、完整的写入、进度以及断开连接,都可以通过委托模式调用。

    基于run loop的,而不是线程的。虽然可以在主线程或者工作线程中使用它,但你不需要这样做。它异步的调用委托方法,使用NSRunLoop。委托方法包括socket的参数,可让你在多个实例中区分。

    自包含在一个类中。你无需操作流或者socket,这个类帮你做了全部。

    支持基于IPV4和IPV6的TCP流。

    AsyncUdpSocket是UDP/IP socket网络库,包装自CFSocket。它的工作很像TCP版本,只不过是用于处理UDP的。它包括基于非阻塞队列的发送接收操作,完整的委托支持,基于runloop,自包含的类,以及支持IPV4和IPV6。

    使用AsyncSocket的时候可以做一层封装,根据需求提供几个接口出来。比如:连接、断开连接、发送消息等等。还有接受消息,接受到的消息可以通过通知、代理、block等传出去。

    下面简单介绍一下对AsyncSocket使用.一般来说,一个用户只需要建立一个socket长连接,所以可以用单例类方便使用。

    定义单列类:LGSocketServe

    LGSocketServe.h

    复制代码代码如下:

    //

    //  LGSocketServe.h

    //  AsyncSocketDemo

    //

    #import

    #import "AsyncSocket.h"

    @interface LGSocketServe : NSObject

    + (LGSocketServe *)sharedSocketServe;

    @end

    LGSocketServe.m

    复制代码代码如下:

    //

    //  LGSocketServe.m

    //  AsyncSocketDemo

    //

    #import "LGSocketServe.h"

    @implementation LGSocketServe

    static LGSocketServe *socketServe = nil;

    #pragma mark public static methods

    + (LGSocketServe *)sharedSocketServe {

    @synchronized(self) {

    if(socketServe == nil) {

    socketServe = [[[self class] alloc] init];

    }

    }

    return socketServe;

    }

    +(id)allocWithZone:(NSZone *)zone

    {

    @synchronized(self)

    {

    if (socketServe == nil)

    {

    socketServe = [super allocWithZone:zone];

    return socketServe;

    }

    }

    return nil;

    }

    @end

    建立socket长连接

    LGSocketServe.h

    复制代码代码如下:

    @property (nonatomic, strong) AsyncSocket         *socket;       // socket

    //  socket连接

    - (void)startConnectSocket;

    LGSocketServe.m

    //自己设定

    #define HOST @"192.168.0.1"

    #define PORT 8080

    //设置连接超时

    #define TIME_OUT 20

    - (void)startConnectSocket

    {

    self.socket = [[AsyncSocket alloc] initWithDelegate:self];

    [self.socket setRunLoopModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];

    if ( ![self SocketOpen:HOST port:PORT] )

    {

    }

    }

    - (NSInteger)SocketOpen:(NSString*)addr port:(NSInteger)port

    {

    if (![self.socket isConnected])

    {

    NSError *error = nil;

    [self.socket connectToHost:addr onPort:port withTimeout:TIME_OUT error:&error];

    }

    return 0;

    }

    宏定义一下HOST、PORT、TIME_OUT,实现startConnectSocket方法。这个时候要设置一下AsyncSocket的代理AsyncSocketDelegate。当长连接成功之后会调用:

    复制代码代码如下:

    - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port

    {

    //这是异步返回的连接成功,

    NSLog(@"didConnectToHost");

    }

    心跳

    LGSocketServe.h

    复制代码代码如下:

    @property (nonatomic, retain) NSTimer             *heartTimer;   // 心跳计时器

    LGSocketServe.m

    - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port

    {

    //这是异步返回的连接成功,

    NSLog(@"didConnectToHost");

    //通过定时器不断发送消息,来检测长连接

    self.heartTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(checkLongConnectByServe) userInfo:nil repeats:YES];

    [self.heartTimer fire];

    }

    // 心跳连接

    -(void)checkLongConnectByServe{

    // 向服务器发送固定可是的消息,来检测长连接

    NSString *longConnect = @"connect is here";

    NSData   *data  = [longConnect dataUsingEncoding:NSUTF8StringEncoding];

    [self.socket writeData:data withTimeout:1 tag:1];

    }

    在连接成功的回调方法里,启动定时器,每隔2秒向服务器发送固定的消息来检测长连接。(这个根据服务器的需要就可以了)

    断开连接

    1,用户手动断开连接

    LGSocketServe.h

    复制代码代码如下:

    // 断开socket连接

    -(void)cutOffSocket;

    LGSocketServe.m

    -(void)cutOffSocket

    {

    self.socket.userData = SocketOfflineByUser;

    [self.socket disconnect];

    }

    cutOffSocket是用户断开连接之后,不在尝试重新连接。

    2,wifi断开,socket断开连接

    LGSocketServe.m

    复制代码代码如下:

    - (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err

    {

    NSLog(@" willDisconnectWithError %ld   err = %@",sock.userData,[err description]);

    if (err.code == 57) {

    self.socket.userData = SocketOfflineByWifiCut;

    }

    }

    wifi断开之后,会回调onSocket:willDisconnectWithError:方法,err.code == 57,这个时候设置self.socket.userData = SocketOfflineByWifiCut。

    重新连接

    socket断开之后会回调:

    LGSocketServe.m

    复制代码代码如下:

    - (void)onSocketDidDisconnect:(AsyncSocket *)sock

    {

    NSLog(@"7878 sorry the connect is failure %ld",sock.userData);

    if (sock.userData == SocketOfflineByServer) {

    // 服务器掉线,重连

    [self startConnectSocket];

    }

    else if (sock.userData == SocketOfflineByUser) {

    // 如果由用户断开,不进行重连

    return;

    }else if (sock.userData == SocketOfflineByWifiCut) {

    // wifi断开,不进行重连

    return;

    }

    }

    在onSocketDidDisconnect回调方法里面,会根据self.socket.userData来判断是否需要重新连接。

    发送消息

    LGSocketServe.h

    复制代码代码如下:

    // 发送消息

    - (void)sendMessage:(id)message;

    LGSocketServe.m

    //设置写入超时 -1 表示不会使用超时

    #define WRITE_TIME_OUT -1

    - (void)sendMessage:(id)message

    {

    //像服务器发送数据

    NSData *cmdData = [message dataUsingEncoding:NSUTF8StringEncoding];

    [self.socket writeData:cmdData withTimeout:WRITE_TIME_OUT tag:1];

    }

    //发送消息成功之后回调

    - (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag

    {

    }

    发送消息成功之后会调用onSocket:didWriteDataWithTag:,在这个方法里可以进行读取消息。

    接受消息

    LGSocketServe.m

    复制代码代码如下:

    //设置读取超时 -1 表示不会使用超时

    #define READ_TIME_OUT -1

    #define MAX_BUFFER 1024

    //发送消息成功之后回调

    - (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag

    {

    //读取消息

    [self.socket readDataWithTimeout:-1 buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0];

    }

    //接受消息成功之后回调

    - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag

    {

    //服务端返回消息数据量比较大时,可能分多次返回。所以在读取消息的时候,设置MAX_BUFFER表示每次最多读取多少,当data.length < MAX_BUFFER我们认为有可能是接受完一个完整的消息,然后才解析

    if( data.length < MAX_BUFFER )

    {

    //收到结果解析...

    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];

    NSLog(@"%@",dic);

    //解析出来的消息,可以通过通知、代理、block等传出去

    }

    [self.socket readDataWithTimeout:READ_TIME_OUT buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0];

    接受消息后去解析,然后可以通过通知、代理、block等传出去。在onSocket:didReadData:withTag:回调方法里面需要不断读取消息,因为数据量比较大的话,服务器会分多次返回。所以我们需要定义一个MAX_BUFFER的宏,表示每次最多读取多少。当data.length < MAX_BUFFER我们认为有可能是接受完一个完整的消息,然后才解析 。

    出错处理

    LGSocketServe.m

    复制代码代码如下:

    - (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err

    {

    NSData * unreadData = [sock unreadData]; // ** This gets the current buffer

    if(unreadData.length > 0) {

    [self onSocket:sock didReadData:unreadData withTag:0]; // ** Return as much data that could be collected

    } else {

    NSLog(@" willDisconnectWithError %ld   err = %@",sock.userData,[err description]);

    if (err.code == 57) {

    self.socket.userData = SocketOfflineByWifiCut;

    }

    }

    }

    socket出错会回调onSocket:willDisconnectWithError:方法,可以通过unreadData来读取未来得及读取的buffer。

    使用

    导入#import “LGSocketServe.h”

    复制代码代码如下:

    LGSocketServe *socketServe = [LGSocketServe sharedSocketServe];

    //socket连接前先断开连接以免之前socket连接没有断开导致闪退

    [socketServe cutOffSocket];

    socketServe.socket.userData = SocketOfflineByServer;

    [socketServe startConnectSocket];

    //发送消息 @"hello world"只是举个列子,具体根据服务端的消息格式

    [socketServe sendMessage:@"hello world"];

    如对本文有疑问,请提交到交流社区,广大热心网友会为你解答!!点击进入社区

    您可能感兴趣的文章:

    IOS 详解socket编程[oc]粘包、半包处理

    iOS开发项目- 基于WebSocket的聊天通讯(2)

    iOS开发项目- 基于WebSocket的聊天通讯(1)

    IOS开发网络篇—Socket编程详解

    iOS App通信之local socket示例

    iOS App之间的通信 local socket

    IOS中使用 CocoaAsyncSocket​

    iOS 使用 socket 实现即时通信示例(非第三方库)

    相关文章

      网友评论

      • CoderQY:楼主, 为什么服务端的的代码 也要运行在 手机上啊 ? 服务端不应该是后台吗? 不应该是客户端与后台 实时保持连接吗 ? 这里的服务端是什么意思?
        CoderQY:@X堇色 明白了 非常感谢:+1:
        F麦子:@CoderQY 为了看清交互逻辑而已...

      本文标题:CocoaAsyncSocket使用

      本文链接:https://www.haomeiwen.com/subject/dszscxtx.html