Hands-On Network Programming with C

Written By Lewis Van Winkle

How to get socket error message

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:

screenshot showing socket programming error message on Linux

The same error message on Windows may display as:

screenshot showing socket programming error message on Windows

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.

If you found this information helpful, you might be interested in my book.

Purchase on Amazon Purchase on Packt