Link to home
Start Free TrialLog in
Avatar of ehensens
ehensensFlag for United States of America

asked on

C++: How to stop socket() from failing

Hello,

I have an iPhone app that runs a c++ class I wrote that HTTP posts to a server I wrote. I let the user have control over what IP address to post to. If the IP address is correct, the app runs fine and gets the information back from the server. Here's where it gets tricky:

If the IP address is not on the network, an error is flagged and a user error message comes up. If the user then changes the IP address to the correct one, the app keeps working fine, no hitches.

On the error screen, the app is constantly (6 times/second) retrying the IP to see if it can connect. If the user sits on the error screen for too long, then when he changes it to the correct IP, the socket() command will still fail.

I'm thinking that maybe because it's running socket() six times/second, maybe it's running it too many times and it's running out of file descriptors? http://www.opengroup.org/onlinepubs/000095399/functions/socket.html says that this is one possible way for it to fail.

Anyone have any ideas for how to stop it from failing? Of course, please let me know if I can clear this up in any way. Thank you for your help!
struct hostent *hostentPointer
struct sockaddr_in serverAddress;
char **pPointer;
char networkAddress[MAX_SIZE];
 
hostentPointer = gethostbyname(host);
pPointer = hostentPointer -> h_addr_list;
int sockFD = socket(AF_INET, SOCK_STREAM, 0);
if (sockFD == -1)
     return 1;
 
etc.

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of mrjoltcola
mrjoltcola
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of ehensens

ASKER

Hi mrjoltcola,

Thank you for your response. How exactly do I "close that descriptor"?
with close()
I would not keep opening and closing, though, you can reuse the descriptor to connect() with until it succeeds.
The weird thing is, i AM close() ing. I am going to try to reuse the descriptor and then if that doesn't work I will post a more complete code sample.
Then that may not be your root problem at all.
I see... well, here's a more complete code sample, this function is called 6 times a second



struct hostent *hostentPointer
struct sockaddr_in serverAddress;
char **pPointer;
char networkAddress[MAX_SIZE];
 
hostentPointer = gethostbyname(host);
pPointer = hostentPointer -> h_addr_list;
int sockFD = socket(AF_INET, SOCK_STREAM, 0);
if (sockFD == -1)
     return 1;
 
bzero(&serverAddress, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(port);
inet_pton(AF_INET, network address, &serverAddress.sin_addr);
 
fcntl(sockFD, F_SETL, O_NONBLOCK);
 
fd_set sockSet;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 300;
FD_SET(sockFD, &sockSet);
 
int connectReturn;
int badReturns = 0;
 
while (connectReturn != 0)
{
   connectReturn = connect(sockFD, (SOCKA *) & serverAddress, sizeof(serverAddress));
   badReturns++;
   if (badReturns > 1000)
      return 1;
}
 
select(5, &sockSet, &sockSet, NULL, &timeout);
 
const void *optVal;
socklen_t *optLen;
getsockopt(sockFD, SOL_SOCKET, SO_ERROR, &optVal, optLen);
 
fcntl(sockFD, F_SETL, 0);
 
ssize_t responseReturn;
char sendRequest[MAX_SIZE];
 
snprintf(sendRequest, BUFFSIZE, "POST %s HTTP/1.0\r\nHost: %s\r\nContent-type: text/plain\r\nContent-length: %d\r\n\r\n%s", directory, host, strlen(payload), payload);
 
write(sockFD, sendRequest, strlen(sendRequest));
 
while ((responseReturn = read(sockFD, receiveResponse, SENDSIZE)) > 0)
   receiveResponse[responseReturn] = '\0';
 
close(sockFD);
 
return 0;

Open in new window

Avatar of jkr
>>On the error screen, the app is constantly (6 times/second) retrying the IP to
>>see if it can connect

I am sorry, but that is not a really good design. If you already know that the connection to that address does not work (that's why you are displaying an error screen, isn't it?), it is futile to try to reconnect. I suspect that the new address information isn't communicated correctly to that code in question. Also, what does 'errno' report when the connection fails?
Hi jkr,

I have it like that in case the sever doesn't come on until after the client app does. That way it's constantly polling that IP address. Can you get any clues as to my problem from the code I just posted?

>> I suspect that the new address information isn't communicated correctly to that code in question

Well it has to be communicated correctly, because if you change the IP to a correct address within a minute or so of being on the error screen, it works great. But if you wait too long on the error screen then socket() fails.
jkr indirectly brings up another good point. Too many failed connects like that generates a lot of network SYN packets, and could cause a firewall or intrusion control to block your IP, because to a dump sniffer, it might look like a scanner.
>>Well it has to be communicated correctly

OK, then the value of 'errno' would be interesting in the case that it still fails.

But, again, as a side note: 6 times a second is a bit too much. How fast can you type in a  new address?
I meant "dumb" sniffer. Some firewalls and intrusion software is not too smart about what it thinks is a scanner, and I'm mostly talking, in this case, to a corporate firewall where the user is on the inside.
The information needs to be updated 6 times a second because the info being served up can change quite often.
The errno is "too many open files"
>>>> while (connectReturn != 0)
>>>> {
>>>>    connectReturn = connect(sockFD, (SOCKA *) & serverAddress, sizeof(serverAddress));
>>>>    badReturns++;
>>>>    if (badReturns > 1000)
>>>>       return 1;
>>>> }

You should add at least a sleep into that while loop.  

As the socket is non-blocking the while loop would return SOCKET_ERROR (== -1) for a lot of these connect calls and your while loop would catch all CPU of at least one of your cores ...

Didn't I show you a much less brute force method for checking one single connect call on being successful by using select with timeout on a set of writeable sockets?
I thought I did that on line 36
I thought I was closing the socket because there was a close() at the bottom of the code, but the return statements didn't close the socket before they "return()"d. So, the close() was never getting called. I just added a close() before each return and it works great.
It's fixed - please see the accepted solution comments.
>>>> select(5, &sockSet, &sockSet, NULL, &timeout);

Indeed. Though you may not use the same socket set for both reading and writing. I also would use sockFD as first argument and not 5.

But what purpose has the while loop with the connect call? You need one single call and it necessarily fails with socket error and errno = E_WOULDBLOCK  cause the socket is non-blocking. Then call select and it either wil timeout (than the IP is wrong or the user plugged of the cable or there is no wireless connection) or succeed.
>>>> select(5, &sockSet, &sockSet, NULL, &timeout);

 select(sockFD, NULL, &sockSet, NULL, &timeout);
This seems to work nicely, itsmeandnobodyelse, thank you.