在项目中用到GCDAsyncsocket,Mac端作为服务器,手机端作为客户端,在通信过程中,不定时出现以下错误:
error domain=gcdasyncsocketerrordomain code=7 socket closed by remote peer
排错
此错误一般由于服务端关闭而导致出错,出现此问题的一般排查步骤:
-
服务端要将新连接的socket强引用,否则会出现此错误。
@property (nonatomic, strong) NSMutableArray *socketsArray; - (void)viewDidLoad { _socketsArray = [[NSMutableArray alloc] init]; } - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket { [_socketsArray addObject:newSocket]; [newSocket readDataWithTimeout:-1 tag:0]; }
-
没有规律发送心跳或者心跳间隔过长。“Socket closed by remote peer” - GCDAsyncSocket Error Code 7中有人尝试将心跳从10分钟改为5分钟之后解决了此错误,可参考。由于我7秒发送一次心跳,因此排除此可能性。
-
是否使用相同的凭证从不同的客户端登录,并且在服务器设置中有这样的设置:如果有资源冲突,立即踢出另一个资源,在服务器>服务器设置>资源策略。
排查
经过排查,发现存在以下问题:
- socket的代理回调队列在主线程,会影响计时,超时从而导致出现此错误。
- 每个socket writeData之后应没有发送readDataWithTimeout,代理不再接收数据。
- 定时器用NSTimer或者PerformSelectAfter两种方式可能存在问题。
解决
-
socket的代理回调队列放在子线程。
//启动服务 _severPort = [OsxPortListHelper port]; _socketDelegateQueue = dispatch_queue_create("socket_delegate_queue", NULL);//注意成员变量引用,否则会释放 _serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:_socketDelegateQueue]; [_serverSocket acceptOnPort:_severPort error:nil];
-
每个socket writeData:之后应发送readDataWithTimeout:方法,告诉代理接收数据。
[sock writeData:data withTimeout:-1 tag:0]; [sock readDataWithTimeout:-1 tag:0];
-
定时器改用
_sendHeartBeatTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _socketDelegateQueue);
方式。
参考
socket closed by remote peer gcdasyncsocket
写网络视频监视器中的总结 (一)