Advertisement

sys/socket.h and std::thread (C__)

Started by February 04, 2018 03:04 PM
8 comments, last by hplus0603 6 years, 9 months ago

Basically i found a problem, i didin't check it out but it seems its already there.

listen() function holds up the thread in which is executed, so how i am supposed to receive any commands, it listens then when client connects, i do what, do i need to create a thread that calls listen() and awaits for clients, then i add newly connected client to a client list (in my server class), and call listen() again to check whenever another clients awaits connection? so i manage receiving packets from another spawned thread? is that correct?

listen() is usually not blocking. Also, you only call listen() once, to specify the backlog (queue) size and mark the socket as listening.

accept() can be blocking, unless you have opened the socket using O_NONBLOCK, or you have seen that the socket is readable using select() or epoll() or a similar API.

Whether you use select(), epoll(), non-blocking sockets, or some similar mechanism is up to what your preference is. select() is traditional, and works great up to 64 sockets on Windows, or 1024 sockets on Linux. epoll() can be more efficient. non-blocking works better for games that have a main loop that uses something other than select() time-outs for timing.

enum Bool { True, False, FileNotFound };
Advertisement

Well, yes, you could use blocking sockets and have a thread listen on the socket, accepting client connections as they come, and go back to blocking on the accept call.  That's all it would do.  It's a very sensible way to program sockets and easy to reason about.

You could also use non-blocking sockets and use a multiplexor like select/poll/epoll/kqueue to combine all of your socket waits onto a single thread, and dispatch them via callbacks to a worker thread pool.  Again, not too difficult to reason about, but targeted towards a POSIX I/O model rather than a Windows I/O model.

If you're using an Windows-based system, you could use IOCP and overlapped I/O to handle everything, including dispatching commands to a thread queue if you really want.

Stephen M. Webb
Professional Free Software Developer


listen() is usually not blocking. (ah usually ;x) Also, you only call listen() once, to specify the backlog (queue) size and mark the socket as listening.

im asking because according to this:

http://www.linuxhowtos.org/data/6/server.c

when listen is called it seems its blocking main thread and waits for connections. Thats why im asking.

so actually i must call listen once

 

and call repeatedly accept() in a thread. - thats what i wanted to know,

however its time to run tests, anyway i couldnt find any info if listen actually holds the thread or not so either it does or not, usually doesnt seem to be a an answer, i need something that either blocks or not ;]

listen() is a system call:  it will block the thread briefly while it switches to the kernel and performs some setup operations.  It gets called once (per server socket, so generally once per application) during startup, and is fairly resource-lean and fast.

You call accept() to wait for connection attempts on a socket that you have previously called listen() on.  The accept() call is also a system call (requires a switch to kernel mode to perform its operations, which means it steps out of the ready-thread queue and go on the IO-wait queue), and whether it performs a blocking wait on the socket or not depends on if the socket is in blocking mode or not.

Stephen M. Webb
Professional Free Software Developer

On 2/5/2018 at 3:31 AM, Cat's machete said:

i couldnt find any info if listen actually holds the thread or not so either it does or not, usually doesnt seem to be a an answer, i need something that either blocks or not ;]

The listen() call is not blocking.  It calls into the OS briefly like all other system calls, but it doesn't block.

The accept() call is potentially blocking based on the socket options.  If the socket is marked as non-blocking the accept() call will immediately return EWOULDBLOCK or EAGAIN if it would have blocked.

If you set your sockets to non-blocking mode then you will need to use accept() periodically to test for new inbound connections.  Otherwise, yes, call accept() once and it will block until something connects or there is an error condition.

 

Advertisement

listen() should be called once to tell the socket that it is listening for incoming connections. The accept() call is the one that should really be placed onto a separate thread. It's been a while since i worked in the linux sockets library. I went to boost asio after realizing the pain it is to make write socket server cross platform. But if my memory serves me right, you want a system similar to this pseudo code:

 

edit: on second thought, i don't think accept needs to be on its own thread, i'm pretty sure that's your main server loop to keep the application running


 


void StartServer()
{
	socket;
    socket.bind(endpoint) // Binds the lisetning socket to a port
    socket.listen() // Sets the socket in listen mode
    
    while(true)
    {
       Client* client_connection = new Client(socket.accept()) // client is some class that accepts a socket and handles recv and send calls
       
       // Start the client's receiving process, this should spin off a new thread so the client can receive messages while the server continues to accept
       client_connection->BeginRecv()
       
       //move client_connection to a list of connections
       connections.append(client_connection)
    }
}

 

Especially if you plan to make this cross-platform (but even if you don't), I highly recommend delegating the low level details of network I/O to a suitable abstraction. Boost's asio is solid, if a little intrusive in its programming model. LibUV/LibEV/LibEvent are pretty lightweight, and handle things like adapting to IOCP on Windows as well.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

One thread per client is terrible. Please don't do that. It's much better to use basic select(). Or, if you need real threading, use kpoll or IOCP or something like it (or a wrapper like libevent or ASIO.)

 

enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement