问题描述:
最近在我的项目组中遇到一个问题,由于我们项目使用采用的是TCP+Protobuf来做主要通讯协议,心跳是使用udp。
服务器分为多台,各服务器负责各自的任务,比如我们账号服务器只负责与用户数据相关的任务,推送服务器负责服务器向前端通知等等。
当我们的TCP中间由于某种原因与服务器断开连接,但是服务器和客户端都没有接到断开通知,或者需要好长时间才能知道(默认的超时时间为7200s,及2个小时,重试5次),彼此之间认为链接正常,但是在这个时间点如果服务器推送一个消息我们是接不到的,所以导致了要设置keepalive的问题。
问题解决:
在socket已经链接成功代理方法中:
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
添加代码:
[sock performBlock:^{
[self setKeepAliveWithSocketFD:sock.socketFD];
}];
- (void)setKeepAliveWithSocketFD:(int)socketFD {
int keepAlive = 1;
int idleTime = 10;
int intvlTime = 10;
int cntCount = 3;
if (setsockopt(socketFD, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(keepAlive)) < 0) {
NSLog(@"#######################: %s", strerror(errno));
}
if (setsockopt(socketFD, IPPROTO_TCP, TCP_KEEPALIVE, &idleTime, sizeof(idleTime)) < 0) {
NSLog(@"#######################: %s", strerror(errno));
}
if (setsockopt(socketFD, IPPROTO_TCP, TCP_KEEPINTVL, &intvlTime, sizeof(intvlTime)) < 0) {
NSLog(@"#######################: %s", strerror(errno));
}
if (setsockopt(socketFD, IPPROTO_TCP, TCP_KEEPCNT, &cntCount, sizeof(cntCount)) < 0) {
NSLog(@"#######################: %s", strerror(errno));
}
}
各个字段解释:
keepAlive // 是否使用keepalive
idleTime // 开始首次keepalive探测前的TCP空闭时间
intvlTime // 两次keepalive探测间的时间间隔
cntCount // keepalive重试次数
如果没有error打出说明设置没问题了。
遇到的问题及思考:
当我去写这段代码时遇到了一点小问题,原因是我的粗心大意及没对GCDAsyncSocket API文档认真阅读导致的。
就是获取套接字没有在performBlock中获取。API文档:
/**
* It's not thread-safe to access certain variables from outside the socket's internal queue.
*
* For example, the socket file descriptor.
* File descriptors are simply integers which reference an index in the per-process file table.
* However, when one requests a new file descriptor (by opening a file or socket),
* the file descriptor returned is guaranteed to be the lowest numbered unused descriptor.
* So if we're not careful, the following could be possible:
*
* - Thread A invokes a method which returns the socket's file descriptor.
* - The socket is closed via the socket's internal queue on thread B.
* - Thread C opens a file, and subsequently receives the file descriptor that was previously the socket's FD.
* - Thread A is now accessing/altering the file instead of the socket.
*
* In addition to this, other variables are not actually objects,
* and thus cannot be retained/released or even autoreleased.
* An example is the sslContext, of type SSLContextRef, which is actually a malloc'd struct.
*
* Although there are internal variables that make it difficult to maintain thread-safety,
* it is important to provide access to these variables
* to ensure this class can be used in a wide array of environments.
* This method helps to accomplish this by invoking the current block on the socket's internal queue.
* The methods below can be invoked from within the block to access
* those generally thread-unsafe internal variables in a thread-safe manner.
* The given block will be invoked synchronously on the socket's internal queue.
*
* If you save references to any protected variables and use them outside the block, you do so at your own peril.
**/
我不明白我获取socket套接字为什么要在GCDAsyncSocket 中的socket队列中。这个问题我会在后面研究下并补上的。或者有知道的麻烦说一下,谢谢。
希望对你有所帮助。