3

I am following this document

I struggle to wrap my head around file descriptors and how I could read data from one, process it and then send to another.

As a server I need to be able to accept connections, receive data, process it and then pass it to another client.

I have been introduced to epolling yesterday and I want to know if my strategy is correct for creating a client-server network.

One epollfd is created. I specify it to be edge-triggered (EPOLLET) and non-blocking (set with: flags |= 0_NONBLOCK and fctnl(epollfd, F_SETFL, flags).

My intention is to now create an array of networkfds (client sockets) and listen for connection/messages.

  1. Get notified about new data
  2. Read data
  3. Process data
  4. Write some data to another socket.

All the examples I found in linux man and online only offer information about how to read data from sockets and I am afraid my design is dumb and due to failure if I try to have many clients practically communicating to many clients at the same time.

I decided to ask here because I read that NGINX (the web server is using epolling)

Can anyone help?

EDIT 1: I am intending to have (many) sockets in a list (struct epoll_event *events) and access them via epoll_wait().

// If I understand correctly: 
int ndfs = epoll_wait(epollfd, events, MAX_EVENTS, -1);
// Puts some events in the <events> array and an int in ndfs

nfds should now contain the number of available fds in events which one can iterate through with a for loop. That's what I understand from the manual.

Once I receive a message from one of them, I would like to be able to process it (i.e read it's contents and take a decision) and eventually trigger a write to another socket.

I do all of this to AVOID multithreading. Is this achievable?

6
  • Could you post the relevant part of the code and tell us what doesn't work as expected? – Eduardo Trápani Oct 17 '19 at 16:14
  • Please update your question with the information about the "drone network" you've provided as comment to an answer. This is a classic case of an XY problem. One simple solution is to use a language framework where you don't have to deal with file descriptor polling. For example a simple nodejs web server with one API for the drones and another API for the users would do nicely. You certainly can reimplement everything in C and re-invent and wheel, but why go to this effort? – dirkt Oct 18 '19 at 11:18
  • @dirkt : nodejs ? seriously ? well, maybe he has a nuclear power-plant on his drones ... and 2TB drives too. (Trying to exagerate as much as you did ...) With a little design beforehand some solutions are billions of times more effective than others. In C, messages can be a few bytes long to cary more information than kilobytes with other solutions, and will require close to no CPU time for handling them ... – Nathael Pajani Oct 21 '19 at 1:46
  • @NathaelPajani: That part is not supposed to run on a drone, it's on a server running on a network the drones connect to. And of course one can trade longer development time by hand-coding everything against code size. The question is "do you spend one hour and have this whole thing working and running on a server, or do you spend one month and code everything from scratch, including the protocols etc., and you'll still have bugs?" Your choice, of course. – dirkt Oct 21 '19 at 6:12
  • @dirkt : lol, bugs do not come from making things from scratch, and making things from scratch does not require more time than doing the same thing using an high level API. You still have to learn the API, whatever the API level, and you'll still have to create a protocol. But while using an API you leave room for bugs from others, good idea :) If some day you find real arguments, feel free to share them :) – Nathael Pajani Oct 22 '19 at 20:11
1

The question is "to which one do you want to write ?"

Let's say you have two client sockets, then it's easy, read (or recv()) on socket_fd1, and write (or send()) on socket_fd2. It's as simple as that.

In C, nothing requires that you "reply" to the client

Whether you need to reply to the client (or another one) is related to the protocol/application you want to implement.

With more sockets opened, the only question is "where does the data go ?".

You could even send the same data to all the clients sockets. Save the fds in a table/list/whatsoever and for each entry "fd" call send(fd, buf, size, flags);

(Sounds like you are trying to write a kind of chat service, so you will send to all but the one from which you received :)

If choosing to who you want to send is more complicated, use a table of structures with more information on each clients to take the decision.

Hope that solves your problem.

3
  • Yes, this is exactly what I am trying to do. I am building a network of drones. I want that when I power a drone, it connects to the network and says "I'm a drone". Then other drones connect in the same fashion. One user could connect to the server and say "I'm a user". Now the user can fetch information from the server (i.e number of drones, available drones, location of drones etc). When he chooses a drone, this "binds" to the user and is not longer available to other users. Now he can send messages to the drone. You can see that in my design the clients are very diverse and they "chat" – bem22 Oct 18 '19 at 11:01
  • The problem occurs when multiple clients want to chat at the same time. Handling everyone could be solved through multithreading OR epolling as far as I can see – bem22 Oct 18 '19 at 11:02
  • Well, that looks like one of the projects I proposed to my students : making a swarm of drones interconnected through a mesh network to explore hostile places :) Have a look at batman for mesh networking over Wifi, at UDP broadcast for messaging, at inotify (and maybe select) as alternatives to epoll (not necessarily better ones, only other ways to do it) – Nathael Pajani Oct 19 '19 at 13:29
0

It might be that you do not differentiate enough between:

file descriptors (fd), and

sockets.

You mention "networkfds". I guess they are borderline, like a NFS file can be special in certain situations. It is not a file, but a NFS file.

It matters, because, afaics, polling an fd is what a socket does not need because of stateful TCP protocol. (I would bolden this if I was more sure of it.)

A raw UDP socket pair would be similar to a pair of fd: the connection is ready, but how to organize the data flow? Explains the ubiquity of the "internet" protocol TCP (With IP being just a layer below, more static, also important. See wikipedia!).

So is your design dumb? By definition yes, a bit raw. Compared to TCP/IP. File descriptors are single contacts for IO streams, sockets are luxurious plugs of the internet age. There is connect etc. only for sockets.

Does that make sense to you? I halfways could follow your Q, hope I'm not too far off. Please tell.

6
  • with the semantic "everything is a fd" there's not much difference in waiting/checking an event in a socket, a pipe (which is used as example in man epoll) or a file. All 3 are using file descriptors. – A.B Oct 17 '19 at 20:06
  • @A.B I extrapolate from writing to a named pipe, where data flows only when a reading process reads also. Or you read an empty fifo and block until somebody write. THAT is the opposite of a well set up socket. More a trap than a plug. – user373503 Oct 17 '19 at 20:19
  • this won't happen when using epoll* or poll since one of their use is to tell: go aheed, you won't block on next operation on this fd (POLLIN for reading, POLLOUT for writing) – A.B Oct 17 '19 at 20:22
  • @A.B This epoll thing looks complicated, period. It seems to be something between fd and sockets. I know only some theory about sockets. I know they are important, but I dont sell them. I think sockets come with "events" built-in. – user373503 Oct 17 '19 at 20:27
  • @rastafile A.B thanks for contributing. I have posted an update. rasta I agree with all the statements you made. However, I missed some relevant points in my description already long. I have posted an edit. Thank you so much. – bem22 Oct 17 '19 at 22:44
0

Ok, guys. I have come to a conclusion for my question: it depends.

It depends on the application. I studied some benchmarks for epoll only/multithread only/ epoll with worker threads.

I concluded that I will use epoll with worker threads for my project because that makes the most sense: epoll_wait is the fastest fd checker/selector when an fd is available for read/write, but the actual job (decoding / analyzing and handling the data have to be done concurrently (i.e, with threads)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.