21

I am really interested in event-driven programming in C especially with sockets so I am going to dedicate some time doing my researches.

Let's assume that I want to build a program with much File and Network I/O like a client/server app, basically, the first question is what is the philosophy behind this model. While in normal programming I would spawn new processes, how come a single process can actually serve many other requests. For example, there are some web-servers which can handle connections without creating threads or other processes, just one main process.

I know this is complicated but it's always nice to know how different solutions work.

iNDicator
  • 514
  • 1
  • 6
  • 15
  • If you are going to do any linux socket stuff, I would recommend Beejs guide here http://beej.us/guide/bgnet/ – mathematician1975 Jun 16 '12 at 10:38
  • Thanks for the recommendation, does this book explain or contains any kind of event-driven programming reference ? – iNDicator Jun 16 '12 at 10:50
  • 1
    It's not clear what you're asking, but you could start by reading for instance libevent's documentation. – Artefacto Jun 16 '12 at 10:52
  • @iINDicator - not as far as I know. If you are already happy with sockets in general on linux then you wont learn much more. I suggested it just in case you were totally new to sockets as this guide really helped me get started. – mathematician1975 Jun 16 '12 at 10:53
  • You can listen sockets|pipes in one process without creating new ones with posix poll function. Probably this is what you looking for. http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html – Sergey Vakulenko Jun 16 '12 at 10:57
  • @Artefacto - I am interested in a design of a program where it can handle multiple connections without threading or spawning processes, just one single main process ( event-driven ). The information i have found is limited, that's why i would like to hear some comments from people with more expirience in the field. – iNDicator Jun 16 '12 at 11:00
  • This is an interesting question. I believe that nodsjs and nginx are implemented with the select system call. The select system call can monitor many concurrent connections without spawning new processes. Those select events are then dispatched to the event loop of nodejs (i.e. your javascript callbacks). – Mike76 Nov 10 '19 at 14:10

5 Answers5

20

You definitely must read the following: http://www.kegel.com/c10k.html. That page is the perfect overview of event-driven and asynchronous techniques.

However, a quick & dirty answer: event-driven is neither non-blocking, nor asynchronous.

Event-driven means, that the process will monitor its file descriptors (and sockets), and act only when some event occurs on some descriptor (events are: data received, error, became writeable, ...).

BSD sockets have the "select()" function. When called, the OS will monitor the descriptors, and return to the process as soon as some event on one of the descriptors occurs.

However, the website above has much better descriptions (and details about the different APIs).

Frunsi
  • 7,011
  • 5
  • 33
  • 42
  • Thanks for the link, seems this is what i am looking for ;) – iNDicator Jun 16 '12 at 13:31
  • This answer is **wrong**. "event-driven" is by it's nature asynchronous. Nothing monitors anything. The event itself derives the operations. See my answer for more details. – Graham Aug 14 '16 at 12:53
  • @Graham: event-driven and async are often used synonyms. But (at least in linux world) they are different things. Its disputatious. Read the answers on this question: http://stackoverflow.com/questions/5844955/whats-the-difference-between-event-driven-and-asynchronous-between-epoll-and-a – Frunsi Aug 15 '16 at 15:39
3

"what is the philosophy behind this model"

Event driven means there is no "monitoring", but that the event itself initiates the action.

Usually this is initiated by an interrupt, which is a signal to the system from an external device, or (in the case of a software interrupt) an asynchronous process.

https://en.wikipedia.org/wiki/Interrupt

Further reading seems to be here:

https://docs.oracle.com/cd/E19455-01/806-1017/6jab5di2m/index.html#sockets-40 - "Interrupt-Driven Socket I/O"

Also http://cs.baylor.edu/~donahoo/practical/CSockets/textcode.html has some examples of Interrupt-Driven Sockets, as well as other socket programming examples.

Graham
  • 419
  • 4
  • 6
1

Event driven programming is based on an event loop. The loop simply waits for a new event, dispatches code to handle the event, then loops back to wait for the next event. In the case of sockets, you're talking about "asynchronous network programming". This involves select() or some other option like Kqueue() to wait for the events in the event loop. Sockets would need to be set to non blocking, so that when you read() or write() your code won't wait for the I/O to complete.

Asynchronous network programming can be very complex, and tricky to get right. Check out a couple of introductions here and here. I strongly suggest using a library such as libevent or liboop to get this right.

Matt
  • 1,344
  • 9
  • 15
1

That kind of TCP servers/clients can be implemented by using select(2) call and non-blocking sockets.

It is more tricky to use non-blocking sockets than blocking sockets.

Example:

connect call usually return -1 immediately and set errno EINPROGRESS when non-blocking socket are used. In this case you should use select to wait when connection is opened or failed. connect may also return 0. This can happen if you create connection to the local host. This way you can serve other sockets, while one socket is opening a TCP connection.

SKi
  • 7,243
  • 21
  • 49
0

It's actually very platform specific as to how that works.

If your running on a linux system it's really not to difficult though, you simply need to spawn a copy of your process using 'fork' something like the following would do the trick:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet.h>
#include <signal.h>
#include <unistd.h>

int main()
{
  int server_sockfd, client_sockfd;
  int server_len, client_len;
  struct sockaddr_in server_address;
  struct sockaddr_in client_address;

  server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

  server_address.sin_family = AF_INET;
  server_address.sin_addr.s_addr = htonl(INADDR_ANY);
  server_Address.sin_port = htons(1234);
  server_len = sizeof(server_address);
  bind(server_sockfd, (struct sockaddr *)&server_address, server_len);

  listen(server_sockfd, 5);

  signal(SIGCHLD, SIG_IGN);

  while(1)
  {
    char ch;
    printf("Server Waiting\n");
    client_len = sizeof(client_address);
    client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len)

    // Here's where we do the forking, if you've forked already then this will be the child running, if not then your still the parent task.

    if(fork() == 0)
    {
      // Do what ever the child needs to do with the connected client
      read(client_sockfd, &ch, 1);
      sleep(5); // just for show :-)
      ch++;
      write(client_sockfd, &ch, 1);
      close(client_sockfd);
      exit(0);
    }
    else
    {
      // Parent code here, close and loop for next connection
      close(client_sockfd);
    }
  }
}

You may have to fiddle with that code a little I'm not near a Linux box at the moment to do a test compile, and I've pretty much typed it from memory.

Using fork however is the standard way to do this in C under a Linux / Unix based system.

Under windows it's a very different story, and one which I can't quite remember all the code needed (I'm way to used to coding in C# these days) but setting up the socket is pretty much the same except you need to use the 'Winsock' API for better compatibility.

You can (I believe anyway) still use standard berkley sockets under windows but it's full of pitfalls and holes, for windows winsock this is a good place to start:

http://tangentsoft.net/wskfaq/

As far as I'm aware too, if your using Winsock it has stuff in to help with the spawning and multi client, myself personally however, I usually just spin off a separate thread and copy the socket connection to that, then go back into the loop listening to my server.

shawty
  • 5,447
  • 2
  • 29
  • 66
  • 1
    A very big thank for your example, however here doesn't the fork create a new child process? so if we had 30 clients connected wouldn't we get 30 new processes? – iNDicator Jun 16 '12 at 12:02
  • 1
    Indeed it does create Child processes, but there not full blown processes there partial tasks spun off from the parent that only last the life time of the service. The other way you could do it, is to use Linux PThreads which still spawns a sub process, just not in it's own memory pool as fork does. Vast majority of Linux services use the Fork way of doing things. – shawty Jun 16 '12 at 19:24
  • Well this is what i want to prevent, see Frunsi's post. this one seems to be what i need! not that yours is not useful ;) – iNDicator Jun 16 '12 at 21:53
  • 1
    No worries, and no offence taken. :-) – shawty Jun 17 '12 at 13:13