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

ehensensAsked:
Who is Participating?
 
mrjoltcolaConnect With a Mentor Commented:
Your code sample isn't complete enough to tell, but surely, if you are calling the socket() function, it allocates a descriptor.

Regardless of whether your connect() later succeeds or not, you still need to close that descriptor on your side.

So, yes, you could be running out of descriptors.

You don't need to repeatedly call socket(), once you get one, then you can try repeatedly to connect() with that socket.
0
 
ehensensAuthor Commented:
Hi mrjoltcola,

Thank you for your response. How exactly do I "close that descriptor"?
0
 
mrjoltcolaCommented:
with close()
0
Improve Your Query Performance Tuning

In this FREE six-day email course, you'll learn from Janis Griffin, Database Performance Evangelist. She'll teach 12 steps that you can use to optimize your queries as much as possible and see measurable results in your work. Get started today!

 
mrjoltcolaCommented:
I would not keep opening and closing, though, you can reuse the descriptor to connect() with until it succeeds.
0
 
ehensensAuthor Commented:
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.
0
 
mrjoltcolaCommented:
Then that may not be your root problem at all.
0
 
ehensensAuthor Commented:
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

0
 
jkrCommented:
>>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?
0
 
ehensensAuthor Commented:
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.
0
 
mrjoltcolaCommented:
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.
0
 
jkrCommented:
>>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?
0
 
mrjoltcolaCommented:
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.
0
 
ehensensAuthor Commented:
The information needs to be updated 6 times a second because the info being served up can change quite often.
0
 
ehensensAuthor Commented:
The errno is "too many open files"
0
 
itsmeandnobodyelseCommented:
>>>> 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?
0
 
ehensensAuthor Commented:
I thought I did that on line 36
0
 
ehensensAuthor Commented:
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.
0
 
ehensensAuthor Commented:
It's fixed - please see the accepted solution comments.
0
 
itsmeandnobodyelseCommented:
>>>> 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.
0
 
itsmeandnobodyelseCommented:
>>>> select(5, &sockSet, &sockSet, NULL, &timeout);

 select(sockFD, NULL, &sockSet, NULL, &timeout);
0
 
ehensensAuthor Commented:
This seems to work nicely, itsmeandnobodyelse, thank you.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.