【摘要】编写Socket通讯程序是一个老话题。本文重点介绍Windows平台和Linux平台Socket通讯的不同,采用C++,编制了一个简单的跨平台的Socket通讯库。
一、Socket通讯的基础知识
Socket通讯是两个计算机之间最基本的通讯方法,有TCP和UDP两种协议。关于这两种协议的区别,不少文章已有详述,这里,稍微总结一下:
1.TCP是面向连接的,是“流”式的,意即通讯两端建立了一个“数码流管”,该流无头无尾,接收端保证接收顺序,但不保证包的分割。
2.UDP是面向无连接的,是“包”式的,意即通讯两端自由发送数据包,接收端不保证接收顺序,但保证包的分割与发送端一致。
正是基于上述二者的不同,在编程上,它们的区别如下:对TCP连接,服务器端过程(bind->listen->accept->send/receive)与客户端不相同(connect->send/receive),对UDP连接,二者似乎更对等一些(服务器端仅需要bind)。
二、socket在windows下和linux下的区别
一些文章也已涉及,这里,也是综合一下,并加上自己的理解。
项目 | Windows | Linux |
主要头文件 | winsock.h/winsock2.h | sys/socket.h fcntl.h errno.h |
链接库 | ws2_32.dll/lib | 连接是使用参数:-lstdc 运行时需要libstdc++.so.5,可在/usr/lib目录中创建一个链接。 |
初始化及退出 | 初始化需要调用WSAStartup,退出需调用WSACleanup | 无 |
关闭Socket | closesocket | 与文件操作相同close |
Socket类型 | SOCKET | 与文件句柄相同int |
错误查看 | WSAGetLastError | 全局变量errno |
设置非阻塞模式 | int i=1 ioctlsocket(sockethandle,FIONBIO,&i) |
fcntl(ockethandle,F_SETFL, O_NONBLOCK) |
send/recv函数最后一个参数 | 一般设置为0 | 可以有多种组合:MSG_NOSIGNAL,MSG_DONTWAIT,MSG_WAITALL |
send的异常 | 当连接断开,还发数据的时候,不仅send()的返回值会有反映,而且还会像系统发送一个异常消息,如果不作处理,程序会退 出。为此,send()函数的最后一个参数可以设置MSG_NOSIGNAL,禁止send()函数向系统发送异常消息。 | |
WSA宏 | 除了可以使用标准的socket函数外,微软自己有许多以WSA开始的函数,作为对标准socket函数的封装(可能微软感觉这些函数更好用一些吧) |
以下给出源代码。
sock_wrap.h代码如下,其中用到了platform.h,定义_WIN32_PLATFROM_和_LINUX_PLATFROM_两个宏。
#ifndef _SOCK_WRAP_H_
#define _SOCK_WRAP_H_
#include "platform.h"
#if defined(_WIN32_PLATFROM_)
#include <winsock2.h>
typedef SOCKET HSocket;
#endif
#if defined(_LINUX_PLATFORM_)
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
typedef int HSocket;
#define SOCKET_ERROR (-1)
#define INVALID_SOCKET 0
#endif
typedef struct
{
int block;
int sendbuffersize;
int recvbuffersize;
int lingertimeout;
int recvtimeout;
int sendtimeout;
} socketoption_t;
typedef struct
{
int nbytes;
int nresult;
} transresult_t;
int InitializeSocketEnvironment();
void FreeSocketEnvironment();
void GetAddressFrom(sockaddr_in *addr, const char *ip, int port);
void GetIpAddress(char *ip, sockaddr_in *addr);
bool IsValidSocketHandle(HSocket handle);
int GetLastSocketError();
HSocket SocketOpen(int tcpudp);
void SocketClose(HSocket &handle);
int SocketBlock(HSocket hs, bool bblock);
int SocketTimeOut(HSocket hs, int recvtimeout, int sendtimeout, int lingertimeout);
int SocketBind(HSocket hs, sockaddr_in *addr);
HSocket SocketAccept(HSocket hs, sockaddr_in *addr);
int SocketConnect(HSocket hs, struct sockaddr_in *paddr);
int SocketListen(HSocket hs, int maxconn);
void SocketSend(HSocket hs, const char *ptr, int nbytes, transresult_t &rt);
void SocketRecv(HSocket hs, char *ptr, int nbytes, transresult_t &rt);
void SocketTryRecv(HSocket hs, char *ptr, int nbytes, int milliseconds, transresult_t &rt);
void SocketTrySend(HSocket hs, const char *ptr, int nbytes, int milliseconds, transresult_t &rt);
void SocketClearRecvBuffer(HSocket hs)