我想寫一個客戶端連接使用非阻塞套接字,我很困惑,我應該檢查和以什麼順序。我看着Non blocking socket - how to check if a connection was successful的問題,並試圖實現它沒有運氣。這是Windows,而不是Linux。我更喜歡使用Posix方法,所以我可以稍後再移植它。處理'非阻塞'套接字連接
問題是,即使服務器不存在,我也會看到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;
}
更新連接使用註釋:
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
@EJP添加額外的'描述'的問題。 –
您會首先看到EWOILDBLOCK,無論目標是否存在。在connect()返回後發生的所有事情是SYN已經排隊等待傳遞。它是以下'select()'和錯誤檢查,告訴你連接是否成功,失敗,超時等。 – EJP