I read from socket using recv function. I have problem when no data available for reading. My programm just stops. I found that I can set timeout using select function. But looks that timeout affects select function itself and recv that goes after select still waits uncontinuously.
fd_set set;
struct timeval timeout;
FD_ZERO(&set); /* clear the set */
FD_SET(s, &set); /* add our file descriptor to the set */
timeout.tv_sec = SOCKET_READ_TIMEOUT_SEC;
timeout.tv_usec = 0;
int rv = select(s, &set, NULL, NULL, &timeout);
if((recv_size = recv(s , rx_tmp , bufSize ,0)) == SOCKET_ERROR)
{
...
}
How to ask recv function return after some timout?
Another way to set a timeout on recv() itself without using select() is to use setsockopt() to set the socket's SO_RCVTIMEO option (on platforms that support it).
On Windows, the code would look like this:
DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000;
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
//...
recv_size = recv(s, rx_tmp, bufSize, 0);
if (recv_size == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAETIMEDOUT)
//...
}
On other platforms, the code would look like this instead:
struct timeval timeout;
timeout.tv_sec = SOCKET_READ_TIMEOUT_SEC;
timeout.tv_usec = 0;
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
//...
recv_size = recv(s, rx_tmp, bufSize, 0);
if (recv_size == -1)
{
if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
//...
}
You should check return value of select. select will return 0 in case timeout expired, so you should check for error and call recv only if select returned positive value:
On success, select() and pselect() return the number of file descriptors contained in the three returned descriptor sets (that is, the total number of bits that are set in readfds, writefds, exceptfds) which may be zero if the timeout expires before anything interesting happens.
int rv = select(s + 1, &set, NULL, NULL, &timeout);
if (rv == SOCKET_ERROR)
{
// select error...
}
else if (rv == 0)
{
// timeout, socket does not have anything to read
}
else
{
// socket has something to read
recv_size = recv(s, rx_tmp, bufSize, 0);
if (recv_size == SOCKET_ERROR)
{
// read failed...
}
else if (recv_size == 0)
{
// peer disconnected...
}
else
{
// read successful...
}
}
use the FD_ISSET() macro to test whether there is data to read. If it returns false, don't do the read.
http://linux.die.net/man/3/fd_set
Related
I am implementing a client-server TCP socket application. Client is on an OpenWRT Linux router (C based) and writes some data on the socket repeatedly and in a loop at some frequency rate. The Server is on a Linux Ubuntu machine (C/C++ based) and reads data in a loop according to data arrival speed.
Problem: Running the Server and then Client, server keeps reading new data. Both sides work well until the number of data deliveries (# of connections) reaches 1013. After that, the Client stuck at socket(AF_INET,SOCK_STREAM,0) with socket creation failed...: Too many open files. Apparently, the number of open fd approaches ulimit -n = 1024 on client.
I put the snippets of the code which shows the loop structures for Server.cpp and Client.c:
Server.c:
// TCP Socket creation stuff over here (work as they should):
// int sock_ = socket() / bind() / listen()
while (1)
{
socklen_t sizeOfserv_addr = sizeof(serv_addr_);
fd_set set;
struct timeval timeout;
int connfd_;
FD_ZERO(&set);
FD_SET(sock_, &set);
timeout.tv_sec = 10;
timeout.tv_usec = 0;
int rv_ = select(sock_ + 1, &set, NULL, NULL, &timeout);
if(rv_ == -1){
perror("select");
return 1;
}
else if(rv_ == 0){
printf("Client disconnected.."); /* a timeout occured */
close (connfd_);
close (sock_);
}
else{
connfd_ = accept (sock_,(struct sockaddr*)&serv_addr_,(socklen_t*)&sizeOfserv_addr);
if (connfd_ >= 0) {
int ret = read (connfd_, &payload, sizeof(payload)); /* some payload */
if (ret > 0)
printf("Received %d bytes !\n", ret);
close (connfd_); /* Keep parent socket open (sock_) */
}else{
printf("Server acccept failed..\n");
close (connfd_);
close (stcp.sock_);
return 0;
}
}
}
Client.cpp:
while (payload_exist) /* assuming payload_exist is true */
{
struct sockaddr_in servaddr;
int sock;
if (sock = socket(AF_INET, SOCK_STREAM, 0) == -1)
perror("socket creation failed...\n");
int one = 1;
int idletime = 2;
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &idletime, sizeof(idletime));
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr("192.168.100.12");
servaddr.sin_port = htons(PORT); /* some PORT */
if (connect (sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0){
perror("connect failed...");
return 1;
}
write(sock, (struct sockaddr*)&payload, sizeof(payload)); /* some new payload */
shutdown(sock,SHUT_WR);
bool serverOff = false;
while (!serverOff){
if(read(sock, &res, sizeof(res)) < 0){
serverOff = true;
close(sock);
}
}
}
NOTE: payload is 800 bytes and always gets fully transmitted per one write action. Having both codes defined under int main(), the client keeps creating sockets and sending data, on the other side, server receives all and would automatically close() and leave if client terminates, due to using select(). If I don't terminate the Client, however, by checking some print logs, it is evident that Server successfully receives 1013 payloads before client crashes with socket creation failed...: Too many open files.
Update:
Following the point mentioned by Steffen Ullrich, it turned out that, the client socket fd has no leak, and the existence of a second fd in the original loop (which was left open) was making the ulimit exceed the limit.
if(read(sock, &res, sizeof(res)) < 0){
serverOff = true;
close(sock); /********* Not actually closing sock *********/
}
Your check for end of connection is wrong.
read returns 0 if the other side has shut down the connection and <0 only on error.
if (sock = socket(AF_INET, SOCK_STREAM, 0) == -1)
perror("socket creation failed...\n");
Given the precedence of operators in C this basically says:
sock = ( socket(AF_INET, SOCK_STREAM, 0) == -1) )
if (sock) ...
Assuming that socket(...) will not return an error but a file descriptor (i.e. >=0) the comparison will be false and thus this essentially says sock = 0 while leaking a file descriptor if the fd returned by socket was >0.
I can see clearly that recvbuf has all the data I was expecting yet select() keeps returning 1.
Right now it's stuck in the limbo of else if (iBuffer == 0) {}.
SOCKET m_ConnectSocket;
/* The socket setup is done elsewhere but just adding this for clarity
This socket is responsible for sending from the client to the server
and also receives anything the server sends back.
This socket is doing the connect() & initial send()
*/
fd_set set;
struct timeval timeout;
// Set up the file descriptor set.
FD_ZERO(&set);
FD_SET(m_ConnectSocket, &set);
// Set up the struct timeval for the timeout.
timeout.tv_sec = RECV_DELAY_SEC;
timeout.tv_usec = RECV_DELAY_USEC;
int iBuffer = 0;
do
{
iResult = select(m_ConnectSocket, &set, NULL, NULL, &timeout);
if (iResult > 0)
{
iBuffer = recv(m_ConnectSocket, recvbuf, DEFAULT_BUFLEN, 0);
if (iBuffer > 0)
{
string sRecv(recvbuf);
STrace = String::Format("Bytes Received: {0}", iBuffer);
Trace(STrace, TRACE_INFO);
STrace = String::Format("Data Received: [{0}]", gcnew String(sRecv.c_str()));
Trace(STrace, TRACE_INFO);
}
else if (iBuffer == 0)
{
STrace = String::Format("iBuffer empty");
Trace(STrace, TRACE_INFO);
}
else
{
STrace = String::Format("recv failed: {0}", WSAGetLastError());
Trace(STrace, TRACE_ERROR);
}
}
else if (iResult == 0)
{
STrace = String::Format("No data left in buffer");
Trace(STrace, TRACE_INFO);
pMessage->Data(recvbuf);
if (iSentType != pMessage->Type())
{
STrace = String::Format("Message type mismatch: {0} | Expected: {1}", (int)pMessage->Type(), (int)iSentType);
Trace(STrace, TRACE_WARNING);
}
}
else if (iResult == -1)
{
STrace = String::Format("select() error");
Trace(STrace, TRACE_ERROR);
}
} while (iResult > 0);
All the answers so far are correct in what they say about resetting the FD sets, but none of them has actually identified the underlying problem.
If recv() returns zero it means the peer has disconnected, and you must close the socket. If you don't, as you aren't, you will continue to select the socket as readable and continue to receive zero.
It does not mean 'buffer empty'.
As select has it parameters passed as pointer and those data structures get altered by select put
fd_set set;
struct timeval timeout;
// Set up the file descriptor set.
FD_ZERO(&set);
FD_SET(m_ConnectSocket, &set);
// Set up the struct timeval for the timeout.
timeout.tv_sec = RECV_DELAY_SEC;
timeout.tv_usec = RECV_DELAY_USEC;
Just before the select statement i.e. within the loop.
You are misusing select(). Please read the documentation:
Parameters
nfds [in]
Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.
...
Upon return, the structures are updated to reflect the subset of these sockets that meet the specified condition.
So you must reset the fd_set structure every time you call select() in your loop.
Also, it looks like when select() times out, you are trying to parse whatever was received, but you are only parsing the last buffer that was returned by the last successful recv(), if any. In case recv() has to be called multiple times before the data times out, you need to collect each returned buffer and then parse them all together as a whole.
Also, your error handling in general could use some improvement, too.
Try something more like this instead:
fd_set set;
struct timeval timeout;
string sBuffer;
int iBuffer;
do
{
// Set up the file descriptor set.
FD_ZERO(&set);
FD_SET(m_ConnectSocket, &set);
// Set up the struct timeval for the timeout.
timeout.tv_sec = RECV_DELAY_SEC;
timeout.tv_usec = RECV_DELAY_USEC;
iResult = select(0, &set, NULL, NULL, &timeout);
if (iResult > 0)
{
iBuffer = recv(m_ConnectSocket, recvbuf, DEFAULT_BUFLEN, 0);
if (iBuffer > 0)
{
string sRecv(recvbuf, iBuffer);
STrace = String::Format("Bytes Received: {0}", iBuffer);
Trace(STrace, TRACE_INFO);
STrace = String::Format("Data Received: [{0}]", gcnew String(sRecv.c_str()));
Trace(STrace, TRACE_INFO);
sBuffer += sRecv;
}
else
{
if (iBuffer == 0)
{
STrace = String::Format("Connection closed");
Trace(STrace, TRACE_INFO);
}
else
{
STrace = String::Format("recv failed: {0}", WSAGetLastError());
Trace(STrace, TRACE_ERROR);
}
break;
}
}
else if (iResult == 0)
{
STrace = String::Format("No data left in buffer");
Trace(STrace, TRACE_INFO);
pMessage->Data(sBuffer.c_str());
if (iSentType != pMessage->Type())
{
STrace = String::Format("Message type mismatch: {0} | Expected: {1}", (int)pMessage->Type(), (int)iSentType);
Trace(STrace, TRACE_WARNING);
}
}
else
{
STrace = String::Format("select failed: {0}", WSAGetLastError());
Trace(STrace, TRACE_ERROR);
}
}
while (iResult > 0);
Trying to write a client which will try to receive data till 3 seconds. I have implemented the connect method using select by below code.
//socket creation
m_hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
m_stAddress.sin_family = AF_INET;
m_stAddress.sin_addr.S_un.S_addr = inet_addr(pchIP);
m_stAddress.sin_port = htons(iPort);
m_stTimeout.tv_sec = SOCK_TIMEOUT_SECONDS;
m_stTimeout.tv_usec = 0;
//connecting to server
long iMode = 1;
int iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
connect(m_hSocket, (struct sockaddr *)&m_stAddress, sizeof(m_stAddress));
long iMode = 0;
iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
fd_set stWrite;
FD_ZERO(&stWrite);
FD_SET(m_hSocket, &stWrite);
iResult = select(0, NULL, &stWrite, NULL, &m_stTimeout);
if((iResult > 0) && (FD_ISSET(m_hSocket, &stWrite)))
return true;
But I cannot figure out what I am missing at receiving timeout with below code? It doesn't wait if the server connection got disconnected. It just returns instantly from select method.
Also how can I write a non blocking socket call with timeout for socket send.
long iMode = 1;
int iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
fd_set stRead;
FD_ZERO(&stRead);
FD_SET(m_hSocket, &stRead);
int iRet = select(0, &stRead, NULL, NULL, &m_stTimeout);
if ((iRet > 0) && (FD_ISSET(m_hSocket, &stRead)))
{
while ((iBuffLen-1) > 0)
{
int iRcvLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
if (iRcvLen == SOCKET_ERROR)
{
return false;
}
else if (iRcvLen == 0)
{
break;
}
pchBuff += iRcvLen;
iBuffLen -= iRcvLen;
}
}
The first parameter to select should not be 0.
Correct usage of select can be found here :
http://developerweb.net/viewtopic.php?id=2933
the first parameter should be the max value of your socket +1 and take interrupted system calls into account if it is non blocking:
/* Call select() */
do {
FD_ZERO(&readset);
FD_SET(socket_fd, &readset);
result = select(socket_fd + 1, &readset, NULL, NULL, NULL);
} while (result == -1 && errno == EINTR);
This is just example code you probably need the timeout parameter as well.
If you can get EINTR this will complicate your required logic, because if you get EINTR you have to do the same call again, but with the remaining time to wait for.
I think for non blocking mode one needs to check the recv() failure along with a timeout value. That mean first select() will return whether the socket is ready to receive data or not. If yes it will go forward else it will sleep until timeout elapses on the select() method call line. But if the receive fails due to some uncertain situations while inside read loop there we need to manually check for socket error and maximum timeout value. If the socket error continues and timeout elapses we need to break it.
I'm done with my receive timeout logic with non blocking mode.
Please correct me if I am wrong.
bool bReturn = true;
SetNonBlockingMode(true);
//check whether the socket is ready to receive
fd_set stRead;
FD_ZERO(&stRead);
FD_SET(m_hSocket, &stRead);
int iRet = select(0, &stRead, NULL, NULL, &m_stTimeout);
DWORD dwStartTime = GetTickCount();
DWORD dwCurrentTime = 0;
//if socket is not ready this line will be hit after 3 sec timeout and go to the end
//if it is ready control will go inside the read loop and reads data until data ends or
//socket error is getting triggered continuously for more than 3 secs.
if ((iRet > 0) && (FD_ISSET(m_hSocket, &stRead)))
{
while ((iBuffLen-1) > 0)
{
int iRcvLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
dwCurrentTime = GetTickCount();
if ((iRcvLen == SOCKET_ERROR) && ((dwCurrentTime - dwStartTime) >= SOCK_TIMEOUT_SECONDS * 1000))
{
bReturn = false;
break;
}
else if (iRcvLen == 0)
{
break;
}
pchBuff += iRcvLen;
iBuffLen -= iRcvLen;
}
}
SetNonBlockingMode(false);
return bReturn;
I'm building an online game client and when I try to connect to an offline server, my client freezes so I wanted to use non blocking sockets which suits games since there are other tasks need to be done while connecting to the server.
While using non blocking sockets, the connect function always returns the same value regardless of the result, so people here recommended using the select function to find the result of the connection request.
(setting the non blocking socket before connection)
u_long iMode=1;
ioctlsocket(hSocket,FIONBIO,&iMode);
(setting the sockets sets)
FD_ZERO(&Write);
FD_ZERO(&Err);
FD_SET(hSocket, &Write);
FD_SET(hSocket, &Err);
TIMEVAL Timeout;
int TimeoutSec = 10; // timeout after 10 seconds
Timeout.tv_sec = TimeoutSec;
Timeout.tv_usec = 0;
int iResult = select(0, //ignored
NULL, //read
&(client.Write), //Write Check
&(client.Err), //Error Check
&Timeout);
if(iResult)
{
}
else
{
message_login("Error","Can't connect to the server");
}
The select function always returns -1, why?
When select() returns -1 (SOCKET_ERROR), use WSAGetLastError() to find out why it failed.
If the socket is in the Err set when select() exits, use getsockopt(SOL_SOCKET, SO_ERROR) to retrieve the socket error code that tells you why connect() failed.
if(iResult) evaluates as true for any non-zero value, including -1. You need to use if(iResult > 0) instead, as iResult will report the number of sockets that are signaled in any fd_set, 0 on timeout, and -1 on failure.
Try something more like this instead:
u_long iMode = 1;
if (ioctlsocket(hSocket, FIONBIO, &iMode) == SOCKET_ERROR)
{
int errCode = WSAGetLastError();
// use errCode as needed...
message_login("Error", "Can't set socket to non-blocking, error: ..."); // however you supply a variable value to your message...
}
if (connect(client.hSocket, ...) == SOCKET_ERROR)
{
int errCode = WSAGetLastError();
if (errCode != WSAEWOULDBLOCK)
{
// use errCode as needed...
message_login("Error", "Can't connect to the server, error: ..."); // however you supply a variable value...
}
else
{
// only in this condition can you now use select() to wait for connect() to finish...
}
}
TIMEVAL Timeout;
int TimeoutSec = 10; // timeout after 10 seconds
Timeout.tv_sec = TimeoutSec;
Timeout.tv_usec = 0;
int iResult = select(0, //ignored
NULL, //read
&(client.Write), //Write Check
&(client.Err), //Error Check
&Timeout);
if (iResult > 0)
{
if (FD_ISSET(client.hSocket, &(client.Err)))
{
DWORD errCode = 0;
int len = sizeof(errCode);
if (getsockopt(client.hSocket, SOL_SOCKET, SO_ERROR, (char*)&errCode, &len) == 0)
{
// use errCode as needed...
message_login("Error", "Can't connect to the server, error: ..."); // however you supply a variable value to your message...
}
else
message_login("Error", "Can't connect to the server, unknown reason");
}
else
message_login("Success", "Connected to the server");
}
else if (iResult == 0)
{
message_login("Error", "Timeout connecting to the server");
}
else
{
int errCode = WSAGetLastError();
// use errCode as needed...
message_login("Error", "Can't connect to the server, error: ..."); // however you supply a variable value to your message...
}
I created a regular C socket. Upon connect, it returns EWOULDBLOCK/WSAEWOULDBLOCK as expected because I did:
unsigned long int mode = 0;
ioctlsocket(ssl_info->sock, FIONBIO, &mode);
setsockopt(ssl_info->sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv));
setsockopt(ssl_info->sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv));
to put the socket in non-blocking mode. After that I do:
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
return SSL_connect(ssl);
However, it returns -1.
I read online that it means I need to handle SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE.
so I did:
int res = -1;
while(res == -1)
{
res = SSL_connect(ssl);
switch (SSL_get_error(ssl, res))
{
case SSL_ERROR_WANT_CONNECT:
MessageBox(NULL, "Connect Error", "", 0);
break;
case SSL_ERROR_WANT_READ: //prints this every time..
MessageBox(NULL, "Read Error", "", 0);
break;
case SSL_ERROR_WANT_WRITE:
MessageBox(NULL, "Write Error", "", 0);
break;
}
SelectSocket(ssl);
}
std::cout<<"Connected!\n";
Where SelectSocket is defined as:
bool SelectSocket(SSL* ssl)
{
if (blockmode)
{
fd_set readfds;
fd_set writefds;
FD_ZERO(&readfds);
FD_ZERO (&writefds);
FD_SET(ssl_info->sock, &readfds);
FD_SET(ssl_info->sock, &writefds);
struct timeval tv = {0};
tv.tv_sec = timeout / 1000;
tv.tv_usec = timeout % 1000;
return select(sock + 1, &readfds, &writefds, NULL, &tv) >= 0;
}
return select(sock + 1, NULL, NULL, NULL, NULL) != SOCKET_ERROR;
}
So how exactly can I get it to connect? I can't seem to be able to read or write anything when the socket is non-blocking :S.
Any ideas?
The (-1) returned by SSL_connect() indicates that the underlying BIO could not satisfy the needs of SSL_connect() to continue the handshake.
Generally, the calling process then must repeat the call after taking appropriate action to satisfy the needs of SSL_connect().
However, when using a non-blocking socket, nothing is to be done; but select() can be used to check for the required condition.
(When using a buffering BIO, like a BIO pair, data must be written into or retrieved out of the BIO before being able to continue.)
Your code actually disables non-blocking I/O. As you are passing 0 as argument value for FIONBIO to ioctlsocket, which is documented as:
FIONBIO
The *argp parameter is a pointer to an unsigned long value. Set *argp to a nonzero value if the nonblocking mode should be enabled, or zero if the nonblocking mode should be disabled. [..]
https://msdn.microsoft.com/en-us/library/windows/desktop/ms738573%28v=vs.85%29.aspx