【发布时间】:2016-03-04 10:48:05
【问题描述】:
我正在尝试编写一个使用非阻塞套接字连接的客户端,但我对应该检查什么以及检查顺序感到困惑。我查看了Non blocking socket - how to check if a connection was successful 问题并试图在没有运气的情况下实施它。这是 Windows,而不是 Linux。我更喜欢使用 Posix 方法,所以以后可以移植它。
问题是在 Windows 中,即使服务器不存在,我也会看到 EWOULDBLOCK。一旦服务器出现,我就会在服务器上看到多个连接,因此我没有正确处理尚未完成的“阻塞”连接。
连接代码是(并且在循环中调用,如果它不能立即连接,直到 100 次尝试或它连接):
bool IPV4Socket::Connect( std::string hostname
, unsigned short remotePort
, TimeoutValue *timeout )
{
AddrInfo getResults;
AddrInfo getaddrinfoHints;
int connReturn = 0;
SockAddr_In *addrData;
std::string service = std::to_string( remotePort );
int errorCode = 0;
getaddrinfoHints.ai_family = AddressFamily_inet;
getaddrinfoHints.ai_socktype = SockType_stream;
if ( m_socketAdaptor->getaddrinfo( hostname
, service
, &getaddrinfoHints
, &getResults ) != 0 )
{
return false;
}
addrData = (SockAddr_In *)&( *getResults.ai_addr.begin() );
connReturn = m_socketAdaptor->connect( m_socket
, (const Sockaddr *)addrData
, (int)getResults.ai_addrlen );
static int staticLastErr = 0;
static int staticConnStat = -2048;
if ( connReturn == 0 )
{
m_isConnected = true;
return true;
}
if (connReturn != staticConnStat)
{
std::cout << "[DEBUG] IPV4Socket::Connect() ::Connect() returned : " << connReturn << std::endl;
staticConnStat = connReturn;
}
// Check if the error is fatal - e.g. not blocking related!
if ( connReturn == SocketError )
{
errorCode = m_socketAdaptor->GetLastError();
// Check for fatal connection error
#ifdef LIBSSL_OS_WIN32
if ( errorCode != SockErr_EWOULDBLOCK )
#else
if ( errorCode != SockErr_EINPROGRESS )
#endif
{
Close();
return false;
}
}
SocketSet writeFDS;
SocketSet exceptFDS;
int selectReturn = 0;
// Clear all the socket FDS structures
SocketSet_ZERO( &writeFDS );
SocketSet_ZERO( &exceptFDS );
// Put the socket into the FDS structures
SocketSet_SET( m_socket, &writeFDS );
SocketSet_SET( m_socket, &exceptFDS );
selectReturn = m_socketAdaptor->select( m_socket + 1
, NULL
, &writeFDS
, &exceptFDS
, timeout );
// select() failed or timed out, connection wasn't successful!
if ( ( selectReturn == SocketError ) || ( selectReturn == 0) )
{
if ( selectReturn != 0 ) std::cout << "[DEBUG] m_socketAdaptor->select() returned : " << selectReturn << std::endl;
Close();
return false;
}
// Check for error (exception) first
if ( m_socketAdaptor->SocketSet_ISSET( m_socket, &exceptFDS ) )
{
std::cout << "[DEBUG] ::Connect() found excetion on 'm_socketAdaptor->SocketSet_ISSET'" << std::endl;
Close();
return false;
}
if ( m_socketAdaptor->SocketSet_ISSET( m_socket, &writeFDS ) )
{
std::cout << "[DEBUG] ::Connect() m_socketAdaptor->SocketSet_ISSET( m_socket, &writeFDS ) [FOUND]" << std::endl;
m_isConnected = true;
return true;
}
Close();
return false;
}
关闭函数:
int IPV4Socket::Close()
{
int errNo = -1;
if ( m_socket >= 0 )
{
errNo = m_socketAdaptor->shutdown(m_socket, ShutdownFlag_Both);
if ( errNo < 0 )
{
int lastError = m_socketAdaptor->GetLastError();
if ( lastError != SockErr_ENOTCONN && lastError != SockErr_EINVAL ) return lastError;
}
errNo = m_socketAdaptor->closesocket(m_socket);
if (errNo < 0) return m_socketAdaptor->GetLastError();
}
return 0;
}
使用 cmets 更新连接:
bool IPV4Socket::Connect( std::string hostname
, unsigned short remotePort
, TimeoutValue *timeout )
{
bool connectReturn = false;
if ( m_incompleteConnect == false )
{
connectReturn = PerformConnect(hostname, remotePort );
}
// If connect failed (returned false) then abort!
if ( connectReturn == false ) return false;
// If Connect() returned success, but didn't connect, it is because of a
// blocking IO not completing in time and needs to be retried, otherwise a
// connection was successful and just return success.
if ( connectReturn && m_isConnected ) return true;
m_incompleteConnect = true;
fd_set writeFDS;
fd_set exceptFDS;
// Clear all the socket FDS structures
FD_ZERO( &writeFDS );
FD_ZERO( &exceptFDS );
// Put the socket into the FDS structures
FD_SET( m_socket, &writeFDS );
FD_SET( m_socket, &exceptFDS );
int selectReturn = ::select( m_socket + 1
, NULL
, &writeFDS
, &exceptFDS
, (const timeval *)timeout);
// Check if ::select() has timed out, if so, connection wasn't successful!
if ( selectReturn == 0 )
{
m_incompleteConnect = false;
return false;
}
if ( FD_ISSET( m_socket, &writeFDS ) )
{
m_isConnected = true;
m_incompleteConnect = false;
}
// Check for error (exception)
if ( FD_ISSET( m_socket, &exceptFDS ) )
{
m_incompleteConnect = false;
return false;
}
return m_isConnected;
}
bool IPV4Socket::PerformConnect( std::string hostname, int port )
{
addrinfo *results;
addrinfo hints;
int connReturn = 0;
std::string service = std::to_string( port );
int errorCode = 0;
bool returnValue = false;
memset( &hints, 0, sizeof hints );
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if (::getaddrinfo( hostname.c_str(), service.c_str(), &hints, &results) != 0 )
{
return false;
}
// Attempt the connection...
connReturn = ::connect( m_socket, results->ai_addr, results->ai_addrlen );
// If connect returned error (SOCKET_ERROR), check that it's not fatal -
// e.g. EWOULDBLOCK, if it is then connect can check until complete!
if (connReturn == SOCKET_ERROR)
{
int errorCode = WSAGetLastError();
returnValue = (errorCode == WSAEWOULDBLOCK) ? true : false;
}
else
{
m_isConnected = true;
returnValue = true;
}
return returnValue;
}
谢谢你:)
【问题讨论】:
-
“没有运气”不是问题描述。
-
@EJP 添加了额外的问题“描述”。
-
无论目标是否存在,您总是会首先看到 EWOILDBLOCK。在
connect()返回之后发生的所有事情就是 SYN 已排队等待传递。正是以下select()和错误检查告诉您连接是否成功、失败、超时等。 -
@EJP 谢谢,我不明白为什么服务器端看到多个连接(例如 10-15),是什么让我认为 connect() 工作不正常.
-
你没有关闭任何东西。
标签: c++ sockets network-programming