I'm trying to write a web program, using poll(). I'm creating a UDP in a struct pollfd array, and then polling it. However, the poll() returns 0 every time, no matter how many times I send it a message. When I just call recvfrom, it works just fine. So here's my code:
Creating and binding the socket:
struct pollfd[2] fds;
// ...
fds[0].fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fds[0].fd < 0)
syserr("socket");
listen_address = { 0 };
listen_address.sin_family = AF_INET;
listen_address.sin_addr.s_addr = htonl(INADDR_ANY);
listen_address.sin_port = htons(m_port);
if (bind(
fds[0].fd,
(struct sockaddr*) &listen_address,
(socklen_t) sizeof(listen_address)
) < 0)
syserr("bind");
Now this works:
for (;;) {
memset(buffer, 0, BUF_SIZE + 1);
rval = recvfrom(
fds[0].fd,
buffer,
BUF_SIZE,
0,
(struct sockaddr *) &respond_address,
&rcva_len
);
std::cout << buffer << std::endl;
}
But this doesn't
finished = false;
do {
fds[0].revents = fds[1].revents = 0;
ret = poll(fds, 2, 0);
if (ret < 0) {
perror("poll");
} else if (ret == 0) {
// the loop always enters here
} else {
// the loop never enters here,
// even though I send messages to the socket
}
} while (!finished);
For testing, I use a command like this
echo -n “foo” | nc -4u -w1 <host> <udp port>
I would appreciate some insight
There are two critical problems with the way that you use poll():
You must set the revents field, for each file descriptor, to indicate which events you are interested in, such as POLLIN and/or POLLOUT. Your code fails to set revents to anything.
The third parameter to poll() is the timeout setting. Which you are setting to 0. This means "check the file descriptors for whether or not any of the requested events have occured, but always return immediately in any case, and if no file descriptors have the requested events then return 0".
Which is the behavior you are seeing.
Instead of telling you what you need to set the third parameter to, in order to wait until any of the file descriptors's events have occured, I'll just refer you to poll()'s manual page. Reading manual pages is good. They explain everything.
In conclusion:
Initialize each file descriptor's revents parameter properly.
Pass the correct parameters to poll(), as described in its manual page. If you want poll() to wait indefinitely, until any of the passed file descriptors' requested events have occured, there's a specific value to do this. Look it up.
After poll() returns, check each file descriptor's events parameter to determine the file descriptor's status.
Related
I'm trying to send data to the connected client, even when the client did not send me a message first.
This is my current code:
while (true) {
// open a new socket to transmit data per connection
int sock;
if ((sock = accept(listen_sock, (sockaddr *) &client_address, &client_address_len)) < 0) {
logger.log(TYPE::ERROR, "server::could not open a socket to accept data");
exit(0);
}
int n = 0, total_received_bytes = 0, max_len = 4096;
std::vector<char> buffer(max_len);
logger.log(TYPE::SUCCESS,
"server::client connected with ip address: " + std::string(inet_ntoa(client_address.sin_addr)));
// keep running as long as the client keeps the connection open
while (true) {
n = recv(sock, &buffer[0], buffer.size(), 0);
if (n > 0) {
total_received_bytes += n;
std::string str(buffer.begin(), buffer.end());
KV key_value = kv_from(vector_from(str));
messaging.set_command(key_value);
}
std::string message = "hmc::" + messaging.get_value("hmc") + "---" + "sonar::" + messaging.get_value("sonar") + "\n";
send(sock, message.c_str(), message.length(), 0);
}
logger.log(TYPE::INFO, "server::connection closed");
close(sock);
}
I thought by moving the n = recv(sock, &buffer[0], buffer.size(), 0); outside the while condition that it would send the data indefinitely, but that is not what happened.
Thanks in advance.
Solution
Adding MSG_DONTWAIT to the recv function enabled non-blocking operations which I was looking for.
First I will explain, why it does not work, then I will make a proposal for solutions. Basically you will find the answer in the man7.org > Linux > man-pages and for recv specifially here.
When the function "recv" is called, then it will not return, until data is available and can be read. This behavior of functions is called "blocking". Means, the current execution thread is blocked until data has been read.
So, calling the function
n = recv(sock, &buffer[0], buffer.size(), 0);
as you did, causes the trouble. You need also to check the return code. 0 means, connection closed, -1 means error and you must check errno for further information.
You can modify the socket to work in non-blocking mode with the function fnctl and the O_NONBLOCK flag, for the lifetime of the socket. You can also use the the flag MSG_DONTWAIT as 4th parameter (flags), to unblock the function on a per-function-call base.
In both cases, if no data is available, the functions returns a -1 and you need to check errno for EAGAIN or EWOULDBLOCK.
return value 0 indicates that the connection has been closed.
But from the architecture point of view, I would not recommend to use this approach. You could use multiple threads for receiving and sending data, or, using Linux, one of select, poll or similar functions. There is even a common design pattern for this. It is called "reactor", There are also related patterns like "Acceptor/Connector" and "Proactor"/"ACT" available. If you plan to write a more robust application, then you may consider those.
You will find an implementation of Acceptor, Connector, Reactor, Proactor, ACT here
Hope this helps
I'm writing a network game for a university project and while I have messages being sent and received between a client and a server, I'm unsure on how I would go about implementing a writeable fd_set (my lecturer's example code only included a readable fd_set) and what the function is of both fd_sets with select(). Any insight you could give would be great in helping me understand this.
My server code is as such:
bool ServerSocket::Update() {
// Update the connections with the server
fd_set readable;
FD_ZERO(&readable);
// Add server socket, which will be readable if there's a new connection
FD_SET(m_socket, &readable);
// Add connected clients' sockets
if(!AddConnectedClients(&readable)) {
Error("Couldn't add connected clients to fd_set.");
return false;
}
// Set timeout to wait for something to happen (0.5 seconds)
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 500000;
// Wait for the socket to become readable
int count = select(0, &readable, NULL, NULL, &timeout);
if(count == SOCKET_ERROR) {
Error("Select failed, socket error.");
return false;
}
// Accept new connection to the server socket if readable
if(FD_ISSET(m_socket, &readable)) {
if(!AddNewClient()) {
return false;
}
}
// Check all clients to see if there are messages to be read
if(!CheckClients(&readable)) {
return false;
}
return true;
}
A socket becomes:
readable if there is either data in the socket receive buffer or a pending FIN (recv() is about to return zero)
writable if there is room in the socket receive buffer. Note that this is true nearly all the time, so you should use it only when you've encountered a prior EWOULDBLOCK/EAGAIN on the socket, and stop using it when you don't.
You'd create an fd_set variable called writeable, initialize it the same way (with the same sockets), and pass it as select's third argument:
select(0, &readable, &writeable, NULL, &timeout);
Then after select returns you'd check whether each socket is still in the set writeable. If so, then it's writeable.
Basically, exactly the same way readable works, except that it tells you a different thing about the socket.
select() is terribly outdated and it's interface is arcane. poll (or it's windows counterpart WSAPoll is a modern replacement for it, and should be always preferred.
It would be used in following manner:
WSAPOLLFD pollfd = {m_socket, POLLWRNORM, 0};
int rc = WSAPoll(&pollfd, 1, 100);
if (rc == 1) {
// Socket is ready for writing!
}
I am trying to understand how are file descriptors related to sockets. As per my understanding, you listen on a particular file descriptor, once a connection comes in , you accept it , which returns you another file descriptor ( 2 in all ) and you use this 2nd descriptor to send/recv data.
The strange behaviour i am observing is that after accept , i have 3 file descriptors instead of two.... and i am not sure why is this the case....
I am either using lsof or /proc/pid to observe the increase in number of fd's.
ps : these are af_unix sockets.
EDIT : CODE
Here is the code to create the scoket.
int s, s2, len;
socklen_t t;
struct sockaddr_un local, remote;
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
{
syslog(LOG_ERR,"Failed to create a socket");
exit(1);
}
int flags = fcntl(s, F_GETFD);
if (flags == -1)
{
syslog(LOG_ERR,"Failed to get socket flags");
exit(1);
}
flags |= FD_CLOEXEC;
if (fcntl(s, F_SETFD, flags) == -1)
{
syslog(LOG_ERR,"Failed to set socket flags");
exit(1);
}
local.sun_family = AF_UNIX;
strcpy(local.sun_path, SOCK_PATH.c_str());
unlink(local.sun_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(s, (struct sockaddr *)&local, len) == -1)
{
syslog(LOG_ERR,"Failed to bind socket");
exit(1);
}
if (listen(s, 5) == -1)
{
syslog(LOG_ERR,"Failed to listen at socket");
exit(1);
}
Code where connection is accepted
while (1)
{
stat =0;
execReturn=0;
t = len;
read_fds = master;
if (select(fdmax+1, &read_fds, NULL, NULL, &tv) != -1)
{
if(FD_ISSET(s,&read_fds))
{
//Accept new connection
//fork child -> fork grand child
//child will return value back
if ((s2 = accept(s, (struct sockaddr*)&remote, &t)) == -1)
{
syslog(LOG_ERR,"Failed to acceptconnection at socket");
exit(1);
}
I am stepping through gdb and exactly after accept , the fd's become 3. The OS is fedora core 13.
The reason i need to validate this is i do not want my process to hold on to FD's ; since being a daemon over time it may walk the system into a corner...
This did seem odd behaviour. After closing the accepted connection i am still left with two fd's .
i.e. one for listen and one ghost fd... Whats even more strange is that even if 10 connections are made , only one ghost fd remains at the end of all of them closing....
It does sound like OS specific implementation..
Cheers!
Your extra file descriptor is most likely related to syslog. Syslog has to open a socket to the syslogd to report messages. Unless you explicitly call openlog this socket is opened upon the first call to syslog, and since you aren't calling syslog until you have an error you are most likely observing syslog's side effects.
The easiest way to debug this sort of issues is to run your app under the strace(1) utility. Check what system calls are made, what the parameters and return values are, and correlate that to file descriptors used.
More code please.
But I am guessing that you are looking at the OS implementation of the socket.
It probably uses one file descriptor for reading and the other for writing (but that is a guess).
What does it matter to you what the OS is doing in /proc/pid the stuff in there is not really there for your direct usage.
You are right in that it's two. You must be confusing the third with something else.
Without more information it's hard to help.
I'm having a strange problem while attempting to transform a blocking socket server into a nonblocking one. Though the message was only received once when being sent with blocking sockets, using nonblocking sockets the message seems to be received an infinite number of times.
Here is the code that was changed:
return ::write(client, message, size);
to
// Nonblocking socket code
int total_sent = 0, result = -1;
while( total_sent < size ) {
// Create a temporary set of flags for use with the select function
fd_set working_set;
memcpy(&working_set, &master_set, sizeof(master_set));
// Check if data is available for the socket - wait 1 second for timeout
timeout.tv_sec = 1;
timeout.tv_usec = 0;
result = select(client + 1, NULL, &working_set, NULL, &timeout);
// We are able to write - do so
result = ::write(client, &message[total_sent], (size - total_sent));
if (result == -1) {
std::cerr << "An error has occured while writing to the server."
<< std::endl;
return result;
}
total_sent += result;
}
return 0;
EDIT: The initialization of the master set looks like this:
// Private member variables in header file
fd_set master_set;
int sock;
...
// Creation of socket in class constructor
sock = ::socket(PF_INET, socket_type, 0);
// Makes the socket nonblocking
fcntl(sock,F_GETFL,0);
FD_ZERO(&master_set);
FD_SET(sock, &master_set);
...
// And then when accept is called on the socket
result = ::accept(sock, NULL, NULL);
if (result > 0) {
// A connection was made with a client - change the master file
// descriptor to note that
FD_SET(result, &master_set);
}
I have confirmed that in both cases, the code is only being called once for the offending message. Also, the client side code hasn't changed at all - does anyone have any recommendations?
fcntl(sock,F_GETFL,0);
How does that make the socket non-blocking?
fcntl(sock, F_SETFL, O_NONBLOCK);
Also, you are not checking if you can actually write to the socket non-blocking style with
FD_ISSET(client, &working_set);
I do not believe that this code is really called only once in the "non blocking" version (quotes because it is not really non-blocking yet as Maister pointed out, look here), check again. If the blocking and non blocking versions are consistent, the non blocking version should return total_sent (or size). With return 0 instead caller is likely to believe nothing was sent. Which would cause infinite sending... is it not what's happening ?
Also your "non blocking" code is quite strange. You seem to use select to make it blocking anyway... Ok, with a timeout of 1s, but why don't you make it really non blocking ? ie: remove all the select stuff and test for error case in write() with errno being EWOULDBLOCK. select or poll are for multiplexing.
Also you should check errors for select and use FD_ISSET to check if socket is really ready. What if the 1 s timeout really happen ? Or if select is stopped by some interruption ? And if an error occurs in write, you should also write which error, that is much more useful than your generic message. But I guess this part of code is still far from finished.
As far as I understand your code it should probably look somewhat like that (if the code is running in an unique thread or threaded, or forking when accepting a connection would change details):
// Creation of socket in class constructor
sock = ::socket(PF_INET, socket_type, 0);
fcntl(sock, F_SETFL, O_NONBLOCK);
// And then when accept is called on the socket
result = ::accept(sock, NULL, NULL);
if (result > 0) {
// A connection was made with a client
client = result;
fcntl(client, F_SETFL, O_NONBLOCK);
}
// Nonblocking socket code
result = ::write(client, &message[total_sent], (size - total_sent));
if (result == -1) {
if (errno == EWOULDBLOCK){
return 0;
}
std::cerr << "An error has occured while writing to the server."
<< std::endl;
return result;
}
return size;
One of my projects on Linux uses blocking sockets. Things happen very serially so non-blocking would just make things more complicated. Anyway, I am finding that often a recv() call is returning -1 with errno set to EAGAIN.
The man page only really mentions this happening for non-blocking sockets, which makes sense. With non-blocking, the socket may or may not be available so you might need to try again.
What would cause it to happen for a blocking socket? Can I do anything to avoid it?
At the moment, my code to deal with it looks something like this (I have it throw an exception on error, but beyond that it is a very simple wrapper around recv()):
int ret;
do {
ret = ::recv(socket, buf, len, flags | MSG_NOSIGNAL);
} while(ret == -1 && errno == EAGAIN);
if(ret == -1) {
throw socket_error(strerror(errno));
}
return ret;
Is this even correct? The EAGAIN condition gets hit pretty often.
EDIT: some things which I've noticed which may be relevant.
I do set a read timeout on the socket using setsockopts(), but it is set to 30 seconds. the EAGAIN's happen way more often than once every 30 secs. CORRECTION my debugging was flawed, EAGAIN's don't happen as often as I thought they did. Perhaps it is the timeout triggering.
For connecting, I want to be able to have connect timeout, so I temporarily set the socket to non-blocking. That code looks like this:
int error = 0;
fd_set rset;
fd_set wset;
int n;
const SOCKET sock = m_Socket;
// set the socket as nonblocking IO
const int flags = fcntl (sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
errno = 0;
// we connect, but it will return soon
n = ::connect(sock, addr, size_addr);
if(n < 0) {
if (errno != EINPROGRESS) {
return -1;
}
} else if (n == 0) {
goto done;
}
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_SET(sock, &rset);
FD_SET(sock, &wset);
struct timeval tval;
tval.tv_sec = timeout;
tval.tv_usec = 0;
// We "select()" until connect() returns its result or timeout
n = select(sock + 1, &rset, &wset, 0, timeout ? &tval : 0);
if(n == 0) {
errno = ETIMEDOUT;
return -1;
}
if (FD_ISSET(sock, &rset) || FD_ISSET(sock, &wset)) {
socklen_t len = sizeof(error);
if (getsockopt(SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
return -1;
}
} else {
return -1;
}
done:
// We change the socket options back to blocking IO
if (fcntl(sock, F_SETFL, flags) == -1) {
return -1;
}
return 0;
The idea is that I set it to non-blocking, attempt a connect and select on the socket so I can enforce a timeout. Both the set and restore fcntl() calls return successfully, so the socket should end up in blocking mode again when this function completes.
It's possible that you have a nonzero receive timeout set on the socket (via setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,...)) as that would also cause recv to return EAGAIN
Is it possible that you're using MSG_DONTWAIT is being specified as part of your flags? The man page says EAGAIN will occur if no data is available and this flag is specified.
If you really want to force a block until the recv() is somewhat successful, you may wish to use the MSG_WAITALL flag.
I don't suggest this as a first-attempt fix, but if you're all out of options, you can always select() on the socket with a reasonably long timeout to force it to wait for data.
EAGAIN is generated by the OS almost like an "Oops! I'm sorry for disturbing you.". In case of this error, you may try reading again, This is not a serious or fatal error. I have seen these interrupts occur in Linux and LynxOS anywhere from one a day to 100 times a day.