Lately I’ve been getting back into C, for various reasons I haven’t been impressed so far with either GoLang nor Rust and the newest additions to the C++ standard seem to be making the language increasingly harder to manage. However, because I might be developing some larger C++ projects in the future I want to revisit the C programming language in order to strengthen my fundamentals in case I need to deal with raw pointers/unsafe sections of C++ applications. What better place to start getting back into C then with sockets programming.
Intro
As my first blog post on getting back into C/C++ programming I want to look at how one would approach building a simple echo server in C using sockets.If you aren’t aware an echo server is a server which copies the client data and sends it back to them upon each request. The simplicity of the echo server is why it is often considered to be the “Hello World” of socket programming. Before we begin make sure you have the following…
A working C compiler, GCC(Linux), Clang(Mac OSX) and Visual Studio C++(Windows) are all great compilers that come standard with their operating systems
A text editor that can enable C syntax highlighting. I prefer to use Vim but SublimeText3 is a great cross-platform text-editor that is gaining popularity
IDEs are also fine to use but for this tutorial you probably won’t need one. Eclipse CDT is great for Linux, while Visual Studio and Xcode are the usual suspects on Windows and Mac OSX respectively.
Once you have your text editor or IDE setup for C/C++ development we can begin.
Part I: The Client
The C preprocessor
Our goal is to build an echo server client. However, we want our client to be able to run on all of the major platforms Linux, OSX and Windows.
To accomplish this we’ll need to define some conditional compilation rules using C’s prepreprocessor grammar.
Create a working directory called UdpEcho
Change into the directory UdpEcho and create a file called client.c
Add the following preprocessor code to make our echo client compile nicely on different platforms.
What will happen when we compile this later is that depending on your operating system the C compiler will either include or exclude specific sections based on the platform detected.
If you are using an intelligent IDE the source code will fade out the preprocessor blocks not used by your system to serve as a visual explaination. For instance if we are on Linux our code will not include the winsock2 header.
Many software devs tend to sometimes rant a little too much about the old WinAPI codebase especially given since in todays world you’ll use the C++ friendly WinRuntimeAPI libraries instead but if you go read about the history of WinAPI you’ll find that Microsoft tried very hard to address the difficulties developers had with the existing BSD libraries. For instance look at how we only had to include just a single header for Windows. Anyways if you’re curious about the Winsock libraries I suggest going straight to the MSDN pages on the topic.
Function prototypes and object macros
After declaring conditional compliation macros and including header files C programs typically reserve the next section of the code for declaring object macros and function prototypes.
We won’t be needing very many for the udp echo client but here they are. Add the following code to client.c as listed below.
|
|
Under the Linux/Mac section we have a error handling function that you’ll hopefully never have to use but just in case you decide to fork this code and add more features you can use it as a debug logger. Aside from the error function, both groups have two functions; the echo loop itself and the client socket setup function. We’ll add the main function first and start off with the Mac/Linux section before tackling the Windows code.
Main
Here is the main function.
|
|
Although its a tad bit ugly our preprocessor object macro for PLATFORM allows us to select the path of execution based on our operating system.
Mac and Linux Client Code
|
|
Our error function is just a wrapper around the stdin function for printing messages to stderr. Its a short function and probably could have been written as a inline function but optimization isn’t the topic of this blog post. Moving along, next up is the client socket setup function.
|
|
The beauty of the User Datagram Protocol is that we don’t need to actually confirm that our sever exists.
We give it a location, arm our packets with some data and fire away. Whether or not the server actually receives the packets and any error handling for packets dropped along the way is entirely up to the developer of the application.
|
|
The above function is the echo loop. Its goal is to accept user input from the keyboard, send it off to the server and spit back out the results.
The only gotcha within the echo loop is the fact that we have to use buffers for storing our data. So basically whenever you want to send data to the server, include the inputBuffer and whenever you want to read a response store it to the recvBuffer. That about wraps up the client code for Mac and Linux.
Windows client code
On Windows based platforms our code will look very similar, with the primary difference being that the Winsock library uses several predefined macros to make our code easier to read. Of course one could easily do the same with good old BSD code Mac/Linux but its nice that Winsock provides macros such as INVALID_SOCKET
versus the C style of checking “if the return value is negative”.
|
|
If you understood the Mac/Linux client you shouldn’t have a hard time digesting the Windows equivalent of the EchoLoop function. The one huge difference here the use of WSACleanup(); if you don’t use it Windows will whine at you for being a sloppy socket programmer.
|
|
Thats it for today; below is the complete source code.