Prequisites for this tutorial is knowledge in programming (lanugage C), basics about threading (see previous tutorials) and basic knowledge of sockets. Sockets not in detail, but the mechanism in general.
This tutorial is intended to show you how to use threads in more than a small example of synchronisation. Client/Server-Systems are very useful if you like to provide a service, reachable in the entire network. This is what many of the deamons do.
Usually, those services are programmed using the fork command, this means multi-processing (in the terms of having multiple processes and not multi processors). The process handling is much more resource consuming than the handling of threads, the complexity is about the same. The advantage of being less resource consuming has an important disadvantage. If one thread (handling one connection) dumps the core, it will affect the other threads as well.
First, we'll have a look at the client and afterwards at the server. I choose this order because all the threading is in the server part and the server part is also more complex. So let's do the boring part first.
The client program is designed to take three commandline paramters:
First several headers to include.
1 2 3
The function main is the only one we'll need within the client program. But we'll need some variables.
6 7 8 9 10 11 12
Since the client program should take several commandline parameter, we have to check them. If they are not in a proper form, let's print the usage of the client program.
14 15 16 17 18 19
The number of parameters was ok, let's try to parse the port number. If it does not work the client program must stop.
21 22 23 24 25 26
So far, so good. Now, we're ready to handle the socket. First we have to create one, a TCP socket for streaming data and for the Internet domain.
28 29 30 31 32 33 34
Next, we have to deal with the address of the server. The function gethostbyname does the trick for us:
36 37 38 39 40 41 42 43 44 45
Ok, we have now all we need: socket, address to host. We're able to connect to the server:
46 47 48 49 50
If the program reaches this far, a connection from the client machine to the server machine is now open (even they are on the same machine).
Next action is to send the message to the server. First we send the length of the message, letting the server know what's going on, and then the message itself. This is very easy because we can use the well known write statement.
52 53 54 55
All what's left to do is to close the socket and quit the program.
57 58 59 60 61
This client program was quite simple, wasn't it?
The server program is more complex than the client program, because here sits the entire handling of multiple connections, using threads of course.
Don't be afraid, this sounds more complex than it really is.
The server program takes one parameter: the port number.
The first thing we need is the inclusion of a few header files:
1 2 3 4
For a better handling of the connection context, let's define a data structure:
1 2 3 4 5 6
This data structure holds three varialbes:
sock: the socket of the connection to one client
address: the address of a connected client
addr_len: the length of the address field
Those are all available information about one connected client.
It is not a good design to handle all the clients in one function (
main). So, let's do the work in a separate function. This comes also in handy, because of the thread handling. Remember: we'll need a function with the following signature:
to do the threads work.
13 14 15 16 17 18
After the declaration of some local variables, we need to check the specified parameter and cast it to our desired data type:
The real work begins now. Maybe you have a look at the client code. The first thing that was submitted was the length of the message as an integer. As the server, we need to read this number from the socket.
23 24 25 26
If the length of the message tells us the message is worth to be processed, we'll have to allocate some memory (for the buffer). The first statement is just for fun: obtaining the client's IP address for further use.
27 28 29
Reading the message is again a very simple task. Just use the function read to read the message from the socket.
Print the message out to stdout and free the allocated memory for the buffer.
34 35 36 37 38 39 40 41 42
Since the great show is over, we have to do some clean up work. Closing the socket, freeing the memory of the conenction context and ending the thread.
44 45 46 47 48
In the method above we had a look at the real work: reading the message from the socket and processing it (in this case: write to stdout).
To make the server program work, we need to do some initialisation work of socket and threads. This is done within the function main. We already saw a few things in the client code.
50 51 52 53 54 55 56
The commandline parameter processing hasn't to be discussed again. If you're not sure about this, have a second look at the client code.
58 59 60 61 62 63 64 65 66 67 68 69 70
The creation of the socket is also simple. We saw this in the client code as well.
72 73 74 75 76 77 78
One new thing is that we have to bind the created socket to the specified port. This is done using the function bind. Please have a look at the man pages if you have questions about bind:
$ man 2 bind.
80 81 82 83 84 85 86 87 88
Now we have to specify that we are a server that likes to listen on the port. The function listen is doing exactly this. For more information:
$ man 2 listen
90 91 92 93 94 95
A simple status message to tell that the server is now up and ready to receive connections from clients.
Now we have done all necessary initialisation work. The main loop is straight ahead. Actually it does only three things: creating a connection context (connection), accepting connections from clients (blocking IO) and starting threads with the proper connection context, that all.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
Althrough this code is never reached it's for a good design, and it let's the compiler think: everything is ok, no warnings needed.
It wasn't too complex, wasn't it? Most of the code is handling of exceptions. If you reduce the real work (without bells and whistles), all that would be left are a few lines of code. I like you to notice one further thing: the handling of the threads is done in three (!) lines of code: creating the thread (
pthread_create), detaching it (
pthread_detatch) and correct termination of the thread (
pthread_exit). The handling of the socket was much more complex.
To compile and run the code provided by this exammple, you'll need Linux, gcc and the pthread package. Small changes will make it run with Cygwin (an Unix environment for Win32, external link).
To compile use the following sequence of commands:
$ gcc -o server server.c -lpthread $ gcc -o client client.c
To run the example you should start a new terminal. In one terminal you start the server, providing a port number on which the server should listen:
$ ./server 5678
On the other terminal run the client:
$ ./client localhost 5678 "Hello World"
It is also possible to run the client on another machine:
$ ./client host 5678 "Hello World"
With host as name or IP address of the machine on which the server program is listening.
If you like a usage of each program just type:
$ ./server $ ./client
All source code files provided by this page is free to copy, modify, redistribute and use for any purpose. The tutorial is copyrighted by Mario Konrad.