Click to See Complete Forum and Search --> : Non blocking connect()


Red Squirrel
March 29th, 2009, 06:21 PM
I could have sworn I got this working at one point but I must of misplaced the code and I never added it to my socket class I use now instead of having to redo the whole socket handling each and every time.

I got this part working:

int flags = fcntl(sockfd,F_GETFL,0);
fcntl(sockfd,F_SETFL, flags | O_NONBLOCK);



But there is a loop of some sort I have to do using Sleep() until I reach a certain amount of interations, and think I also have to use select() somewhere.

I just can't seem to peiece it all together and online resources have not been very helpful and showing a complete program making use of this.

Basically, I want connect() to timeout after like 5 seconds. By default it just sits there forever. Oddly, I've seen times where it does not. It seems to be very random. I want it to wait 5 seconds no matter what. If it can't connect, it returns error, and my program can move on.


With the above code I have it where it fails each time now, so I am getting somewhere... My entire code is this:


bool SocketClient::Connect()
{
#ifdef WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
fprintf(stderr, "WSAStartup failed.\n");
exit(1);
}
#endif



sockfd = socket(PF_INET, SOCK_STREAM, 0);

if ((he=gethostbyname(ip.c_str())) == NULL) { // get the host info
status=2;
return false;
}

dest_addr.sin_family = AF_INET; // host byte order
dest_addr.sin_port = htons(port); // short, network byte order
dest_addr.sin_addr = *((struct in_addr *)he->h_addr);
memset(dest_addr.sin_zero, '\0', sizeof dest_addr.sin_zero);


//set non blocking mode:
int flags = fcntl(sockfd,F_GETFL,0);
fcntl(sockfd,F_SETFL, flags | O_NONBLOCK);

//connect:
int res = connect(sockfd, (struct sockaddr *)&dest_addr, sizeof dest_addr);
if(res==-1)
{
status=2;
return false;
}

#ifdef WIN32
//enable non blocking sockets for win32:
u_long arg = 1;
ioctlsocket(sockfd, FIONBIO, &arg);
#endif

status=1;

return true;
}

Red Squirrel
March 29th, 2009, 07:02 PM
I think I managed to get it working in Linux, not to figure how to get it working in windows. Wish they'd just standardize all these functions so it works cross platform accross the board.




bool SocketClient::Connect()
{
#ifdef WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
{
status=2;
return false;
}
#endif



sockfd = socket(PF_INET, SOCK_STREAM, 0);

if ((he=gethostbyname(ip.c_str())) == NULL) { // get the host info
status=2;
return false;
}



#ifndef WIN32
//declare some temp vars:
fd_set myset;
struct timeval tv;
socklen_t lon;


// Set non-blocking:
int valopt;
long arg = fcntl(sockfd, F_GETFL, NULL);
arg |= O_NONBLOCK;
fcntl(sockfd, F_SETFL, arg);
#endif

// Try to connect:

dest_addr.sin_family = AF_INET; // host byte order
dest_addr.sin_port = htons(port); // short, network byte order
dest_addr.sin_addr = *((struct in_addr *)he->h_addr);
memset(dest_addr.sin_zero, '\0', sizeof dest_addr.sin_zero);
int res = connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(dest_addr));

#ifndef WIN32
if (res < 0) {
if (errno == EINPROGRESS) {
tv.tv_sec = m_contimeoutsec;
tv.tv_usec = m_contimeoutusec;
FD_ZERO(&myset);
FD_SET(sockfd, &myset);
if (select(sockfd+1, NULL, &myset, NULL, &tv) > 0) {
lon = sizeof(int);
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);
if (valopt)
{
status=2;
return false;
}
}
else {
status=2;
return false;
}
}
else {
status=2;
return false;
}
}


// Set to blocking mode again:
arg = fcntl(sockfd, F_GETFL, NULL);
arg &= (~O_NONBLOCK);
fcntl(sockfd, F_SETFL, arg);

#else
if(res==-1) //TODO: Find a way to setup non blocking sockets in win32 so it does not try to connect forever
{
status=2;
return false;
}
#endif


status=1;

return true;
}

Richard.J
March 30th, 2009, 09:43 AM
I think Windows return WSAEWOULDBLOCK on a non-blocking socket.
And you might want to use setsockopt to set the socket to nonblocking, that looks more intuitive to me.

BTW, connect does not try forever. The 'normal' timeout would be about 20 seconds, but I have seen longer times, but definitely not forever.


HTH,
Richard

jim enright
March 31st, 2009, 11:52 AM
dont know if this helps - i have been using CAsyncSocket as both a client and a server to get into sockets. i stubbed my toe trying to connect and WSAEWOULDBLOCK was returned indicating an attempt to a non-blocking socket failed because there was no immediate connection.

i use the following code to continue to attempt to connect and it works now. strangely (in my mind) the second attempt would also fail because now the
socket complains that the sonnection already exists.

i use the following code to set up a series of attempts to connect and have not had a failure yet the basic idea is to keep tyring to connect and get the last error if the return form the connect attempt is 0 BUT determine the connection has been made if the GetLasError() fn sas the conneciton exists:


ErrorCode = SocketToSend.Create(); // no parms because it is a client
if (ErrorCode == 0){
MessageBox(
_T("Faliure on SocketToSend.Create()"),
Mode,
MB_OK
);
}
else{
// use it to listen
bool FinallyGotAConnection = FALSE;
bool StopThis = FALSE;
int i = 0;

while (StopThis == FALSE){
ErrorCode = SocketToSend.Connect(LPCTSTR(Server),Port);
if (ErrorCode != 0){
FinallyGotAConnection = TRUE;
StopThis = TRUE;
}
else{
LastError = GetLastError();
if (LastError == WSAEISCONN){
FinallyGotAConnection = TRUE;
StopThis = TRUE;
}
}

i++;
if (i > 100){
StopThis = TRUE;
}
}

if (FinallyGotAConnection == FALSE){
s = GetErrorText(_T("connect"));
MessageBox(
_T("SocketToSend.Connect(LPCTSTR(Server),Port) fail - error ") + s,
Mode,
MB_OK
);
}
else{
MessageBox(
_T("SocketToSend.Connect(LPCTSTR(Server),Port) completed"),
Mode,
MB_OK
);
}
}

Richard.J
March 31st, 2009, 02:34 PM
jim, you did not understand what I tried to explain. As Red Squirrel and you expirienced, calling connect on a non-blocking socket will lead to an error. This is per definition, as a real connection might take some seconds. The error code connect() returns simply indicates this. Under the hood the TCP stack still performs its task to try and establish the desired connection.
Now, to get an indication when the connection really is available, you could use select() on the low level API or wait for the OnConnect() event on the CAsyncSocket API. Both ways are equivalent because the CAsyncSocket makes use of the lower level Windows/BSD socket API.

The approach Red Squirrel took is correct, except that he/she uses the wrong error code on Windows.

HTH,
Richard

jim enright
March 31st, 2009, 04:01 PM
how does one wait for the onconnect event?

Richard.J
April 1st, 2009, 09:11 AM
Assuming you are using CAsyncSocket as you stated in that other thread of yours, derive your class from CAsyncSocket and overwrite the virtual OnConnect() method. You might want to read the docs on MSDN on this topic (http://msdn.microsoft.com/en-us/library/3d46645f(VS.80).aspx)