Programs on different machines need to talk to each other.
You’ve learned how to use I/O to communicate with files and how processes on the same machine can communicate with each other. Now you’re going to reach out to the rest of the world, and learn how to write C programs that can talk to other programs across the network and across the world. By the end of this chapter, you’ll be able to create programs that behave as servers and programs that behave as clients.
C is used to write most of the low-level networking code on the Internet. Most networked applications need two separate programs: a server and a client.
You’re going to build a server in C that tells jokes over the Internet. You’ll be able to start the server on one machine like this:
Other than telling you it’s running, the server won’t display
anything else on the screen. However, if you open a second console,
you’ll be able to connect to the server using a client program called
telnet. Telnet takes two parameters:
the address of the server, and the
port the server is running on. If you are running
telnet on the same machine as the server, you can use 127.0.0.1
for the address:
You’ll be using telnet quite a lot in this chapter to test our server code.
If you try to use the built-in Windows telnet, you might have problems because of the way it communicates with the network. If you install the Cygwin version of telnet, you should be fine.
Do this!
You will need a telnet program in order to connect to the server. Most systems come with telnet already installed. You can check that you have telnet by typing: |
|
on the command line. |
If you don’t have telnet, you can install it in one of these ways: |
Cygwin: |
Run the |
Linux: |
Search for telnet in your package manager. On many systems, the package manager is called Synaptic. |
Mac: |
If you don’t have telnet, you can install it from www.macports.org or www.finkproject.org. |
The server will be able to talk to several clients at once. The client and the server will have a structured conversation called a protocol. There are different protocols used on the Internet. Some of them are low-level protocols, like the internet protocol (IP), which are used to control how binary 1s and 0s are sent around the Internet. Other protocols are high-level protocols, like the hypertext transfer protocol (HTTP), which controls how web browsers talk to web servers. The joke server is going to use a custom high-level protocol called the Internet knock-knock protocol (IKKP).
A protocol is a structured conversation.
The client and the server will exchange messages like this:
A protocol always has a strict set of rules. As long as the client and the server both follow those rules, everything is fine. But if one of them breaks the rules, the conversation usually stops pretty abruptly.
When C programs need to talk to the outside world, they use data streams to read and write bytes. You’ve used data streams that are connected to the files or Standard Input and Output. But if you’re going to write a program to talk to the network, you need a new kind of data stream called a socket.
Before a server can use a socket to talk to a client program, it needs to go through four stages that you can remember with the acronym BLAB: Bind, Listen, Accept, Begin.
Bind to a port.
Listen.
Accept a connection.
Begin talking.
A computer might need to run several server programs at once. It might be sending out web pages, posting email, and running a chat server all at the same time. To prevent the different conversations from getting confused, each server uses a different port. A port is just like a channel on a TV. Different ports are used for different network services, just like different channels are used for different content.
When a server starts up, it needs to tell the operating system
which port it’s going to use. This is called binding the port. The knock-knock server
is going to use port 30000, and to bind it you’ll need two things: the
socket descriptor and a socket name. A socket name is just a struct
that means “Internet port
30000.”
If your server becomes popular, you’ll probably get lots
of clients connecting to it at once. Would you like the clients to
wait in a queue for a connection? The listen()
system call tells the operating
system how long you want the queue to be:
Calling listen()
with a queue
length of 10 means that up to 10 clients can try to connect to the
server at once. They won’t all be immediately answered, but they’ll be
able to wait. The 11th client will be told the server is too
busy.
Once you’ve bound a port and set up a listen queue, you then
just have to...wait. Servers spend most of their lives waiting for
clients to contact them. The accept()
system call waits until a client
contacts the server, and then it returns a second socket descriptor that you can use to
hold a conversation on.
This new connection
descriptor ( connect_d
)
is the one that the server will use to...
Begin talking.
So far, data streams have all been the same. Whether
you’re connected to files or Standard Input/Output, you’ve been able to
use functions like fprintf()
and
fscanf()
to talk to them. But sockets
are a little different. A socket is two way: it can
be used for input and output. That means it needs
different functions to talk to it.
If you want to output data on a socket, you can’t use fprintf()
. Instead, you use a function called
send()
:
Remember: it’s important to
always check the return value of system calls like send()
. Network errors are really common, and
your servers will have to cope with them.
What port should I use?
You need to be careful when you choose a port number for a server application. There are lots of different servers available, and you need to make sure you don’t use a port number that’s normally used for some other program. On Cygwin and most Unix-style machines, you’ll find a file called /etc/services that lists the ports used by most of the common servers. When you choose a port, make sure there isn’t another application that already uses the same one.
Port numbers can be between 0 and 65535, and you need to decide whether you want to use a low number (< 1024) or a high one. Port numbers that are lower than 1024 are usually only available to the superuser or administrator on most systems. This is because the low port numbers are reserved for well-known services, like web servers and email servers. Operating systems restrict these ports to administrators only, to prevent ordinary users from starting unwanted services.
Most of the time, you’ll probably want to use a port number greater than 1024.
The server looks like it’s starting correctly the second time, but the client can’t get any response from it. Why is that?
Remember that the code was written without any error checking. Let’s add a little error check into the code and see if we can figure out what’s happening.
If you add an error check on the line that binds the socket to a port:
Then you’ll get a little more information from the server if it is stopped and restarted quickly:
If the server has responded to a client and then gets stopped and restarted, the call to the bind system call fails. But because the original version of the program never checked for errors, the rest of the server code ran even though it couldn’t use the server port.
When you bind a socket to a port, the operating system will prevent anything else from rebinding to it for the next 30 seconds or so, and that includes the program that bound the port in the first place. To get around the problem, you just need to set an option on the socket before you bind it:
ALWAYS check for errors on system calls.
This code makes the socket reuse the port when it’s bound. That means you can stop and restart the server and there will be no errors when you bind the port a second time.
You’ve learned how to send data to the client, but what
about reading from the client? In the same way that
sockets have a special send()
function to write data, they also have a
recv()
function to read
data.
<bytes read> = recv(<descriptor>, <buffer>, <bytes to read>, 0);
If someone types in a line of text into a client and hits return,
the recv()
function stores the text
into a character array like this:
There are a few things to remember:
The characters are not terminated with a character.
When someone types text in telnet, the string always ends .
The recv() will return the number of characters, or –1 if there’s an error, or 0 if the client has closed the connection.
You’re not guaranteed to receive all the characters in a single call to recv().
This last point is important. It means you might have to call
recv()
more than once:
That means recv()
can be tricky
to use. It’s best to wrap recv()
in a
function that stores a simple