我有两个同龄人。对等A(MAC OSX)在同一端口上绑定了两个套接字。一个在主动模式下操作,一个在被动模式下操作。对等B(任何操作系统)只有一个套接字作用于活动模式。对等A开始在单独的线程中侦听。之后,从另一个线程调用Connect。但是对等B没有收听,所以A的连接呼叫失败了。然后Peer B呼叫Connect,但此连接也失败。
在wireshark中,我看到SYN离开B并进入A的电脑。但是A的Accept API调用不会返回有效的FD,这意味着A无法处理SYN。所以没有SYN-ACK也离开了A的PC。
如果对等体A是除MAC以外的任何操作系统(例如windows,ubuntu,centos),则不会再现此问题。
修改
在创建套接字时,我们设置了一些套接字选项。它们是O_NONBLOCK,TCP_NODELAY,SO_REUSEADDR,SO_LINGER和OSX,iOS SO_NOSIGPIPE。
CODE:
这是A的听力部分。
int iAcceptResult = -1;
if (::listen(currentTcpSocket,1)< 0)
{
Log("Listen error: " + itoa(LastNetworkError());
}
unsigned int i = 0;
while ((iAcceptResult < 0 )
{
sleep(80);
iAcceptResult = Accept(currentTcpSocket);
if (0 == iAcceptResult)
{
//success
break;
}
}
这是A和B的连接部分。
int iConnect = Connect(currentTcpSocket, uAddr, uPort);
if (iConnect < 0)
{
return false;
}
int connectTimeout = 750; //milliseconds
int nWaitResult = UseSelect(currentTcpSocket,connectTimeout);
if (nWaitResult != 0)
{
// timeout or error
}
连接,接受和使用选择功能
int Connect(SOCKET sock, const IpAddress& uAddr, u_int16_t uPort)
{
struct sockaddr_in sin;
memset((char *)&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = uPort;
sin.sin_addr = uAddr.addr4;
return ::connect(sock, (struct sockaddr *)&sin, sizeof(sin));
}
int Accept(SOCKET m_sock)
{
sock_accept = ::accept(m_sock, NULL, NULL);
if (INVALID_SOCKET == sock_accept)
return -1;
return 0;
}
int UseSelect(SOCKET fd, int iTimeoutMs)
{
fd_set readEvents;
fd_set writeEvents;
fd_set exceptEvents;
FD_ZERO(&writeEvents);
FD_ZERO(&readEvents);
FD_ZERO(&exceptEvents);
FD_SET(fd, &exceptEvents);
if(true == read)
{
FD_SET(fd, &readEvents);
}
else
{
FD_SET(fd, &writeEvents);
}
timeval timeout;
timeout.tv_sec = iTimeoutMs / 1000;
timeout.tv_usec = (iTimeoutMs % 1000) * 1000;
return ::select(fd + 1, &readEvents, NULL, &exceptEvents, &timeout);
}