我想在Android中使用NDK创建一个套接字,但是有时在连接服务器时会出现一些错误,并且可以确保用户手机的网络可用。
一种情况是超时错误,另一种情况似乎是连接被拒绝,因为我收到了以下日志,我认为第二种错误是连接被拒绝,因为getsockopt
的错误是111,即使strerror
给了我操作正在进行中,但服务器地址有效:
connect::socket error: Operation now in progress
Or
connect::error:111, Operation now in progress
这是我的代码段:
bool connect(int sockfd, struct sockaddr *address, socklen_t address_len, int timeout) {
int ret = 0;
struct timeval tv;
fd_set mask;
// set socket non block
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
// use select to check socket connection
ret = connect(sockfd, address, address_len);
if (-1 == ret) {
if (errno != EINPROGRESS) {
perror("connect");
inetConnectFailCode = errno;
LOG(TAG.c_str(), "connect::errno != EINPROGRESS: %s", strerror(errno));
return false;
}
LOG(TAG.c_str(), "connecting...\n");
FD_ZERO(&mask);
FD_SET(sockfd, &mask);
tv.tv_sec = timeout;
tv.tv_usec = 0;
if (select(sockfd + 1, NULL, &mask, NULL, &tv) > 0) {
int error = 0;
socklen_t tmpLen = sizeof(int);
int retopt = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &tmpLen);
if (retopt != -1) {
if (0 == error) {
LOG(TAG.c_str(), "has connect");
return true;
} else {
//I get error here
LOG(TAG.c_str(), "connect::error:%d, %s", error, strerror(errno));
return false;
}
} else {
LOG(TAG.c_str(), "connect::socket error:%d", error);
return false;
}
} else {
//timeout, and I get error here sometimes
LOG(TAG.c_str(), "connect::socket error: %s", strerror(errno));
return false;
}
}
LOG(TAG.c_str(), "has connect");
return true;
}
这个问题困扰了我很长一段时间,任何人都可以帮我一个忙,谢谢你的进步。
答案 0 :(得分:2)
如果getsockopt(SO_ERROR)
失败,则您将输出error
,即使该值无效。但是,更重要的是,如果getsockopt(SO_ERROR)
成功但error
不为0,则您将errno
传递给strerror()
而不是传递error
。最初失败的errno
调用中EINPROGRESS
仍为connect()
,因此您的错误消息为"Operation now in progress"
。错误111是ECONNREFUSED
,应该是"Connection refused"
。
此外,如果select()
返回<= 0,则无论实际返回什么strerror(errno)
,您都将输出select()
。 errno
仅在select()
返回-1的情况下才有效。如果select()
返回0,则不能保证更新errno
。
请尝试以下类似操作:
bool connect(int sockfd, struct sockaddr *address, socklen_t address_len, int timeout) {
// set socket non block
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
// use select to check socket connection
int ret = connect(sockfd, address, address_len);
if (-1 == ret) {
int error = errno;
if (EINPROGRESS == error) {
LOG(TAG.c_str(), "connecting...\n");
fd_set mask;
struct timeval tv;
FD_ZERO(&mask);
FD_SET(sockfd, &mask);
tv.tv_sec = timeout;
tv.tv_usec = 0;
ret = select(sockfd + 1, NULL, &mask, NULL, &tv);
if (0 < ret) {
socklen_t tmpLen = sizeof(int);
ret = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &tmpLen);
if (-1 == ret) {
error = errno;
}
} else if (0 == ret) {
error = ETIMEDOUT;
} else {
error = errno;
}
}
if (0 != error) {
inetConnectFailCode = error;
LOG(TAG.c_str(), "connect::error:%d, %s", error, strerror(error));
return false;
}
}
LOG(TAG.c_str(), "has connect");
return true;
}
话虽如此,如果服务器计算机可访问并收到您的连接请求,您会收到一个ECONNREFUSED
错误,但是由于当时它不能接受您的连接而主动拒绝了它,原因可能是:
请求的端口未打开以供监听。
侦听套接字的未决连接积压已满。
无法区分哪种情况,因此您只需要在以后的时间重新尝试连接,最好是经过一小段时间(例如至少5秒)之后。如果连续几次尝试都失败,则连接失败。
如果服务器计算机可以访问并且正在侦听,但在超时时间过去之前没有完成三向握手,则可能会收到ETIMEDOUT
错误。
如果防火墙阻止了您的连接,则可能还会发生两种错误。
更新:根据我发布的an earlier answer的评论,如果积压已满,只有Windows服务器会导致ECONNREFUSED
。 *由九台服务器代替ETIMEDOUT
。