I have just made my way through chapter 2. It had a few hiccups but was overall well done.
This chapters introduces you to a simple Client and Server communication process. The client program can send text to the server. The server sends the text back.
The client code seems to work fine. I am a big fan of showing the absolute minimum the first time and not including anything else that may get in the way. However, they didn’t do the minimal, (though don’t worry, they were very close to the minimal). They also added two files, so to compile the following files are needed:
Personally, I have a hard time using code that I don’t understand, so this made me deviate from my purpose of learning sockets to see what is in these other files. Of course, the book give brief explanations too. These files were small and easy to understand.
Practical.h just declares a bunch of functions. However the implementation of these functions are if different .c files.
DieWithMessage.c provides the implementation of two of these functions, DieWithUserMessage and DieWithSystemMessage. These functions are little more than wrappers to output error messages to standard error.
To compile the client code on FreeBSD 8, I did this:
- Put the following files in the same directory: /home/jared/Devel/Echo/echoclient
- Change to that directory.
- Run gcc as follows:
gcc TCPEchoClient4.c DieWithMessage.c -o echoclient
The book includes information about a web site where the code from the book can be downloaded. This web site is here:
When I downloaded the code from there, the compiling of the client code worked first time and the echoclient works.
When I typed the code in myself (which I like to do because I think it helps me learn better to actually have to type all the code), I had some typos that the compiler errored on and I fixed them.
Server Code (IPv4)
My experience with this first example of server code was…well, lets just say it was confidence building.
The server code also had other files. Not only did it have the same extra files the client code had, but it required even more files:
We already know what Practical.h and DieWithMessage.c do.
TCPServerUtility.c is similar to DieWithMessage.c in that it provides implementation of functions declared in Practical.h, however, it is not the same in that it is not as short of a file with over 100 lines. It is not as complex as it looks because TCPServerUtility.c provides implementation for only three functions: SetupTCPServerSocket, AcceptTCPConnection, and HandleTCPConnection.
AddressUtility.c provides the implementation to functions declared in Practical.h as well. The following two functions are implemented: PrintSocketAddress, SockAddrsEqual.
I put these files in the same directory and tried to compile just like I did with the client code. However, there was a huge problem when I tried to compile. I got a long lists of errors.
This was not comforting. I really don’t like to debug sample code but here it goes. In the past, I probably would have stared at these errors and maybe I wouldn’t have had the skills to solve them. However, I was undaunted and resolved these. Let me explain how I resolved them.
- TCPServerUtility.c:17: error: ‘IPPROTO_TCP’ undeclared (first use in this function)
This first error is a problem in TCPServerUtility.c with IPPROTO_TCP variable being undeclared. So I had to figure out where IPPROTO_TCP was declared. It is declared in netinet/in.h, which for some reason is not included. Maybe it only needs to be included on FreeBSD or maybe this is a bug in the Author’s code. Later, I will send the author a link to this post and let him comment on it. For now, I will add this line to the top of the TCPServerUtility.c.
- TCPServerUtility.c:25: error: ‘for’ loop initial declaration used outside C99 mode
This second error can easily be resolved by compiling with C99 mode enabled. This can be added to gcc as a paramter -std=c99.
- AddressUtility.c:16: error: dereferencing pointer to incomplete type
This is not super clear to me at first. However, the line isn’t doing much. So why is the type incomplete? Maybe the struct is not declared yet because again, a file that should be included is not. The struct being called is sockaddr_in and it is defined in netinet/in.h but sure enough, netinet/in.h is not included in AddressUtility.c, so lets add it.
- AddressUtility.c:17: error: ‘AF_INET’ undeclared (first use in this function)
This error in AddressUtility.c is similar to the first error in TCPServerUtility.c. There is probably a header file missing. Again, this may be only a FreeBSD issue or it may be a bug in the author’s code. It is hard to know since I don’t see anywhere where the author specified the platform this was tested on. So I researched and found that AF_INET is declared in sys/socket.h however, again it is not included. Se we add this line to the file.
Ok, now try to compile with this command line:
Everybody cheer…this time it worked.
It tested the client and the server application together and sure enough, communication occurred. Any string I sent from the client, server sent back.
I was able to use the exact same command with the IPv6 version of the server code, only replacing the TCPEchoServer4.c with TCPEchoServer6.c.
The rest of the chapter
The rest of the chapter is about what you would expect. The author gives a snippet of code and explains it well. Make sure to take time to read it. This is foundational material for understanding the rest of this book, so it may not hurt to read it twice.
Other notes on the C code
You need to be familar with some common C language features such as typedef and struct as they are used heavily.
Most the types used are just types that have been renamed with typedef. I am not 100% sure why one would declare a function as follows but I know at least one reason that I will mention:
To me this is much more confusing than what it is really saying. in_port_t is just an unsigned short. So it would seem that one should declare the function as follows:
unsigned short port;
However, what if your code is very popular and five years or ten years from now the RFC changes the implementation of a port so that an unsigned int is now required. it is really easy to change in_port_t to point be a typedef of an unsigned int and no other work needs to be done. However, if you declared your variable using unsigned short instead of in_port_t, then every where in your code that you did this, you have to find and manually change it or your code will probably fail.
You can even make a typedef of a typedef and in fact that is what in_port_t is. If you have an IDE (I am using Code::Blocks), then you can highlight the type, right click it and choose something similar to Find Declaration of: in_port_t and it will take you to where this value is defined using a typedef. However, in_port_t is a typedef of uinst16_t, which is a typedef itself of __uinst16_t, which is a typedef of unsigned short. So it is multiple typedefs deep. While sometimes confusing, this can be useful as mentioned above.
C does not have objects or classes. Instead it has structs and one might say that they are the predecessor to objects or classes. However, they are not objects or classes and you should understand how they function. If you understand them, this book will be easier to understand. If you don’t, you will probably struggle. If you aren’t clear on structs, then take time to read about them.