I used the following C++ code to create a TCP/IP client. In actual use, I use it in a loop and keep checking for connections until the server is ready.
This code keeps looping through the order 0.2, 0.6, 1 before connecting to the server.
After a successful connection to the server, nothing is printed. No errors are printed, and almost nowhere is passed.
How do I know TCP/IP made a successful connection?
int connect_timeout(int & socket, struct sockaddr * name, int namelen, timeval timeout)
{
unsigned long mode = 1;
int result= ioctlsocket(socket, FIONBIO, &mode);
if(NO_ERROR != result)
{
//std::cout << "0.0---------------" << std::endl;
return -1;
}
else
{
//std::cout << "0.1---------------" << std::endl;
}
result= connect(socket, name, namelen);
if(SOCKET_ERROR == result)
{
int errono = WSAGetLastError();
if(WSAEWOULDBLOCK == errono)
{
std::cout << "0.2---------------" << std::endl;
errono = 0;
}
else
{
//std::cout << "0.3---------------" << std::endl;
mode = 0;
ioctlsocket(socket, FIONBIO, &mode);
return -1;
}
}
else
{
std::cout << "0.4---------------" << std::endl;
}
mode = 0;
result= ioctlsocket(socket, FIONBIO, &mode);
if(0 < result)
{
//error
std::cout << "0.5---------------" << std::endl;
return -1;
}
else
{
std::cout << "0.6---------------" << std::endl;
}
fd_set readFd, writeFd, errFd;
FD_ZERO(&readFd);
FD_ZERO(&writeFd);
FD_ZERO(&errFd);
FD_SET(socket, &readFd);
FD_SET(socket, &writeFd);
FD_SET(socket, &errFd);
int sockNum = select(socket + 1, &readFd, &writeFd, &errFd, &timeout);
if(0 == sockNum)
{
//timeout
std::cout << "1---------------" << std::endl;
return -1;
}
else if(FD_ISSET(socket, &readFd) || FD_ISSET(socket, &writeFd) )
{
std::cout << "2---------------" << std::endl;
}
else
{
//error
std::cout << "3---------------" << std::endl;
return -1;
}
std::cout << "4---------------" << std::endl;
return 0;
}
The connect()
documentation says:
With a nonblocking socket, the connection attempt cannot be completed immediately. In this case, connect will return
SOCKET_ERROR
, andWSAGetLastError
will returnWSAEWOULDBLOCK
. In this case, there are three possible scenarios:
- Use the
select
function to determine the completion of the connection request by checking to see if the socket is writable.- ...
Until the connection attempt completes on a nonblocking socket, all subsequent calls to
connect
on the same socket will fail with the error codeWSAEALREADY
, andWSAEISCONN
when the connection completes successfully. Due to ambiguities in version 1.1 of the Windows Sockets specification, error codes returned fromconnect
while a connection is already pending may vary among implementations. As a result, it is not recommended that applications use multiple calls toconnect
to detect connection completion. If they do, they must be prepared to handleWSAEINVAL
andWSAEWOULDBLOCK
error values the same way that they handleWSAEALREADY
, to assure robust operation....
For connection-oriented, nonblocking sockets, it is often not possible to complete the connection immediately. In such a case, this function returns the error
WSAEWOULDBLOCK
. However, the operation proceeds.When the success or failure outcome becomes known, it may be reported in one of two ways, depending on how the client registers for notification.
- If the client uses the
select
function, success is reported in thewritefds
set and failure is reported in theexceptfds
set.- ...
And the select()
documentation says:
The parameter
writefds
identifies the sockets that are to be checked for writability. If a socket is processing aconnect
call (nonblocking), a socket is writable if the connection establishment successfully completes. ...The parameter
exceptfds
identifies the sockets that are to be checked for the presence of OOB data or any exceptional error conditions.Note Out-of-band data will only be reported in this way if the option
SO_OOBINLINE
is FALSE. If a socket is processing aconnect
call (nonblocking), failure of the connect attempt is indicated inexceptfds
(application must then callgetsockopt
SO_ERROR
to determine the error value to describe why the failure occurred). This document does not define which other errors will be included.
You are turning off the socket's non-blocking mode before you are calling select()
. You need to wait for the non-blocking connect()
operation to complete before you can then mess around with the socket's settings.
Try something more like this instead:
int connect_timeout(int & socket, struct sockaddr * name, int namelen, timeval timeout)
{
u_long mode = 1;
int errNo;
int result = ioctlsocket(socket, FIONBIO, &mode);
if (SOCKET_ERROR == result)
{
errNo = WSAGetLastError();
std::cout << "Unable to set socket to non-blocking mode. Error: " << errNo << std::endl;
return -1;
}
std::cout << "Connecting..." << std::endl;
result = connect(socket, name, namelen);
if (SOCKET_ERROR == result)
{
errNo = WSAGetLastError();
if (WSAEWOULDBLOCK == errNo)
{
fd_set writeFd, errFd;
FD_ZERO(&writeFd);
FD_ZERO(&errFd);
FD_SET(socket, &writeFd);
FD_SET(socket, &errFd);
result = select(socket + 1, NULL, &writeFd, &errFd, &timeout);
if (SOCKET_ERROR == result)
{
errNo = WSAGetLastError();
}
else if (0 == result)
{
result = SOCKET_ERROR;
errNo = WSAETIMEDOUT;
}
else if (FD_ISSET(socket, &errFd))
{
result = SOCKET_ERROR;
errNo = 0;
int len = sizeof(errNo);
getsockopt(socket, SOL_SOCKET, SO_ERROR, (char*)&errNo, &len);
}
else
{
result = errNo = 0;
}
}
if (SOCKET_ERROR == result)
{
if (WSAETIMEDOUT == errNo)
std::cout << "Connection timed out" << std::endl;
else
std::cout << "Unable to connect. Error: " << errNo << std::endl;
mode = 0;
ioctlsocket(socket, FIONBIO, &mode);
return -1;
}
}
std::cout << "Connected" << std::endl;
mode = 0;
result = ioctlsocket(socket, FIONBIO, &mode);
if (SOCKET_ERROR == result)
{
errNo = WSAGetLastError();
std::cout << "Unable to set socket to blocking mode. Error: " << errNo << std::endl;
return -1;
}
return 0;
}