To begin with, I know there are ways to handle multiple client requests by forking or threading. But I cannot understand why there cannot be multiple acceptance by the server without forking or threading. accept() call can simply accept all process wish to connect to it. Why cannot the the call(accept()) go on unless a client cut its connection??
server does socket(), listen() and bind() with blocking(default) way
client does likewise by default socket() and connect()
What I think is accept's returned value will be for the recent child. But in reality it blocks until the prior client(s) cut its connection.
I wonder whether there is file-descriptor which is returned by accept() overwriting? If not, how?
There is no overwriting; accept() creates a new connected socket, and returns a new file descriptor referring to that socket - a new, distinct one each time. Of course, a server which manages all client connections without creating other threads must store all those file descriptors, e. g. in an array.
Related
I am implementing a reliable, connection-oriented application over UDP (uni assignment). It is required that the server, after it receives an initial packet from the client and acknowledges it, creates a new worker process. The worker process shall have a socket dedicated to this particular client.
Now I see three ways of implementing this.
The server has a socket, let's call it listener_socket, which is bound to the specified port and waits for requests. I could, then, in the child process, connect() this very socket to the client's address.
Or, I could close listener_socket in the child altogether, then open a brand new socket connection_socket, bind it to the port and connect it to the client.
Or, open a new socket on a new port and code the client to deal with this new port for the remaining duration of the connection instead.
What I am uncertain about (regarding option 1) is how connecting the listener_socket to the client affect the original listener_socket in the parent server. Will it prevent the parent from receiving further messages from other clients? If not, why? Don't they both refer ultimately to the same socket?
As for option 2, this gives me, quite expectedly, "address already in use". Is there some functionality like in routers (i.e. longest matching prefix) that delivers datagrams to the "most fitting" socket? (Well, TCP already does this in accept(), so it's quite unlikely that this logic could be replicated for UDP.)
Regarding option 3, I think it's quite inefficient, because it implies that I should use a new port for each client.
So could someone please advise on which method to use, or if there is another way I'm not yet aware of?
Thanks.
What I am uncertain about (regarding option 1) is how connecting the listener_socket to the client affect the original listener_socket in the parent server. Will it prevent the parent from receiving further messages from other clients?
Yes.
If not, why? Don't they both refer ultimately to the same socket?
Yes, when the parent forks the child, the child receives copies of all the parent's open file descriptors, and these refer to open file descriptions managed by the kernel. Those open file descriptions are thus shared between parent and child. Therefore, if the child connect()s the socket to a client, then both parent and child will see the effect that
If the socket sockfd is of type SOCK_DGRAM, then [the specified address] is the address to which datagrams are sent by default, and the only address from which datagrams are received.
(Linux manual page; emphasis added)
As for option 2, this gives me, quite expectedly, "address already in use". Is there some functionality like in routers (i.e. longest matching prefix) that delivers datagrams to the "most fitting" socket?
What would make one socket more fitting than the other? In any case, no. A UDP endpoint is identified by address and port alone.
Regarding option 3, I think it's quite inefficient, because it implies that I should use a new port for each client.
Inefficient relative to what? Yes, your option (3) would require designating a separate port for each client, but you haven't presented any other viable alternatives.
But that's not to say that there aren't other viable alternatives. If you don't want to negotiate a separate port and open a separate socket per client (which is one of the ways that FTP can operate, for example) then you cannot rely on per-client UDP sockets. In that case, all incoming traffic to the service must go to the same socket. But you can have the process receiving messages on that socket dispatch each one to an appropriate cooperating process, based on the message's source address and port. And you should be able to have those processes all send responses via the same socket.
There are numerous ways that the details of such a system could be set up. They all do have the limitation that the one process receiving all the messages could be a bottleneck, but in practice, we're talking about I/O. It is very likely that the main bottleneck would be at the network level, not in the receiving process.
If the socket in the child process after a fork is the same as the one in the parent, then they both reflect the same kernel socket. Since binding and connecting is done on the kernel socket any changes to the child socket will thus affect the parent socket too. It is therefore necessary to create a new (independent) connected socket.
The socket needs to bound to the same local address and port as the listener socket though, since this is where the peer expects packets to come from to keep the same UDP association.
I'm making a concurrent server/client program in C using threads. Whenever a client connects, I create a new thread to handle it.
My problem is: I want to be able to close the server, from the client. With the command '..' for example. When I type '..' in the client, I want the server to close immediately.
I thought about having a global variable, that indicates wether the server should close or not. The problem is: When the thread is created to handle the client, the main thread goes back to accept(), and it cannot check that variable. So, it will only close when a new client connects.
Any ideas on how to solve this?
Thanks!
Use select() or (e)poll() or equivalent to wait for a client to connect BEFORE you then call accept() to accept the connection. Those kind of functions allow you to specify a timeout, that will allow you to stop waiting periodically to check for other conditions, like a shutdown request. On some platforms, you can even have these functions wait on not only the listening socket but also a separate pipe that you create privately for yourself, and when you want to "wake up" your waiting loop to do something, simply write a byte into that pipe, and when the loop detects that byte arriving then it can act accordingly.
I am working on TCP/IP project (Server, and many clients). I wrote code completely in C. I have observed that whenever a new client connects to the server, function accept() returns a number for that particular connection.
newton = accept(mysocket, (struct sockaddr *)&dest, &socksize)
newcon in the above code is different for different clients. Whenever a client disconnects, the connection number which was assigned to that disconnected client is gone. That number is not going to get assigned to another client at all. if the server has many users then clients connect and disconnect lot of times. so the number go on increasing (from 0 to bigger number). How should I deal with this if the server has millions of clients connected? Is there any way that I can reuse the connection number again?
You misunderstand the return value of accept. It is not a number of connections, but a socket descriptor. (Which happens to be file descriptor, which is integer, on *nix platforms).
The same number can be returned again if one of the previously assigned sockets is closed - so, in order to maintain your scalability, make sure you close your sockets after they are no longer needed - which is a proper thing to do anyways.
Test it.
Create an array of integers, say 10000 in size. When you accept a connection, push the fd value returned into that array. Then at the end, sort the array and look for duplicates. You should see duplicates, as others have alluded to.
The server side implementation of a TCP connection is designed to support multiple concurrent connections. This behavior is achieved using the accept() system call which is invoked with the server socket as parameter. When a new connection requests arrives, the system creates a new socket to handle this new connection and this socket descriptor is returned by the accept system call. Subsequently the communication on this new connection is handled using this socket descriptor returned by accept. When the communication with client is completed, the connection is closed and this socket descriptor is returned to the system. The system can reuse the same descriptor again to handle a new client request again.
It should be noted that once the connection is closed, in certain conditions the connection moves to TIME_WAIT state and the socket is not re-used when the connection state is TIME_WAIT. The socket becomes free after the TIME_WAIT state. So this is quite possible that when one connection is assumed to be closed, it can actually be in TIME_WAIT state. This can be verified using 'netstat -anp' command.
Socket descriptors are system resources and each system has an upper limit on maximum number of open descriptors. Once that limit is reached, the system does not accept new connections. So if a client-server system has been implemented in such a way that there are not large number of concurrent TCP connections, the limit of maximum number of open descriptors is not hit and server will continue to server the clients. If the client-server system requires large number of persistent connections then definitely the limit of open descriptors can be a problem and will need to be increased.
accept returns a descriptor. You could implement a data structure, where you could store the descriptors and manipulate them. But it depends on the use case and your implementation and what you are going to do with them.
I've been reading this tutorial to learn about socket programming. It seems that the listen() and accept() system calls both do the same thing, which is block and wait for a client to connect to the socket that was created with the socket() system call. Why do you need two separate steps for this? Why not just use one system call?
By the way, I have googled this question and found similar questions, but none of the answers were satisfactory. For example, one of them said that accept() creates the socket, which makes no sense, since I know that the socket is created by socket().
The listen() function basically sets a flag in the internal socket structure marking the socket as a passive listening socket, one that you can call accept on. It opens the bound port so the socket can then start receiving connections from clients.
The accept() function asks a listening socket to accept the next incoming connection and return a socket descriptor for that connection. So, in a sense, accept() does create a socket, just not the one you use to listen() for incoming connections on.
It is all part of the historic setup. listen prepares socket for the next accept call. Listen also allows one to setup the backlog - the number of connections which will be accepted by the system, and than put to wait until your program can really accept them. Everything which comes after the backlog is full well be rejected by the system right away. listen never blocks, while accept will block (unless the socket is in non-blocking mode) until the next connection comes along. Obviously, this does not have to be two separate functions - it is conceivable that accept() function could do everything listen does.
The above two answers clearly state the difference between accept and listen. To answer your other question - why we need two separate functions?
One use case is, for e.g. if you only want to test if a port is still available/and accessible, you can do so by just listening to the port and then closing it without accepting any connections.
For e.g. https://github.com/coolaj86/golang-test-port uses the listen call for testing a port's availability.
listen() uses a backlog parameter which specifies the maximum number of queued connections and should be at least 0. It's value increases as the server receives a lot of connection requests simultaneously.
accept() waits for incoming connections. When a client connects, it returns a new socket object representing the connection.
Another imperative thing to note is that accept() creates a new socket object which will be used to communicate with the client. It is different from the listening socket that server uses to accept new connections.
Maximum number of sockets allowed per each connection between an application and the TCP/IP sockets interface is 65535.
Is it possible for me to accept a connection and have it die withouit my knowing, then accept another connection on the same socket number?
I've got a thread to do protocol parsing and response creation. I've got another thread to handle all my network IO and one more thread to handle new incomcing connection requests. That makes three threads total. Using select in the IO thread, I get a failure and have to search for the dead socket. I am afraid there is the case that accept might want to accept a new connection on a socket number that was previous dead.
I'd assume this can't happen until I "shutdown() || close();" the socket that may be dead on the server side. If it could happen, is the only solution to setup mutexes to halt everything while I sort out what sockets have gone bonkers?
Thanks,
Chenz
A socket descriptor wont get reused until you close it.
Assuming we're talking TCP, then if the remote side closes its send side of the connection then you'll get a recv() returning 0 bytes to tell you of this. Since TCP support half closed connections you could still be able to send data to the remote side of the connection (if your application level protocol is made that way) or you might take the fact that the remote side has closed its send side as an indication that you should do the same.
You use shutdown() to close either your send side or your recv side or both sides of the connection. You use close() to close the socket and release the handle/descriptor for reuse.
So, in answer to your question. No, you wont be able to accept another connection with the same socket descriptor until you call close() on the descriptor that you already have.
You MAY accept a connection on a new socket descriptor; but that's probably not a problem for you.