Non-blocking socket loses data on Windows - c++

I have a non-blocking socket server which supports all connecting clients. It's using multi-threading and it's cross-compilable using GCC.
It works perfect (as I want) in Linux, but when I try to use it in Windows, when I send a 70MB of file through it, it loses around 20MB from the file.
All sockets are non-blocking, so for recv/send socket calls, I don't have check/stop. It's in a loop and it sends what it receive, it sort of acts as a Echo server, but it loses data in Windows. I'm using Winsock 2.2 in WSAStartup.
What is wrong? How can I have wait/flush send calls, but never block recv calls? (if this is the issue)
Code pieces:
How I make it non-blocking:
iMode = 1;
ioctlsocket(sock1,FIONBIO, &iMode);
ioctlsocket(sock2,FIONBIO, &iMode);
How I send/receive between two sockets:
for (;;)
{
memset(buffer, 0, 8192);
int count = recv(sock1, buffer, sizeof(buffer), 0);
receiveResult = WSAGetLastError();
if (receiveResult == WSAEWOULDBLOCK && count <= 0)
continue;
if (count <= 0)
{
closesocket(sock1);
closesocket(sock2);
return;
}
if (count > 0)
{
int retval = send(sock2, buffer, count, 0);
}
}

int count = recv(sock1, buffer, sizeof(buffer), 0);
receiveResult = WSAGetLastError();
if (receiveResult == WSAEWOULDBLOCK && count <= 0)
When calling recv() or send(), WSAGetLastError() will return a meaningful value only if -1 (SOCKET_ERROR) is returned, but you are also checking it when 0 is returned instead. They do not set an error code for WSAGetLastError() when returning >= 0. You need to separate those conditions.
Also, just because you have read X number of bytes does not guarantee that you will be able to send X number of bytes at one time, so you need to check send() for WSAEWOULDBLOCK until you have no more data to send.
Try something more like this:
bool keepLooping = true;
do
{
int count = recv(sock1, buffer, sizeof(buffer), 0);
if (count > 0)
{
// data received...
char *p = buffer;
do
{
int retval = send(sock2, p, count, 0);
if (retval > 0)
{
p += retval;
count -= retval;
}
else if (retval == 0)
{
// peer disconnected...
keepLooping = false;
}
else if (WSAGetLastError() != WSAEWOULDBLOCK)
{
// a real error occurred...
keepLooping = false;
}
else
{
// peer is not ready to receive...
// optionally use select() to wait here until it is...
}
}
while ((count > 0) && (keepLooping));
}
else if (count == 0)
{
// peer disconnected...
keepLooping = false;
}
else if (WSAGetLastError() != WSAEWOULDBLOCK)
{
// a real error occurred...
keepLooping = false;
}
else
{
// no data is available for reading...
// optionally use select() to wait here until it is...
}
}
while (keepLooping);
closesocket(sock1);
closesocket(sock2);
return;

Related

File descriptor returned from socket is larger than FD_SETSIZE

I have a problem where the returned file descriptor would gradually increase to be a number larger than FD_SETSIZE.
My tcp server is continually shutdown which requires my client to close the socket and reconnect. The client will then attempt to reconnect to the server by calling socket to obtain a new file descriptor before calling connect.
However it appears that everytime I call socket the file descriptor returned is incremented and after a certain amount of time it becomes larger than FD_SETSIZE, which is a problem where I use select to monitor the socket.
Is it ok to reuse the first file descriptor returned from socket for the connect call even though the the socket was closed? Or is there other workarounds?
Reconnect code (looping until connected):
int s = getaddrinfo(hostname, port, &hints, &result);
if (s != 0) { ... HANDLE ERROR ...}
...
struct addrinfo *rp;
int sfd;
for (rp = result; rp != NULL; rp -> ai_protocol)
{
sfd = socket( rp->ai_family, rp->ai_sockettype, rp->ai_addrlen);
if (sfd >= 0)
{
int res = connect(sfd, rp->ai_addr, rp->ai_addrlen);
if (res != -1)
{
_sockFd = sfd;
_connected = true;
break;
}
else
{
close (sfd);
break;
}
}
}
if (result != NULL)
{
free(result);
}
Read Message code:
if (_connected)
{
...
retval = select(n, &rec, NULL, NULL, &timeout);
if (retval == -1)
{
...
_connected = false;
close(_sockFd);
}
else if (retval)
{
if (FD_ISSET(_sockFD, &rec) == 0)
{
....
return;
}
int count = read(...)
if (count)
{
....
return;
}
else
{
....
_connected = false;
close(_sockFd);
}
}
}
You're not closing the socket if the connect fails. So it remains open, occupying an FD, so next time you call socket() you get a new FD. You're also not breaking out of your loop when connect() succeeds, which is another leak. You're also not checking the result of read() for -1.

Communication with Openssl epoll server to multiple clients simultaneously in C/C++

I have an SSL server (code listed below) connecting to multiple SSL client. I am using a single context and the initialization is
SSL_CTX *ctx;
SSL *ssl[MAXEVENTS];
SSL_library_init();
ctx = InitServerCTX(); // initialize SSL
...
...
Then I have the following piece of code
ssl[i] = SSL_new(ctx); // get new SSL state with context
SSL_set_fd(ssl[i], infd); // set connection socket to SSL state
Then I perform SSL_accept(ssl[i]).
All this is being performed using epoll (edge trigger mode). I have modified the example in https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/ to use SSL by referring to https://www.cs.utah.edu/~swalton/listings/articles/ssl_server.c as a reference
The logic for this is
events = new epoll_event[MAXEVENTS * sizeof event];
// The event loop
while (true)
{
int n, i;
n = epoll_wait (efd, events, MAXEVENTS, -1);
for (i = 0; i < n; i++)
{
if ((events[i].events & EPOLLERR) ||
(events[i].events & EPOLLHUP) ||
(!(events[i].events & EPOLLIN)))
{
// An error has occured on this fd, or the socket is not
// ready for reading (why were we notified then?)
fprintf (stderr, "epoll error\n");
close (events[i].data.fd);
continue;
} else if (sfd == events[i].data.fd) {
// We have a notification on the listening socket, which
// means one or more incoming connections.
while (1)
{
struct sockaddr in_addr;
socklen_t in_len;
int infd;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
in_len = sizeof in_addr;
infd = accept (sfd, &in_addr, &in_len);
if (infd == -1)
{
if ((errno == EAGAIN) ||(errno == EWOULDBLOCK)) {
// We have processed all incoming
// connections.
break;
} else {
perror ("accept");
break;
}
}
s = getnameinfo (&in_addr, in_len,
hbuf, sizeof hbuf,
sbuf, sizeof sbuf,
NI_NUMERICHOST | NI_NUMERICSERV);
if (s == 0) {
printf("Accepted connection on descriptor %d "
"(host=%s, port=%s)\n", infd, hbuf, sbuf);
}
ssl[i] = SSL_new(ctx); // get new SSL state with context
SSL_set_fd(ssl[i], infd); // set connection socket to SSL state
int ret;
if ( (ret=SSL_accept(ssl[i])) == FAIL ) { // do SSL-protocol accept
ERR_print_errors_fp(stderr);
printf("Performing exchange Error 1.\n");
int error = SSL_get_error(ssl[i], 0);
//TODO A retry timer or retry counter. Cannot keep retrying perpetually.
if (ret <=0 && (error == SSL_ERROR_WANT_READ)) {
//Need to wait until socket is readable. Take action?
//LOG the reason here
perror ("Need to wait until socket is readable.");
} else if (ret <=0 && (error == SSL_ERROR_WANT_WRITE)) {
//Need to wait until socket is writable. Take action?
//LOG the reason here
perror ("Need to wait until socket is writable.");
} else {
//LOG the reason here
perror ("Need to wait until socket is ready.");
}
shutdown (infd, 2);
SSL_free (ssl[i]);
continue;
}
// Make the incoming socket non-blocking and add it to the
// list of fds to monitor.
s = SocketNonBlocking (infd);
if (s == -1) {
abort ();
}
event.data.fd = infd;
event.events = EPOLLIN | EPOLLET | EPOLLHUP;
s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &event);
if (s == -1) {
perror ("epoll_ctl");
abort ();
}
}
continue;
Now,
while (1)
{
ssize_t count;
char buf[1024];
char reply[1024];
printf("Performing exchange.\n");
const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";
ShowCerts(ssl[i]); // get any certificates
count = SSL_read(ssl[i], buf, sizeof(buf)); // get request
int32_t ssl_error = SSL_get_error (ssl[i], count);
switch (ssl_error) {
case SSL_ERROR_NONE:
printf("SSL_ERROR_NONE\n");
break;
case SSL_ERROR_WANT_READ:
printf("SSL_ERROR_WANT_READ\n");
break;
case SSL_ERROR_WANT_WRITE:
printf("SSL_ERROR_WANT_WRITE\n");
break;
case SSL_ERROR_ZERO_RETURN:
printf("SSL_ERROR_ZERO_RETURN\n");
break;
default:
break;
}
if (( count > 0 ) )
{
buf[count] = 0;
printf("count > 0 Client msg: \"%s\"\n", buf);
sprintf(reply, HTMLecho, buf); // construct reply
SSL_write(ssl[i], reply, strlen(reply)); // send reply
} else if ((count < 0) ){
printf("count < 0 \n");
if (errno != EAGAIN)
{
printf("count < 0 errno != EAGAIN \n");
perror ("read");
done = 1;
}
break;
} else if (count==0){
ERR_print_errors_fp(stderr);
epoll_ctl(efd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
printf("count == 0 Client Disconnected.\n");
done = 1;
break;
}
}
if (done)
{
printf("Freeing data.\n");
int sd = SSL_get_fd(ssl[i]);
SSL_free(ssl[i]); // release SSL state
close(sd); // close connection
//close (events[i].data.fd);
}
}
This works fine for one server - one client. But the moment I try to connect two clients, the client that connected last is the only one that receives the data. The client that was connected before just keeps hanging without any activity.
UPDATE
I found that there is some indexing issue going on here. The value of the variable i from the epoll example is not corresponding to what I think it should be corresponding. I tried connecting two clients and I had thought initially the i should have incremented for second client but it is not the case. It still stays 0.
Ok I solved the issue. My problem stemmed from incorrect indexing. I was relying on the variable i that did not behave the way I expected. (see update in my question)
First I declare std::map<int,SSL*> sslPairMap;
Then I insert the successful fd and SSL accept into a std::map in C++. In C, one can use struct based pairing. There is one example here https://github.com/dCache/dcap/blob/b432bd322f0c1cf3e5c6a561845899eec3acad1e/plugins/ssl/sslTunnel.c
//(c) 2014 enthusiasticgeek for stack overflow
sslPairMap.insert(std::pair<int,SSL*>(infd, ssl));
// Make the incoming socket non-blocking and add it to the
// list of fds to monitor.
s = AibSocketNonBlocking (infd);
if (s == -1) {
abort ();
}
aibevent.data.fd = infd;
aibevent.events = EPOLLIN | EPOLLET | EPOLLHUP;
s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &aibevent);
if (s == -1) {
perror ("epoll_ctl");
abort ();
}
After this I simply retrieve the SSL* from the map which ensures I do not change index inadvertently. std::map saves the day
//(c) 2014 enthusiasticgeek for stack overflow
while (1)
{
ssize_t count;
char buf[1024];
char reply[1024];
printf("Performing exchange where i = %d.\n",i);
const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";
ShowCerts(sslPairMap[aibevents[i].data.fd]); // get any certificate
count = SSL_read(sslPairMap[aibevents[i].data.fd], buf, sizeof(buf)); // get request
ssl_error = SSL_get_error (sslPairMap[aibevents[i].data.fd], count);
switch (ssl_error) {
case SSL_ERROR_NONE:
printf("SSL_ERROR_NONE\n");
break;
case SSL_ERROR_WANT_READ:
printf("SSL_ERROR_WANT_READ\n");
break;
case SSL_ERROR_WANT_WRITE:
printf("SSL_ERROR_WANT_WRITE\n");
break;
case SSL_ERROR_ZERO_RETURN:
printf("SSL_ERROR_ZERO_RETURN\n");
break;
default:
break;
}
if (( count > 0 ) )
{
buf[count] = 0;
printf("count > 0 Client msg: \"%s\"\n", buf);
sprintf(reply, HTMLecho, buf); // construct reply
SSL_write(sslPairMap[aibevents[i].data.fd], reply, strlen(reply)); // send reply
break;
} else if ((count < 0) ){
printf("count < 0 \n");
if (errno != EAGAIN)
{
printf("count < 0 errno != EAGAIN \n");
perror ("read");
done = 1;
}
break;
} else if (count==0){
ERR_print_errors_fp(stderr);
epoll_ctl(efd, EPOLL_CTL_DEL, aibevents[i].data.fd, NULL);
printf("count == 0 Client Disconnected.\n");
done = 1;
break;
}
}
if (done)
{
printf("Freeing data.\n");
int sd = SSL_get_fd(sslPairMap[aibevents[i].data.fd]);
if(ssl_error == SSL_ERROR_NONE){
SSL_shutdown(sslPairMap[aibevents[i].data.fd]);
}
SSL_free(sslPairMap[aibevents[i].data.fd]); // release SSL state
close(sd); // close connection
//close (aibevents[i].data.fd);
erase_from_map(sslPairMap, aibevents[i].data.fd);
}
}
If anyone comes across this, another way to go about it is to store the SSL* pointer in the event data itself:
events[i].data.u64 = (long long)ssl;
And when you need to read/write from it:
auto ssl = (SSL*)events[i].data.u64;
SSL_read(ssl, someBuffer, sizeof(someBuffer));
For example.

c++ winsock - server communicates only with single client while it should communicate with every client

I am writing a single chat program with GUI. I wanted to write a server that would accept many clients. Every client can connect successfuly. But there is a strange problem with sending and receiving data. I use select() and a thread to handle many sockets at the same time. If a client sends some data to server, it will receive it and send it back to that client (the client is especially written without "prediction"). But the server won't send it further to another clients (like every client had its own private conversation with the server). Here's my code:
// this is rewritten from the Beej's tutorial with a little and insignificant changes
/* in the thread */
fd_set mainfd;
fd_set readfd;
// sin-size, newfd, maxfd - int
while(TRUE)
{
readfd = mainfd;
if(select(maxfd+1, &readfd, NULL, NULL, NULL) == -1)
{
MessageBoxA(NULL, "Error while trying to accept incoming connections (select)", "Error", 16);
itoa(GetLastError(), buf, 10);
MessageBoxA(NULL, buf, buf, 0);
break;
}
for(int i = 0; i <= maxfd; i++)
{
char* psr;
char srMsg[256];
if(FD_ISSET(i, &readfd))
{
if(i == mainSocket)
{
sin_size = sizeof(their_addr);
newfd = accept(mainSocket, (struct sockaddr*)&their_addr, &sin_size);
if(newfd == SOCKET_ERROR)
{
AddTextToEdit(hStaticChat, "* Error: couldn't accept incoming connection.", TRUE);
}
else
{
FD_SET(newfd, &mainfd);
if(newfd > maxfd)
{
maxfd = newfd;
}
}
}
else
{
len = recv(i, srMsg, 256, 0);
if(len == 0 || len == SOCKET_ERROR)
{
AddTextToEdit(hStaticChat, "* Client has disconnected", TRUE);
close(i);
FD_CLR(i, &mainfd);
}
else
{
AddTextToEdit(hStaticChat, srMsg, TRUE);
for(int j = 0; j <= maxfd; j++)
{
if(FD_ISSET(j, &readfd))
{
send(j, srMsg, len, 0);
}
}
}
}
}
}
}
You are only sending data to the clients whos fd is in readfd, that is, only to that one which just communicated to you. Try to test FD_ISSET(j, mainfd) instead.
This code is not valid under WinSock. Windows does not deal with sockets using integer file descriptors like other platforms do. Sockets are represented using actual kernel objects instead, so you can't use loop counters as socket handles and such. There is also API differences (closesocket() instead of close(), maxfd is ignored by select(), FD_XXX() expect SOCKET handles instead of int, etc).
On Windows, you need to use something more like this instead:
fd_set mainfd;
SOCKET newfd;
int sin_size;
...
while(TRUE)
{
fd_set readfd = mainfd;
if (select(0, &readfd, NULL, NULL, NULL) == SOCKET_ERROR)
{
itoa(WSAGetLastError(), buf, 10);
MessageBoxA(NULL, "Error while trying to accept incoming connections (select)", "Error", 16);
MessageBoxA(NULL, buf, buf, 0);
break;
}
for(int i = 0; i < readfd.fd_count; i++)
{
if (readfd.fd_array[i] == mainSocket)
{
sin_size = sizeof(their_addr);
newfd = accept(mainSocket, (struct sockaddr*)&their_addr, &sin_size);
if (newfd == INVALID_SOCKET)
{
AddTextToEdit(hStaticChat, "* Error: couldn't accept incoming connection.", TRUE);
}
else
{
// Note that fd_set can only hold FD_SETSIZE (64) sockets as a time!
FD_SET(newfd, &mainfd);
}
}
else
{
char srMsg[257];
len = recv(readfd.fd_array[i], srMsg, 256, 0);
if (len < 1)
{
if (len == 0)
AddTextToEdit(hStaticChat, "* Client has disconnected", TRUE);
else
AddTextToEdit(hStaticChat, "* Error: couldn't read from a client connection.", TRUE);
closesocket(readfd.fd_array[i]);
FD_CLR(readfd.fd_array[i], &mainfd);
}
else
{
srMsg[len] = 0;
AddTextToEdit(hStaticChat, srMsg, TRUE);
for (int j = 0; j < mainfd.fd_count; j++)
{
if (mainfd.fd_array[i] != mainSocket)
send(mainfd.fd_array[j], srMsg, len, 0);
}
}
}
}
}

Winsock nonblocking send() wait buffer. What is the correct method?

I have some questions about when it is needed to store the data in the wait buffer (waiting for the FD_WRITE event).
This is my send function (fixed):
bool MyClass::DataSend(char *buf, int len)
{
if (len <=0 || m_Socket == INVALID_SOCKET) return false;
if (m_SendBufferLen > 0)
{
if ((m_SendBufferLen + len) < MAX_BUFF_SIZE)
{
memcpy((m_SendBuffer + m_SendBufferLen), buf, len);
m_SendBufferLen += len;
return true;
}
else
{
// close the connection and log insuficient wait buffer size
return false;
}
}
int iResult;
int nPosition = 0;
int nLeft = len;
while (true)
{
iResult = send(m_Socket, (char*)(buf + nPosition), nLeft, 0);
if (iResult != SOCKET_ERROR)
{
if (iResult > 0)
{
nPosition += iResult;
nLeft -= iResult;
}
else
{
// log 0 bytes sent
break;
}
}
else
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
if ((m_SendBufferLen + nLeft) < MAX_BUFF_SIZE)
{
// log data copied to the wait buffer
memcpy((m_SendBuffer + m_SendBufferLen), (buf + nPosition), nLeft);
m_SendBufferLen += nLeft;
return true;
}
else
{
// close the connection and log insuficient wait buffer size
return false;
}
}
else
{
// close the connection and log winsock error
return false;
}
}
if (nLeft <= 0) break;
}
return true;
}
My send (FD_WRITE event) function (fixed):
bool MyClass::DataSendEvent()
{
if (m_SendBufferLen < 1) return true;
int iResult;
int nPosition = 0;
int nLeft = m_SendBufferLen;
while (true)
{
iResult = send(m_Socket, (char*)(m_SendBuffer + nPosition), nLeft, 0);
if (iResult != SOCKET_ERROR)
{
if (iResult > 0)
{
nPosition += iResult;
nLeft -= iResult;
}
else
{
// log 0 bytes sent
break;
}
}
else
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
if (nPosition > 0)
{
memmove(m_SendBuffer, (m_SendBuffer + nPosition), (m_SendBufferLen - nPosition));
m_SendBufferLen -= nPosition;
}
break;
}
else
{
// close the connection and log winsock error
return false;
}
}
if (nLeft <= 0)
{
if (m_SendBufferLen == nPosition)
{
m_SendBufferLen = 0;
break;
}
else
{
memmove(m_SendBuffer, (m_SendBuffer + nPosition), (m_SendBufferLen - nPosition));
m_SendBufferLen -= nPosition;
nPosition = 0;
nLeft = m_SendBufferLen;
}
}
}
return true;
}
Do I really need the if (nPosition > 0) or not? How do I simulate this scenario? Is there possible send() in non-blocking mode send less bytes than the requested? If not, why using the while() looping?
This is the final code (thanks to #user315052)
At the top of your while loop, you are already decrementing nLeft, so you don't need to subtract nPosition from it.
iResult = send(m_Socket, (char*)(buf + nPosition), nLeft, 0);
In your second function, when you are shifting the unsent bytes to the beginning of the array, you should use memmove, since you have overlapped regions (you are copying a region of m_SendBuffer into itself). The overlap is illustrated below, where some of the A's would get copied onto itself.
m_SendBuffer: [XXXXAAAAAAAAAAAA]
mPosition: 4
nLeft: 12
I am a little confused about why DataSend() is implemented to allow the caller to keep calling it with success even when the WSAEWOULDBLOCK is encountered. I would suggest the interface be modified to return a result that lets the caller know that it should stop sending, and to wait for an indication to resume sending.
You don't need the nPosition > 0 check.
You can force the case to occur by having the receiver of the data not read anything.
It is definitely possible for send in non-blocking mode to send fewer bytes than requested.

Reading socket: EAGAIN: Resource temporarily unavailable

I have created a socket in C++ and I needed it to have certain connection timeout. So that's what is happening:
Create socket
Make it NON_BLOCKING
Call connect
It returns -1 and errno EINPROGRESS as expected
Call select
Returns >0, so connection has been made
Make the socket BLOCKING again
Code for this part is the following:
bool mastControl::prepareSocket(char * address, int port, int * sockfd) {
struct sockaddr_in serv_addr;
struct timeval timeout = {0,100000};
struct timeval connTimeout;
struct hostent * server = NULL;
fd_set socketSet;
socklen_t lon;
int sockOpt = 0;
long socketFlags = 0;
int buffersize = 8;
int res = 0;
int connectReturn = 0;
const int WAIT_TO_RECONN = 15;
server = gethostbyname(address);
*sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (*sockfd < 0) {
qDebug()<<"Impossible to open socket: "<<strerror(errno);
return false;
}
if (server == NULL) {
qDebug()<<"No such host: "<<strerror(h_errno);
return false;
}
// Initializating server direction struct:
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(port);
// Making socked non-blocking in order to set a timeout value for connection:
if((socketFlags = fcntl(*sockfd, F_GETFL, NULL)) < 0){
qDebug()<<"Impossible to retrieve sockets descriptor flags "<<strerror(errno);
return false;
}
socketFlags |= O_NONBLOCK;
if(fcntl(*sockfd, F_SETFL, socketFlags) <0){
qDebug()<<"Impossible to update sockets descriptor flags: "<<strerror(errno);
return false;
}
connectReturn = connect(*sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr));
if(connectReturn < 0){
if(errno == EINPROGRESS){
do{
// Establishing a 15 seconds timeout:
connTimeout.tv_sec = 15;
connTimeout.tv_usec = 0;
FD_ZERO(&socketSet); // Initialising set of sockets as an empty set
FD_SET(*sockfd, &socketSet); // Adding socket to set
connectReturn = select(*sockfd+1, NULL, &socketSet, NULL, &connTimeout);
if(connectReturn<0 && errno!=EINTR){ // Error in select
qDebug()<<"Connection error in select function: "<<strerror(errno);
return false;
}
else if(connectReturn>0){ // Socket selected for writing
lon = sizeof(int);
if(getsockopt(*sockfd, SOL_SOCKET, SO_ERROR, (void*)(&sockOpt), &lon) <0){
qDebug()<<"Unnable to get socket options: "<<strerror(errno);
return false;
}
// Checking the value returned:
if(sockOpt){
qDebug()<<"Error in delayed connection: "<<strerror(errno);
return false;
}
break;
}
else{ // Timeout
qDebug()<<"Connection timeout exceeded: "<<strerror(errno);
return false;
}
} while (1);
}
else{
qDebug()<<"Connection error: "<<strerror(errno);
sleep(WAIT_TO_RECONN); // Wait 15 seconds
return false;
}
}
//Connected
// Must set the socket as blocking again:
if((socketFlags = fcntl(*sockfd, F_GETFL, NULL)) < 0){
qDebug()<<"Impossible to retrieve sockets descriptor flags "<<strerror(errno);
return false;
}
socketFlags &= (~O_NONBLOCK);
if(fcntl(*sockfd, F_SETFL, socketFlags) <0){
qDebug()<<"Impossible to update sockets descriptor flags "<<strerror(errno);
return false;
}
if (setsockopt (*sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
sizeof(timeout)) < 0) {
qDebug()<< "ERR - setsockopt failed";
return false;
}
if (setsockopt (*sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
sizeof(timeout)) < 0) {
qDebug()<< "ERR - setsockopt failed";
return false;
}
if ((res = setsockopt (*sockfd, SOL_SOCKET, SO_SNDBUF, &buffersize,
sizeof(buffersize))) == -1) {
qDebug()<< "ERR - setsockopt failed (SO_SNDBUF) = " << res;
return false;
}
//Socket Ready
return true;
}
That works ok. But then I have a loop where I'm calling a function which checks if there is a new packet received to read:
bool mastControl::findPacket(int sockfd, st_messageMastToPc * messageReceived, bool * connected) {
int n = 0;
bool messageFound = false;
char * buffer = (char *) messageReceived;
unsigned int pos = 0;
while ( ((n = read(sockfd, &(buffer[pos]), 1)) > 0) and not messageFound) {
//qDebug() << "read output " << n;
if (n == 1) {
pos++;
if ( (pos == 1) && (buffer[0] == 2)) {
// Some stuff...
} else if ( (pos == 2) && (buffer[1] == 2) ) {
// Some stuff...
} else if (pos >= uiMessageMastToPcSize) {
messageFound = true;
//Complete message received
} else if (pos < 2) {
// Reseting pos
pos = 0;
}
}
}
if (n < 0){
qDebug()<< "Disconnected. Reason #" << errno << ": " << strerror(errno);
*connected = false;
}
return messageFound;
}
Read function is giving EAGAIN as errno, which means "Resource temporarily unavailable". Then I'm supposing I am disconnected, and since boolean connected is now false, in the loop I will be trying to reconnect creating the socket again, ans so on.
So, in the first place I don't know why I'm receiving this error.
In the second place, I don't know if I am disconnected at all, or it's me the one who is disconnecting when creating the new socket after this error.
Any help?
EAGAIN does not mean you're disconnected, it just means "there's nothing to read now; try again later".
You could either unset O_NONBLOCK with fcntl(2) (making read wait until there's something available), or just wait on the socket with something like select(2) before calling read.
EDIT: Now that you've added more code, I can see that you're setting SO_RCVTIMEO for the socket. This can cause a blocking read to return EAGAIN (so if you don't want that to happen, simply leave SO_RCVTIMEO alone).