Socks 5: Porting the test to Windows

Porting networking code from POSIX to Windows is actually remarkably simple: Windows’ sockets implementation, winsock, implements almost exactly the same interface and there are really onlt very few caveats to take into account.

There are, however, a few caveats to take into account when porting from one compiler to another. For example, the following patch shows that a static assertion that shouldn’t fail on any standards-compliant compiler does fail, but without reason, on Microsoft’s compiler (the one that comes with the SDK we’re using):

diff --git a/lib/rfc1961/Token.cpp b/lib/rfc1961/Token.cpp
index 9932143..ddd2d69 100644
--- a/lib/rfc1961/Token.cpp
+++ b/lib/rfc1961/Token.cpp
@@ -18,8 +18,11 @@ struct NeedSwab
 	};
 	BOOST_STATIC_ASSERT(sizeof(U_) == sizeof(boost::uint16_t));
 	BOOST_STATIC_ASSERT(offsetof(U_, u16_) == 0);
+#ifndef _MSC_VER
+	/* I don't know why, but MSVC8 trips over these compile-time assertions, even though they do actually hold. */
 	BOOST_STATIC_ASSERT(offsetof(U_, u8_[0]) == 0);
 	BOOST_STATIC_ASSERT(offsetof(U_, u8_[1]) == 1);
+#endif
 	BOOST_STATIC_ASSERT(sizeof(char) == sizeof(boost::uint8_t));
 
 	NeedSwab()

The reason I say it fails “without reason” is that when you check at run-time, the union is properly aligned – which is what the static assertions check. That is reflected in the comment added with the #ifndef/#endif pair.

There are also a few typedefs that Windows’ SDK doesn’t supply – a signed version of size_t is a good example. We have to provide those ourselves.

diff --git a/tests/RFC1961/Details/Socket.h b/tests/RFC1961/Details/Socket.h
index e564c75..06236fb 100644
--- a/tests/RFC1961/Details/Socket.h
+++ b/tests/RFC1961/Details/Socket.h
@@ -1,6 +1,10 @@
 #ifndef chausette_tests_rfc1961_details_socket_h
 #define chausette_tests_rfc1961_details_socket_h
 
+#if _MSC_VER
+#include <windows.h>
+typedef int ssize_t;
+#endif
 #include <vector>
 #include <boost/cstdint.hpp>

Then there’s the caveat that some functions are actually different in winsock than they are in the POSIX world. The close function is one such example: because sockets aren’t actually file descriptors on Windows (unlike on most UNIX systems) Winsock has a different call for closing sockets, closesocket, than it has for files (close).

The header files to include are also different: you get everything by including winsock2.h, which should be included before including windows.h because windows.h includes an older version of the winsock library headers.

Finally, because the socket functions are implemented in a separate library that is not part of the C/C++ runtime, you should link your executable with ws2_32.lib (at least on Desktop Windows).

Note that in all of these snippets, I expect Windows code to be compiled with the Windows SDK compiler, which defines _MSC_VER. If you want to use the MinGW compiler, you have to make a few changes to pick that up.

diff --git a/tests/RFC1961/Details/Socket.cpp b/tests/RFC1961/Details/Socket.cpp
index 9480b22..5f7c471 100644
--- a/tests/RFC1961/Details/Socket.cpp
+++ b/tests/RFC1961/Details/Socket.cpp
@@ -1,8 +1,18 @@
+#ifdef _MSC_VER
+#include <winsock2.h>
+#include <windows.h>
+void close(int fd)
+{
+	closesocket(fd);
+}
+#endif
 #include "Socket.h"
 #include <cassert>
+#ifndef _MSC_VER
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <unistd.h>
+#endif
 #include <stdexcept>
 
 namespace Details
diff --git a/tests/RFC1961/Server.cpp b/tests/RFC1961/Server.cpp
index cbc1da0..7f38ba3 100644
--- a/tests/RFC1961/Server.cpp
+++ b/tests/RFC1961/Server.cpp
@@ -1,6 +1,8 @@
 #include "Server.h"
 #include <boost/thread.hpp>
+#ifndef _MSC_VER
 #include <sys/time.h>
+#endif
 #include <cerrno>
 #include "../../lib/rfc1961/Token.h"
 #include "Details/Socket.h"

Finally, Windows’ Sleep takes milliseconds while sleep takes seconds – luckily C and C++ are case-sensitive so we can distinguish the two easily – and we have to initialize the Winsock library before we can use it. This is done calling WSAStartup. I should actually have called WSACleanup at the end as well, but that is left as an exercise to the reader.

diff --git a/tests/RFC1961/main.cpp b/tests/RFC1961/main.cpp
index 91dae61..739180e 100644
--- a/tests/RFC1961/main.cpp
+++ b/tests/RFC1961/main.cpp
@@ -1,11 +1,27 @@
+#ifdef _MSC_VER
+#include <winsock2.h>
+#include <windows.h>
+#endif
 #include <boost/thread.hpp>
 #include "Server.h"
 #include "Client.h"
 
+#ifdef _MSC_VER
+#define sleep(x) Sleep((x) * 1000)
+#endif
+
 #define TEST_PORT		2478
 
 int main()
 {
+#ifdef _MSC_VER
+	WSADATA wsa_data;
+	if (WSAStartup (MAKEWORD(2,2), &wsa_data) != 0)
 
+	{
 
+		return 1;
 
+	}
+#endif
+
 	bool shared_done(false);
 	bool local_done(shared_done);
 	Server server(shared_done, TEST_PORT);

About rlc

Software Analyst in embedded systems and C++, C and VHDL developer, I specialize in security, communications protocols and time synchronization, and am interested in concurrency, generic meta-programming and functional programming and their practical applications. I take a pragmatic approach to project management, focusing on the management of risk and scope. I have over two decades of experience as a software professional and a background in science.
This entry was posted in Uncategorized and tagged , . Bookmark the permalink.