The last few chapters have explained how to write a program that acts as a client to an existing server program. Now, we will begin exploring the other side of the connection, the server-side.
Usually the role of a client program is to initiate the network connection, to request services from the server, and then to terminate the connection. Most of the time, a client program becomes active when it is run by a user, and is deactivated when it is done processing the data it received from the server, or when the user tells it to deactivate.
In contrast, a server program usually stays active at all times. It does not initiate any activity on its own, but rather waits for connections from client programs. When a client program has connected, it waits for the client program to make requests. When the server program realizes that the client program is done, it waits for the next connection.
There are minor differences in which API calls you need to make when writing a server program versus writing a client program. You still need to call socket() to create a socket to use for communications, but instead of issuing a connect() call, you will listen() for connections, and then accept() the connections as they come in.
It's also important to understand that a client program has to know where the server program is listening for connections. (pause to let that sink in) In other words, when a client issues a connect(), it has to tell the API which address & port to connect to -- consequently, we have to make sure that the server is, in fact, listening on the port & address that the client expects it to be listening on. This is called 'binding' to a port.
In addition to the differences in which API is called, there are a few other considerations that make server programs different from client programs:
Server programs usually have to be capable of handling many client connections at once. Generally speaking, this isn't true of clients. This creates some significant hurdles to overcome when trying to write code.
Because a server is available for connections at all times, the security of a server-side program is usually a much bigger concern. You have to be security conscious when writing a server program.
For the same reasons, server programs frequently need to validate a user-id and password of the person connecting, and be careful to only give them access to what the security officer has deemed that a user should be able to access.
Server programs usually aren't interactive applications. They have no screen associated with them, besides the one that might be found on the client side. Therefore, they tend to be a little trickier to debug.
The basic model for a server program looks something like this:
Call the getservbyname() API to find out the port number for the service you want to be a server for.
Call the socket() API to create a new socket.
Call the bind() API to bind the socket to the port that we found in step #1.
Call the listen() API to tell the system that you want to listen for connections with this socket. (This "opens up" the port so that people can connect to it)
Call the accept() API. The accept() API will wait until a client connects to the port, and then will create a new socket. The new socket will already be connected to the client.
Here we process the newly connected client. Depending on whether we're only handling one connection at a time, or what model we're using to handle many connections, we'll do things very differently at this point.
Close the socket you got from accept()
Go back to step 5.