我有一个listen函数,用于检查新连接是否在服务器套接字上等待,如果我先启动客户端然后启动服务器,我看到了新的连接'多次,如果我启动服务器然后客户端,我会看到它一次。
我是否缺少一些关于Posix标准的知识或者缺乏知识(最有可能)?
轮询新连接的功能是:
bool IPV4Socket::HasNewConnection( TimeoutValue *timeout )
{
SocketSet read_fd_set;
bool hasConnection = false;
SocketSet_ZERO( &read_fd_set );
SocketSet_SET( m_socket, &read_fd_set );
if ( m_socketAdaptor->select( m_socket + 1
, &read_fd_set
, NULL
, NULL
, timeout ) < 0 )
{
throw( std::string( "::select() failed, unable to continue." ) );
}
for ( unsigned int i = 0; i <= m_socket; i++ )
{
if ( m_socketAdaptor->SocketSet_ISSET( i, &read_fd_set ) &&
( i == m_socket ) )
{
hasConnection = true;
}
}
return hasConnection;
}
使用以下方式调用:
while( 1 )
{
if ( socket->HasNewConnection( &newConnTimeout ) )
{
std::cout << "[INFO] A new connection is waiting...." << std::endl;
acceptedSocket = socket->Accept( &newConnTimeout );
if ( acceptedSocket )
{
std::cout << "[INFO] New connection received...." << std::endl;
}
}
}
连接功能是:
while( connStatus == false )
{
socket = socketlayer->CreateSocket( socketlayer::SockType_stream );
socket->SetNonBlocking( true );
socket->SetSocketOption( socketlayer::SocketOption_KeepAlive, true );
socket->SetSocketOption( socketlayer::SocketOption_ReuseAddr, true );
connStatus = socket->Connect( "127.0.0.1", 18000, &tv );
if ( connStatus == false ) socket->Close();
}
因为这是一段开源代码,可以在Sourceforge
中找到通过以下方式完成连接:
bool IPV4Socket::Connect( std::string hostname
, unsigned short remotePort
, TimeoutValue *timeout )
{
AddrInfo getResults;
AddrInfo getaddrinfoHints;
int connReturn = 0;
SockAddr_In *addrData;
//bool connectSuccess = false;
std::string service = std::to_string( remotePort );
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 );
if ( connReturn == SocketError)
{
int m_lastErrorCode = m_socketAdaptor->GetLastError();
// Connection error : FATAL
if ( ( m_lastErrorCode != SockErr_EWOULDBLOCK) &&
( m_lastErrorCode != SockErr_EALREADY ) &&
( m_lastErrorCode != SockErr_EINPROGRESS ) )
{
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
, NULL
, timeout );
// Check for Socket Error or timeout
if ( ( selectReturn == SocketError ) || ( selectReturn == 0 ) )
{
return false;
}
return true;
}
答案 0 :(得分:1)
套接字设置为非阻塞,对connect的调用通常会返回错误,因为无法确定连接状态:
使用非阻塞套接字,无法立即完成连接尝试。在这种情况下,connect将返回SOCKET_ERROR,WSAGetLastError将返回WSAEWOULDBLOCK。 (...)
- 使用select函数通过检查套接字是否可写来确定连接请求的完成。
鉴于您的客户端代码和上述文档,它永远不能成功连接。但是,由于您使用环回设备进行连接,因此在服务器已经运行的情况下,由于时间安排,连接会立即被接受(并且确实发生)。
因此,如上所述,在select
调用后在客户端上使用connect
,或者只使用阻塞套接字。
答案 1 :(得分:1)
HasNewConnection()
超时时, select()
忽略,并且它的循环完全是冗余的,因为它一次只在一个套接字上等待。代码可以简化为以下内容:
bool IPV4Socket::HasNewConnection( TimeoutValue *timeout )
{
SocketSet read_fd_set;
SocketSet_ZERO( &read_fd_set );
SocketSet_SET( m_socket, &read_fd_set );
int ret = m_socketAdaptor->select( m_socket + 1
, &read_fd_set
, NULL
, NULL
, timeout );
if (ret < 0)
{
throw( std::string( "::select() failed, unable to continue." ) );
}
return (ret > 0);
}
话虽如此,我只是查看Accept()
的代码(请不要让人们查看外部网站的代码,请始终将其放在您的实际问题中,它属于哪里)并且我看到一个大错误。您对SocketSet
的两个不同参数使用相同的select()
变量,因此当select()
退出时,该变量的内容将不确定。如果您想同时检查read/write
和except
参数,则需要使用单独的变量(就像在Connect()
中一样):
iSocket *IPV4Socket::Accept( TimeoutValue *timeout )
{
SocketSet readFDS;
SocketSet exceptFDS;
Socket newSocketHandle = 0;
// Clear all the socket FDS structures
SocketSet_ZERO( &readFDS );
SocketSet_ZERO( &exceptFDS );
// Add listening socket to the FDS structures
SocketSet_SET( m_socket, &readFDS );
SocketSet_SET( m_socket, &exceptFDS );
if ( m_socketAdaptor->select( m_socket + 1, &readFDS, NULL, &exceptFDS, timeout ) > 0 )
{
if ( !m_socketAdaptor->SocketSet_ISSET( m_socket, &exceptFDS ) )
{
newSocketHandle = m_socketAdaptor->accept( m_socket, NULL, NULL );
if ( newSocketHandle != Invalid_Socket )
{
return new IPV4Socket( newSocketHandle, m_socketType, m_socketAdaptor );
}
}
}
return NULL;
}
但是,对于accept()
来说,这是相当多余的,您根本不需要使用except
参数(这对connect()
有意义,但不是accept()
}):
iSocket *IPV4Socket::Accept( TimeoutValue *timeout )
{
SocketSet readFDS;
Socket newSocketHandle = 0;
// Clear the socket FDS structure
SocketSet_ZERO( &readFDS );
// Add listening socket to the FDS structure
SocketSet_SET( m_socket, &readFDS );
if ( m_socketAdaptor->select( m_socket + 1, &readFDS, NULL, NULL, timeout ) > 0 )
{
newSocketHandle = m_socketAdaptor->accept( m_socket, NULL, NULL );
if ( newSocketHandle != Invalid_Socket )
{
return new IPV4Socket( newSocketHandle, m_socketType, m_socketAdaptor );
}
}
return NULL;
}