GCDAsyncSocket聊天实践
2019-08-01 本文已影响0人
大猿媛
鉴于服务器已经有搭建好的socket服务器,所以我只关注iOS端如何通过CocoaSyncSocket实现客服聊天
创建聊天工具单例KefuHelper,遵守协议GCDAsyncSocketDelegate
- 初始化时,创建socket对象clientSocket【GCDAsyncSocket】
-(instancetype)init{
if (self = [super init]) {
self.clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
}
return self;
}
- 链接服务器socket
-(void)connect{
NSError *error = nil;
self.isConnected = [self.clientSocket connectToHost:@"47.94.39.111" onPort:8283 error:&error];
QMPLog(@"链接socket服务器----%@",error);
}
- GCDAsyncSocket代理方法实现
#pragma mark - GCDAsyncSocketDelegate
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{
// NSLog(@"连接主机对应端口%@", sock);
[PublicTool showMsg:@"链接成功"];
QMPLog(@"服务器IP: %@-------端口: %d", host,port);
// 连接成功开启定时器,发送心跳包,用于检测链接是否断开
[self addTimer];
// 连接后,可读取服务端的数据
[self.clientSocket readDataWithTimeout:-1 tag:0];
self.isConnected = YES;
}
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
[PublicTool showMsg:@"断开连接"];
QMPLog(@"服务器链接失败: -----%@", err);
self.clientSocket.delegate = nil;
self.clientSocket = nil;
self.isConnected = NO;
[self.connectTimer invalidate];
}
// 接收到socket服务器发来的消息,发送消息后端写了相应API, 不通过socket发送
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSString *text = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
[PublicTool showMsg:[NSString stringWithFormat:@"socket接收数据--%@",text]];
// 读取到服务端数据值后,能再次读取
[self.clientSocket readDataWithTimeout:-1 tag:0];
}
- 心跳连接
- (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];
}
GCDAsyncSocket常见问题
- Error Domain=GCDAsyncSocketErrorDomain Code=4 "Read operation timed out" UserInfo=0xa8db6a0 {NSLocalizedDescription=Read operation timed out}
scoket读取数据超时,当网络不怎么稳定通信方给发送消息的时候时不时的会冒一个这个错误,而且Socket也会自动断开连接。一直跟踪GCDAsyncSocket.m的代码5068行<可能代码有更新的会有点差异>有一个方法
- (void)setupReadTimerWithTimeout:(NSTimeInterval)timeout
这个方法是就是专门监听socket读取数据是否有超时的现象的方法,源代码设置成if(timeout >= 0.0)即检测到超时就抛异常 这样很容易导致socket连接异常。
处理方式:你可以打印一下这个timeout值,就会大概知道你的socket读取数据超时的范围,在项目允许的范围内设置这个值的大小,因为我的项目总是在10以内,所以我设置成if(timeout > 10.0)之后,基本运行的时候就很少抛这个异常了。你也可以再接收到这个异常的时候重新连接一次。
- Error Domain=GCDAsyncSocketErrorDomain Code=3 "Attempt to connect to host timed out" UserInfo=0x7bd14f40 {NSLocalizedDescription=Attempt to connect to host timed out}
socket连接的时候超时,一般发生在你向服务端发送一条连接消息的时候,服务端无响应,一般是由于服务端没有开启服务,也有可能是设置响应时间的timeout值过小,在GCDAsyncSocket.m的代码1938行的位置有一个设置timeout的地方 你可以设置一个稍微比较长的响应时间
- (BOOL)connectToHost:(NSString*)host onPort:(uint16_t)port error:(NSError **)errPtr
{
return [self connectToHost:host onPort:port withTimeout:5 error:errPtr];
}
-
Error Domain=GCDAsyncSocketErrorDomain Code=51,网络断开,可以检查一下网络连接状态
-
Error Domain=NSPOSIXErrorDomain Code=61 "Connection refused" UserInfo=0x7b288750 {NSLocalizedFailureReason=Error in connect() function, NSLocalizedDescription=Connection refused}
服务器没启动,或者端口没开启。