我正在尝试编写一个使用非阻塞套接字连接的客户端,我对应该检查的内容以及以何种顺序感到困惑。我查看了Non blocking socket - how to check if a connection was successful问题并尝试实施它而没有运气。这是Windows,而不是Linux。我更喜欢使用Posix方法,所以我可以稍后移植它。
问题在于,即使服务器不存在,我也会看到EWOULDBLOCK。一旦服务器出现,我在服务器上看到多个连接,因此我没有正确处理“被阻止的”#39;尚未完成的连接。
连接代码是(并且在循环中调用,如果它不能直接连接,直到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;
}
谢谢:)
答案 0 :(得分:1)
connect from msdn(仔细阅读返回值)
所以第一次尝试连接循环时,会得到SOCKET_ERROR和errno = WSAEWOULDBLOCK; (这意味着一切都好!)
如果你继续循环(再次连接),你得到SOCKET_ERROR和errno = WSAEINPROGRESS,所以不要再循环!
所以在获得WSAEWOULDBLOCK之后,正如所解释的那样,你可以,
使用select通过检查套接字是否可写来确定连接请求的完成。
fd_set wr_set;
struct timeval timeout={10,1};
int err=WSAConnect(s,(struct sockaddr*)&addr,len,0,0,0,0);
printf("connect: %d %d\n",err,WSAGetLastError());
FD_ZERO(&wr_set);
FD_SET(s, &wr_set);
printf("%d\n",wr_set.fd_count);
select(s,0,&wr_set,0,&timeout); <- blocks (10s) only if can't get socket writable
printf("%d\n",wr_set.fd_count);
答案 1 :(得分:1)
调用import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.event.EventHandler;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.Circle;
public class circle extends Application{
Scene scene;
VBox v;
public void start(Stage stage){
double x = 0;
double y = 0;
v = new VBox(10);
scene = new Scene(v, 300, 300);
scene.setOnMousePressed(new EventHandler<MouseEvent>(){
public void handle(MouseEvent e)
{
x = e.getX();
y = e.getY();
}
});
Circle circle = new Circle();
circle.setCenterX(x);
circle.setCenterY(y);
v.getChildren().add(circle);
stage.setScene(scene);
stage.show();
}
}
后,您必须立即致电GetLastError
。这两个函数之间的所有代码都可以清除或更改错误代码。
您的代码将输出写入这两个调用之间的流。如果该写入没有产生错误,则connect
不会从正在进行的GetLastError
中获取错误。
将connect
行移至errorCode = m_socketAdaptor->GetLastError();
后面。{/ p>
答案 2 :(得分:0)
我不确定这是否有帮助,但我发现在Windows平台上使用connect()函数时它将返回一个WSAEWOULDBLOCK(我认为它与EWOULDBLOCK相同)。你必须在以后确定连接是否真的成功。如果你想要,你可以检查出https://msdn.microsoft.com/en-us/library/windows/desktop/ms737625%28v=vs.85%29.aspx(我所指的部分是“返回值”)
答案 3 :(得分:0)
问题在于,即使服务器不存在,我也会看到EWOULDBLOCK。
由于套接字处于非阻塞模式,因此connect()会在实际连接到服务器之前立即返回。因此,预计会获得EINPROGRESS。
没有m_socketAdaptor的代码,我可以产生这个,但我很确定问题在于:
连接代码是(并且在循环中被调用,如果它不能直接连接,直到100次尝试或它连接):
如果你已经调用了connect()一次并获得了(WSA)EINPROGRESS,那么你不应该再次调用connect,但是你应该在下面的迭代中执行select()部分。你也不应该在检查一次后关闭()。
所以你应该做的是:
getaddrinfo();
connect();
select();
select()
close()
go back to 1st step