Babra Cunningham
Babra Cunningham

Reputation: 2967

Sys/socket concurrency for non-blocking?

I have a simple socket server set up using sys/socket and OpenSSL. For each connection, the client is required to send a message to the server, receive a response and then reply to that response.

I can't find any clear mechanism for making these sockets non-blocking? The system has to be able to handle multiple sockets concurrently...

My server code for listening for connections:

while(1)
{
   struct sockaddr_in addr;
   uint len = sizeof(addr);
   SSL *ssl;            
   int client = accept(sock, (struct sockaddr*)&addr, &len);
   if (client > 0) 
   {
      std::cout<<"Client accepted..."<<std::endl;
   }
   else
   {
      perror("Unable to accept");
      exit(EXIT_FAILURE);
   }

   ssl = SSL_new(ctx); 
   SSL_set_fd(ssl, client);

   if (SSL_accept(ssl) <= 0)
   {
      std::cout<<"ERROR"<<std::endl;
   }
   else
   {
      char buff[1024];
      SSL_read(ssl, buff, 1024);
      std::cout<<buff<<std::endl;

      std::string reply="Thanks from the server";
      char buff_response[1024];
      reply.copy(buff_response, 1024);
      const void *buf=&buff_response;
      SSL_write(ssl, buf, 1024);

      char another_buff[1024];
      SSL_read(ssl,another_buff,1024);
      std::cout<<another_buff<<std::endl;
   }
}

I've looked into 'select()', however this doesn't seem to allow concurrency as such, but allows the system to know when a socket is freed?

Does anyone have any experience in solving this basic problem?

Upvotes: 1

Views: 712

Answers (1)

Ami Tavory
Ami Tavory

Reputation: 76297

First, with server code, it's important to differentiate between concurrency and parallelism. A reasonable server will typically handle many more connections concurrently than its number of cores. Consequently, it's important to make the code concurrent in the sense that it can (efficiently) handle many concurrent connections, in a way that does not rely on parallelism (in the sense of having each connection handled by a thread).

In this sense, select is actually a reasonable choice for concurrency, and gives you the effect of being non-blocking.

When your system handles multiple sockets concurrently, select indicates on which socket(s) you can perform operations such as send and recv without their blocking when you do so. If you use select well you won't have cases where your thread is idling, waiting indefinitely for some operation to proceed, while other sockets are ready.

The minimal example from gnu.org shows a reasonably efficient server which it seems you can adapt to your needs.

fd_set active_fd_set, read_fd_set;
FD_ZERO (&active_fd_set);
FD_ZERO (&read_fd_set);

// Use FD_SET to add sockets according to what you want to do with them

/* This call (checking to see who can be read) is the 
* only thing that blocks. But if it does, no socket is ready for reading. */
if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0) {
    // Handle error;

for (i = 0; i < FD_SETSIZE; ++i)
    if (FD_ISSET (i, &read_fd_set))
        // Here you can read without its blocking.

Upvotes: 1

Related Questions