I've been able to use epoll successfully after your help and reading another resources, the problem I'm facing now is that I need to send to different FD than the EPOLLOUT event return, so if I have 5 FDs, then I need to send to 1 then to 4 then back to 1 then to 5 and so on based.
The following pseudo code is what I have now with both IN and OUT events,
while(1) {
rval = epoll_wait();
for (i = 0; i < rval; i++) {
if (events & EPOLLOUT) {
//send to the ready FD for sending
}
if (events & EPOLLIN) {
//Receive from ready fd, this part is perfect for my application
}
}
}
What I need to do is something like the following
char buff[100];
fd = read_data(buff); // read data ready to send, the return value is the FD the application should send the returned data to
send(fd, buff, 100, 0);
and then I need the EPOLLIN to receive data from any FD because I'll be receiving from all fd all the time, I'm not sure how to do that, can you please help?
You should add a layer of wrappage around every FD that can send: Let it have some state that says "ready", and a local data queue. Now make an interface that accepts data. If the state is not "ready", immediately enqueue the data and return. Otherwise attempt to send the data (in a loop), and if you hit EAGAIN or EWOULDBLOCK, enqueue the rest and set "ready" to false.
Meanwhile, in your main event loop, whenever you get an EPOLLOUT, go into a send loop to drain the queue, and if you finish without error set "ready" to true. (With this logic, EPOLLOUT should only fire when your FD is not "ready", assuming you're in edge-triggered mode.)
Related
I have a question regarding adding new socket file descriptors to an FDSET. Lets say we've already connected to a socket s1:
fd_set readfds;
//s1 = socket(...);
//connect(s1, ...)...
FD_ZERO(&readfds);
FD_SET(s1, &readfds);
and we are waiting for data to come down the socket, by calling select in a thread:
socket_reader_thread() {
for (;;)
{
int rv = select(n, &readfds, NULL, NULL, &tv);
if (rv == -1) {
perror("select"); // error occurred in select()
}
else if (rv == 0) {
printf("Timeout occurred! No data after 10.5 seconds.\n");
}
else {
// one the descriptors have data
.....
}
}
}
If I now wanted to add another socket (or may be two more socket etc) to the readfds set, given that select is blocking, how should I proceed? how can I interrupt select
Is the trick to add a zero timeout and use select like poll?
You need to use the "pipe-trick".
This is where an additional socket or pipe is created add it to the fd_set.
Then to interrupt a running or pending select, send a 1 byte message to it via another thread.
The select will then return and if the special pipe FD is one of the ones that are ready in the set, that means you need to say look at a list or something "do work" - like add any new FDs to the fd_set before returning to the select call.
You can interrupt select by sending (and catching) a signal to your process, for example using raise. select will return in this case with -1 and errno set to EINTR. You can then change the events you want to wait for and call select again.
Is the trick to add a zero timeout and use select like poll?
One can simply use a timeout of 0 in which case it will just do a non-blocking check if any of the events got triggered, i.e. polling. But this should only be done in a few cases since busy polling instead of a blocking wait uses lots of resources of machine. And I would even consider the interrupting of a blocking select a questionable design, although probably not as bad as busy polling.
I have a C socket client program where one thread is for receiving data and another for sending. If the server shuts down then the sender gets EPIPE. If I reconnect the same socket then it can receive data but the sender still gets EPIPE.
How to fix this?
Update:
Actually sender seems to send data as I see number of byte sent. But errno is still set to broken pipe. Before I only checked errno. Shouldn't it be changed to successful?
If I reconnect the same socket then it can receive data but the sender still gets EPIPE.
That can only mean that the sender is still sending via the old socket; also that you haven't closed the old socket.
sender seems to send data as I see number of byte sent. But errno is still set to broken pipe. Before I only checked errno. Shouldn't it be changed to successful?
No. It is only valid to check errno when an immediately prior system call has returned -1. Example:
int rc = send(...);
if (rc < 0)
{
if (errno == EWOULDBLOCK) // or EAGAIN *and* we are in non-blocking mode
{
// queue the write and return to the select() loop
}
else
{
perror("send"); // for example
}
}
else
{
// write succeeded ...
}
I am aware that select() will be triggered whenever there is a data in the registered socket buffer.
what will happen if there is a delay between these two statements.
FD_SET(listener, &read_fds); // &
(select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
what will happen if packet arrives between these two statements?
//create socket and listen for packets &
FD_SET(listener, &read_fds);
Assume that recv() is done once select is triggered.
What will happen if a packet arrives before the select() call is
made.?
does FD_ISSET still detects the packet which is already in
socket buffer or it will be detected only if new packet arrives and
select gets triggered?
Sample code:
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
Understanding gap between socket creation and select() system call
There is no gap between socket creation and select() in your question.
I am aware that select() will be triggered whenever there is a data in the registered socket buffer.
That's true for read events and it applies to the socket receive buffer of connected sockets. It also triggers when there is an inbound connection on a listening socket, or room in the socket send buffer for send events.
what will happen if there is a delay between these two statements.
FD_SET(listener, &read_fds); // &
(select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
Nothing bad. Any event that occurs between them will still be signalled. But the first statement isn't a socket creation, contrary to your title.
what will happen if packet arrives between these two statements?
//create socket and listen for packets &
FD_SET(listener, &read_fds);
The socket send buffer exists from the moment the socket is created, so the data will go into the buffer, so when select() runs it will see that and trigger a read event.
Assume that recv() is done once select is triggered.
What will happen if a packet arrives before the select() call is made.?
The socket send buffer exists from the moment the socket is created, so the data will go into the buffer, so when select() runs it will see that and trigger a read event.
does FD_ISSET still detects the packet which is already in socket buffer
Yes.
or it will be detected only if new packet arrives and select gets triggered?
It will always be detected.
If data is waiting to be read, select will return immediately, and FD_ISSET will return true for the file descriptor that the data arrived on. It doesn't matter if data arrived before or after select was called.
select() completes immediately if one or more of the watched conditions is already active; otherwise it blocks until one or more of the watched conditions becomes active (or the timeout, if specified, expires).
I am trying to make a simple client-server chat program. On the client side I spin off another thread to read any incomming data from the server. The problem is, I want to gracefully terminate that second thread when a person logs out from the main thread. I was trying to use a shared variable 'running' to terminate, problem is, the socket read() command is a blocking command, so if I do while(running == 1), the server has to send something before the read returns and the while condition can be checked again. I am looking for a method (with common unix sockets only) to do a non-blocking read, basically some form of peek() would work, for I can continually check the loop to see if I'm done.
The reading thread loop is below, right now it does not have any mutex's for the shared variables, but I plan to add that later don't worry! ;)
void *serverlisten(void *vargp)
{
while(running == 1)
{
read(socket, readbuffer, sizeof(readbuffer));
printf("CLIENT RECIEVED: %s\n", readbuffer);
}
pthread_exit(NULL);
}
You can make socket not blockable, as suggested in another post plus use select to wait input with timeout, like this:
fd_set input;
FD_ZERO(&input);
FD_SET(sd, &input);
struct timeval timeout;
timeout.tv_sec = sec;
timeout.tv_usec = msec * 1000;
int n = select(sd + 1, &input, NULL, NULL, &timeout);
if (n == -1) {
//something wrong
} else if (n == 0)
continue;//timeout
if (!FD_ISSET(sd, &input))
;//again something wrong
//here we can call not blockable read
fcntl(socket, F_SETFL, O_NONBLOCK);
or, if you have other flags:
int x;
x=fcntl(socket ,F_GETFL, 0);
fcntl(socket, F_SETFL, x | O_NONBLOCK);
then check the return value of read to see whether there was data available.
note: a bit of googling will yield you lots of full examples.
You can also use blocking sockets, and "peek" with select with a timeout. It seems more appropriate here so you don't do busy wait.
The best thing is likely to get rid of the extra thread and use select() or poll() to handle everything in one thread.
If you want to keep the thread, one thing you can do is call shutdown() on the socket with SHUT_RDWR, which will shut down the connection, wake up all threads blocked on it but keep the file descriptor valid. After you have joined the reader thread, you can then close the socket. Note that this only works on sockets, not on other types of file descriptor.
Look for function setsockopt with option SO_RCVTIMEO.
I have a fairly basic TCP server keeping track of a couple connections and recv'ing data when it's available. However, I'd like to artificially trigger an event from within the program itself, so I can send my TCP server data as if it came from sock1 or sock2, but in reality came from somewhere else. Is this possible, or at all clear?
struct pollfd fds[2];
fds[0].fd = sock1;
fds[1].fd = sock2;
while (true) {
int res = poll(fds, 2, timeout);
if ((fds[0].revents & POLLIN)){
//ready to recv data from sock1
}
if ((fds[1].revents & POLLIN)){
//ready to recv data from sock2
}
}
Create a pair of connected sockets (see socketpair(2)), and wait for events on one of the sockets in your poll loop. When you want to wake up the poll thread, write a single byte to the other socket. When the polling loop wakes up, read the byte, do whatever was required and continue.
This is more like a design question -- your polling loop should probably abstract the poll method to allow trapping on other external signals, like from kill -USR1.
If you really want to trigger port traffic, you'll likely want to use netcat to send data to the socket.
I would consider something like this:
struct pollfd fds[2];
fds[0].fd = sock1;
fds[0].events = POLLIN;
fds[1].fd = sock2;
fds[1].events = POLLIN;
for (;;) {
int result = poll(fds, 2, timeout);
if (result) {
if ((fds[0].revents & POLLIN)){
/* Process data from sock1. */
}
if ((fds[1].revents & POLLIN)){
/* Process data from sock2. */
}
} else {
/* Do anything else you like, including
processing data that wasn't from a
real socket. */
}
}
Notes:
don't forget to initialise your events field
for(;;) is more idiomatic C than while(true) and doesn't require true to be defined