前言
笔者在将socket通信的自定义类从Linux移植到Windows时遇到一些问题,整理下来希望帮助到需要的人,同时也加深自己的理解。
差异
头文件
#ifdef _WIN32
#include <ws2tcpip.h>
#define inet_pton InetPton
#define SHUT_RDWR SD_BOTH
#define MSG_NOSIGNAL 0
#else
#include <netinet/in.h>
#endif
Windows使用头文件ws2tcpip.h
,Linux则是netinet/in.h
,旧版Windows不提供标准函数inet_pton
,但是有InetPton
,在这里用宏替换做了适配。另外Windows缺少的宏定义也做了简单适配。
fd类型
Linux:int
Windows:SOCKET
,底层是UINT_PTR
类型,无效值INVALID_SOCKET
,定义为(SOCKET)(~0)
。
这里的适配代码:
class TcpClientIO : public SocketIO
{
public:
#ifdef _WIN32using socket_t = SOCKET;
#elseusing socket_t = int;
#endifTcpClientIO();virtual ~TcpClientIO();private:socket_t sockFd_{static_cast<socket_t>(-1)};
};
static_cast<socket_t>(-1)
和(SOCKET)(~0)
结果应该是一致的。
send / recv
函数原型略微有差异。
Linux:ssize_t send (int __fd, const void *__buf, size_t __n, int __flags);
Windows:WINSOCK_API_LINKAGE int WSAAPI send(SOCKET s,const char *buf,int len,int flags);
初始化
适用Windows socket之前需要调用WSAStartup
初始化,Linux不需要。反初始化同样(我实现的类没有做这步,待完善)
TcpClientIO::TcpClientIO()
{
#if _WIN32std::lock_guard<std::mutex> lock(winsockMutex_);if (!winsockInitialized_) {WSADATA wsaData;WSAStartup(MAKEWORD(2,2), &wsaData);winsockInitialized_ = true;}
#endif...
}
用一个静态成员变量记录是否执行过初始化。
反初始化调用cleanupWinsock();
在整个程序结束时调用(并非在析构函数中调用)。
关闭fd
另一个容易被忽略的差异是关闭fd的接口。Windows使用closeSocket
而Linux使用close
。可以在Linux中定义#define closesocket close
,统一调用closesocket
.
库包含
Windows中需要动态链接ws2_32.dll
,或者静态链接#pragma comment(lib, "ws2_32.lib")
错误处理
Windows使用WSAGetLastError()
获取错误信息,Linux可以用errno
。
当前的类实现并不是很完善,只满足基础需求。作为初学者,欢迎讨论和批评指正。