-1

On my TCP server I would like to have:

  1. non-blocking passive socket to have non-blocking accept();

  2. after accepting connection I would like to perform some authentication like verifying client provided Id and Password. So I have well defined protocol and I would like to have blocking recv()/send() talk between TCP server and client via connection socket.

  3. After client identity verification I would like to have non-blocking connection socket to enable server shutdown from external thread.

The problem is that when I firstly set non-blocking PASSIVE socket then accepted CONNECTION sockets are also non-blocking? Why aren't they separate sockets?

I have set passive socket to non-blocking mode using this code:

fcntl(ps_fd, F_SETFL, O_NONBLOCK);

I do authentication through connection socket:

if((n_recv = recv(sock_fd, buf, sizeof(buf) - 1, 0)) <= 0) { ... }

But here recv() doesn't block and client cannot deliver it's authentication id and password on the time before EAGAIN error.

Can I revert connection socket to be in blocking mode again, and passive socket left non-blocking?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Michał Ziobro
  • 10,759
  • 11
  • 88
  • 143
  • This sounds very wrong ... don't block your whole service on a single client! Instead, use some context and a state machine. Also suggest `select()` (or something platform-specific like `epoll()`) instead of `O_NONBLOCK` –  Jun 19 '17 at 15:40
  • Yeah but they seems to inherit state from passive sockets and doesn't block :( – Michał Ziobro Jun 19 '17 at 15:41
  • 1
    So put them into blocking mode. What's stopping you? – user207421 Jun 19 '17 at 17:03

2 Answers2

1

The blocking-status is not inherited by accepted sockets in Linux. On BSD-derived systems (like macOS and probably Windows (though I can't find anything specified)) the non-blocking is inherited.

One solution is of course to make the accepted socket blocking again, and then non-blocking once the authentication phase is done. This will block the rest of the program if you're single-threaded, meaning you can't accept other connections while one user is authenticating.

Another possible solution is to use threads, or even processes, to handle connections.

Or you could use some polling like select, poll or if you're on Linux use epoll (or corresponding variants on BSD systems) to know when there is data to be received on the accepted socket. For this to work you could use a simple state-machine, where you have states like WAITING_FOR_USERNAME, WAITING_FOR_PASSWORD and LOGGED_IN etc.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
1

I have tried such solution and it seems to work.

For me on OS X connection socket received from accept() on passive socket inherits its non-blocking mode. So after accept I am changing the connection socket mode to blocking one again.

Example code

    fcntl(ps_fd, F_SETFL, O_NONBLOCK);

    int cs_fd = accept(ps_fd); 
    // revert connection socket to non-blocking
    int opts = fcntl(cs_fd, F_GETFL);
    opts = opts & (~O_NONBLOCK);
    fcntl(cs_fd, F_SETFL, opts);

    // then authentication via cs_fd

    // after authentication change it to non-blocking again 
    fcntl(cs_fd, F_SETFL, O_NONBLOCK);
Michał Ziobro
  • 10,759
  • 11
  • 88
  • 143