Socket programming from C or C++ can be tricky. One thing you can do to make it easier is to implement decent error handling. This can sometimes be difficult because Windows handles errors differently than Linux or macOS.
Getting the error code
On Linux or macOS, the last error is stored in the errno
variable. On
Windows, you can retrieve the last error by calling the WSAGetLastError()
function. Since your program will almost certainly need to check for errors
multiple times, I suggest you wrap these methods in a macro.
The following code defines a macro called GETSOCKETERRNO()
which will return
the last error code.
//Cross-platform error code #if defined(_WIN32) #define GETSOCKETERRNO() (WSAGetLastError()) #else #define GETSOCKETERRNO() (errno) #endif
Now if you call a function, such as socket()
you can check for and display
the error code easily. For example, the following code will cause an error and
print the error code:
socket(0, 0, 0); //definitely causes an error! printf("Last error was %d\n", GETSOCKETERRNO());
So that's an easy enough way to get the error code, but what about a descriptive error message?
Getting Error Messages on Linux/macOS
Getting the last error message on Linux or macOS is very easy. You merely pass
the errno
variable to the strerror()
function.
For example:
printf("The last error message is: %s\n", strerror(errno));
Getting Error Messages on Windows
Getting the error message on Windows is quite a bit trickier. You'll want to
use the FormatMessage()
function, and that function is... complicated.
Rather than explain all the options of FormatMessage()
, it'll be easier to
just give you an example.
The following code will print your program's last error message on Windows:
static char message[256] = {0}; FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, 0, WSAGetLastError(), 0, message, 256, 0); printf("The last error message is: %s", message);
One thing to note is that FormatMessage usually returns messages with a trailing newline character at the end.
For more information, you can read about FormatMessage()
from the Windows
Dev Center.
Cross-Platform Error Messages
Assuming you're writing network code expected to run on various platforms, you'll want to wrap the Windows and non-Windows error message handling in its own function to simplify things.
The following function will return the last error message on both Windows and Linux/macOS:
const char *get_error_text() { #if defined(_WIN32) static char message[256] = {0}; FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, 0, WSAGetLastError(), 0, message, 256, 0); char *nl = strrchr(message, '\n'); if (nl) *nl = 0; return message; #else return strerror(errno); #endif }
Notice the use of strchr()
in the Windows section of the code above. This is
to remove the trailing newline character that Windows includes in error
messages. That way the function will return messages without a trailing
newline, regardless of whether it's being run on Windows or an Unix-like
platform.
You can use the get_error_text()
function as follows:
socket(0, 0, 0); //definitely causes an error! printf("Last error was: %s\n", get_error_text());
If you run that code on Linux you might see something like this:
The same error message on Windows may display as:
Each operating system will use different error codes and messages, but at least we have portable code to retrieve the last error code and message in a cross-platform way.
I hope you found this article helpful. I'd love to hear any feedback you might have. Don't hesitate to get in touch.
You can find the rest of my network programming articles here.