跨平台Socket通讯程序(Windows+Linux)底层封装

跨平台Socket通讯程序(Windows+Linux)底层封装

转自:http://blog.csdn.net/guxch/article/details/7310452

【摘要】编写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下的区别

一些文章也已涉及,这里,也是综合一下,并加上自己的理解。

项目WindowsLinux
主要头文件winsock.h/winsock2.hsys/socket.h fcntl.h  errno.h
链接库ws2_32.dll/lib连接是使用参数:-lstdc
运行时需要libstdc++.so.5,可在/usr/lib目录中创建一个链接。
初始化及退出初始化需要调用WSAStartup,退出需调用WSACleanup
关闭Socketclosesocket与文件操作相同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函数的封装(可能微软感觉这些函数更好用一些吧) 


三、跨平台的Socket辅助程序

以下给出源代码。

sock_wrap.h代码如下,其中用到了platform.h,定义_WIN32_PLATFROM_和_LINUX_PLATFROM_两个宏。

  1. #ifndef _SOCK_WRAP_H_  
  2. #define _SOCK_WRAP_H_  
  3.   
  4. #include "platform.h"  
  5.   
  6. #if defined(_WIN32_PLATFROM_)  
  7. #include <winsock2.h>  
  8. typedef SOCKET HSocket;  
  9. #endif  
  10.   
  11. #if defined(_LINUX_PLATFORM_)  
  12. #include <netinet/in.h>  
  13. #include <sys/socket.h>  
  14. #include <sys/types.h>  
  15.   
  16. typedef int HSocket;  
  17. #define SOCKET_ERROR  (-1)  
  18. #define INVALID_SOCKET  0  
  19. #endif  
  20.   
  21.   
  22. typedef struct  
  23. {  
  24.     int block;  
  25.     int sendbuffersize;  
  26.     int recvbuffersize;  
  27.     int lingertimeout;  
  28.     int recvtimeout;  
  29.     int sendtimeout;  
  30. } socketoption_t;  
  31.   
  32. typedef struct  
  33. {  
  34.    int nbytes;  
  35.    int nresult;  
  36. } transresult_t;  
  37.   
  38. int InitializeSocketEnvironment();  
  39. void FreeSocketEnvironment();  
  40. void GetAddressFrom(sockaddr_in *addr, const char *ip, int port);  
  41. void GetIpAddress(char *ip, sockaddr_in *addr);  
  42. bool IsValidSocketHandle(HSocket handle);  
  43. int GetLastSocketError();  
  44.   
  45. HSocket SocketOpen(int tcpudp);  
  46. void SocketClose(HSocket &handle);  
  47.   
  48. int SocketBlock(HSocket hs, bool bblock);  
  49. int SocketTimeOut(HSocket hs, int recvtimeout, int sendtimeout, int lingertimeout);  
  50.   
  51. int SocketBind(HSocket hs, sockaddr_in *addr);  
  52. HSocket SocketAccept(HSocket hs, sockaddr_in *addr);  
  53. int SocketListen(HSocket hs, int maxconn);  
  54.   
  55. void SocketSend(HSocket hs, const char *ptr, int nbytes, transresult_t &rt);  
  56. void SocketRecv(HSocket hs, char *ptr, int nbytes, transresult_t &rt);  
  57. void SocketTryRecv(HSocket hs, char *ptr, int nbytes, int milliseconds, transresult_t &rt);  
  58. void SocketTrySend(HSocket hs, const char *ptr, int nbytes, int milliseconds, transresult_t &rt);  
  59.   
  60. void SocketClearRecvBuffer(HSocket hs);  
  61.   
  62. class CSockWrap  
  63. {  
  64. public:  
  65.     CSockWrap(int tcpudp);  
  66.     ~CSockWrap();  
  67.     void SetAddress(const char *ip, int port);  
  68.     void SetAddress(sockaddr_in *addr);  
  69.     int SetTimeOut(int recvtimeout, int sendtimeout, int lingertimeout);  
  70.     int SetBufferSize(int recvbuffersize, int sendbuffersize);  
  71.     int SetBlock(bool bblock);  
  72.   
  73.     HSocket  GetHandle () { return m_hSocket;}  
  74.     void Reopen(bool bForceClose);  
  75.     void Close();  
  76.     transresult_t Send(void *ptr, int nbytes);  
  77.     transresult_t Recv(void *ptr, int nbytes );  
  78.     transresult_t TrySend(void *ptr, int nbytes, int milliseconds);  
  79.     transresult_t TryRecv(void *ptr, int nbytes, int  milliseconds );  
  80.     void ClearRecvBuffer();  
  81.   
  82. protected:  
  83.     HSocket  m_hSocket;  
  84.     sockaddr_in m_stAddr;  
  85.     int m_tcpudp;  
  86. };  
  87.   
  88.   
  89. #endif   
#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 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);

class CSockWrap
{
public:
    CSockWrap(int tcpudp);
    ~CSockWrap();
    void SetAddress(const char *ip, int port);
    void SetAddress(sockaddr_in *addr);
    int SetTimeOut(int recvtimeout, int sendtimeout, int lingertimeout);
    int SetBufferSize(int recvbuffersize, int sendbuffersize);
    int SetBlock(bool bblock);

    HSocket  GetHandle () { return m_hSocket;}
    void Reopen(bool bForceClose);
    void Close();
    transresult_t Send(void *ptr, int nbytes);
    transresult_t Recv(void *ptr, int nbytes );
    transresult_t TrySend(void *ptr, int nbytes, int milliseconds);
    transresult_t TryRecv(void *ptr, int nbytes, int  milliseconds );
    void ClearRecvBuffer();

protected:
    HSocket  m_hSocket;
    sockaddr_in m_stAddr;
    int m_tcpudp;
};


#endif 

sock_wrap.cpp代码如下,其中引用了lightThread.h和spantime.h,它们的代码见“ 跨平台(Windows+Linux)的线程辅助程序”。

  1. #include "platform.h"  
  2.   
  3. #include <stdio.h>  
  4. #include <string.h>  
  5. #include <fcntl.h>  
  6. #include "lightthread.h"  
  7. #include "sock_wrap.h"  
  8. #include "TimeSpan.h"  
  9.   
  10. #define INVALIDSOCKHANDLE   INVALID_SOCKET  
  11.   
  12. #if defined(_WIN32_PLATFROM_)  
  13. #include <windows.h>  
  14. #define ISSOCKHANDLE(x)  (x!=INVALID_SOCKET)  
  15. #define BLOCKREADWRITE      0  
  16. #define NONBLOCKREADWRITE   0  
  17. #define SENDNOSIGNAL        0  
  18. #define ETRYAGAIN(x)     (x==WSAEWOULDBLOCK||x==WSAETIMEDOUT)  
  19. #define gxsprintf   sprintf_s  
  20.   
  21. #endif  
  22.   
  23.   
  24. #if defined(_LINUX_PLATFORM_)  
  25. #include <stdlib.h>  
  26. #include <errno.h>  
  27. #include <unistd.h>  
  28. #include <sys/socket.h>  
  29. #include <netinet/in.h>  
  30. #include <arpa/inet.h>  
  31. #define ISSOCKHANDLE(x)    (x>0)  
  32. #define BLOCKREADWRITE      MSG_WAITALL  
  33. #define NONBLOCKREADWRITE   MSG_DONTWAIT  
  34. #define SENDNOSIGNAL        MSG_NOSIGNAL  
  35. #define ETRYAGAIN(x)        (x==EAGAIN||x==EWOULDBLOCK)  
  36. #define gxsprintf           snprintf  
  37.   
  38. #endif  
  39.   
  40.   
  41. void GetAddressFrom(sockaddr_in *addr, const char *ip, int port)  
  42. {  
  43.     memset(addr, 0, sizeof(sockaddr_in));  
  44.     addr->sin_family = AF_INET;            /*地址类型为AF_INET*/  
  45.     if(ip)  
  46.     {  
  47.         addr->sin_addr.s_addr = inet_addr(ip);  
  48.     }  
  49.     else  
  50.     {  
  51.         /*网络地址为INADDR_ANY,这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP地址, 
  52.         这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP地址*/  
  53.         addr->sin_addr.s_addr = htonl(INADDR_ANY);  
  54.     }  
  55.     addr->sin_port = htons(port);   /*端口号*/  
  56. }  
  57. void GetIpAddress(char *ip, sockaddr_in *addr)  
  58. {  
  59.     unsigned char *p =(unsigned char *)( &(addr->sin_addr));  
  60.     gxsprintf(ip, 17, "%u.%u.%u.%u", *p,*(p+1), *(p+2), *(p+3) );  
  61. }  
  62.   
  63. int GetLastSocketError()  
  64. {  
  65. #if defined(_WIN32_PLATFROM_)  
  66.     return WSAGetLastError();  
  67. #endif  
  68.   
  69. #if defined(_LINUX_PLATFORM_)  
  70.     return errno;  
  71. #endif  
  72. }  
  73.   
  74. bool IsValidSocketHandle(HSocket handle)  
  75. {  
  76.     return ISSOCKHANDLE(handle);  
  77. }  
  78.   
  79. void SocketClose(HSocket &handle)  
  80. {  
  81.     if(ISSOCKHANDLE(handle))  
  82.     {  
  83. #if defined(_WIN32_PLATFROM_)  
  84.         closesocket(handle);  
  85. #endif  
  86.   
  87. #if defined(_LINUX_PLATFORM_)  
  88.         close(handle);  
  89. #endif  
  90.         handle = INVALIDSOCKHANDLE;  
  91.     }  
  92. }  
  93.   
  94. HSocket SocketOpen(int tcpudp)  
  95. {  
  96.     int protocol = 0;  
  97.     HSocket hs;  
  98. #if defined(_WIN32_PLATFROM_)  
  99.     if(tcpudp== SOCK_STREAM) protocol=IPPROTO_TCP;  
  100.     else if (tcpudp== SOCK_DGRAM) protocol = IPPROTO_UDP;  
  101. #endif  
  102.     hs = socket(AF_INET, tcpudp, protocol);  
  103.     return hs;  
  104. }  
  105. int SocketBind(HSocket hs, sockaddr_in *paddr)  
  106. {  
  107.     return bind(hs, (struct sockaddr *)paddr, sizeof(sockaddr_in));  
  108. }  
  109. int SocketListen(HSocket hs, int maxconn)  
  110. {  
  111.     return listen(hs,maxconn);  
  112. }  
  113. HSocket SocketAccept(HSocket hs, sockaddr_in *paddr)  
  114. {  
  115. #if defined(_WIN32_PLATFROM_)  
  116.     int cliaddr_len = sizeof(sockaddr_in);  
  117. #endif  
  118. #if defined(_LINUX_PLATFORM_)  
  119.     socklen_t cliaddr_len = sizeof(sockaddr_in);  
  120. #endif  
  121.     return accept(hs, (struct sockaddr *)paddr, &cliaddr_len);  
  122. }  
  123. //  
  124. // if timeout occurs, nbytes=-1, nresult=1  
  125. // if socket error, nbyte=-1, nresult=-1  
  126. // if the other side has disconnected in either block mode or nonblock mode, nbytes=0, nresult=-1  
  127. // otherwise nbytes= the count of bytes sent , nresult=0  
  128. void SocketSend(HSocket hs, const char *ptr, int nbytes, transresult_t &rt)  
  129. {  
  130.     rt.nbytes = 0;  
  131.     rt.nresult = 0;  
  132.     if(!ptr|| nbytes<1) return;  
  133.   
  134.     //Linux: flag can be MSG_DONTWAIT, MSG_WAITALL, 使用MSG_WAITALL的时候, socket 必须是处于阻塞模式下,否则WAITALL不能起作用  
  135.     rt.nbytes = send(hs, ptr, nbytes, BLOCKREADWRITE|SENDNOSIGNAL);  
  136.     if(rt.nbytes>0)  
  137.     {  
  138.         rt.nresult = (rt.nbytes == nbytes)?0:1;  
  139.     }  
  140.     else if(rt.nbytes==0)  
  141.     {  
  142.        rt.nresult=-1;  
  143.     }  
  144.     else  
  145.     {  
  146.         rt.nresult = GetLastSocketError();  
  147.         rt.nresult = ETRYAGAIN(rt.nresult)? 1:-1;  
  148.     }  
  149. }  
  150.   
  151.   
  152.   
  153. // if timeout occurs, nbytes=-1, nresult=1  
  154. // if socket error, nbyte=-1, nresult=-1  
  155. // if the other side has disconnected in either block mode or nonblock mode, nbytes=0, nresult=-1  
  156. void SocketRecv(HSocket hs, char *ptr, int nbytes, transresult_t &rt)  
  157. {  
  158.     rt.nbytes = 0;  
  159.     rt.nresult = 0;  
  160.     if(!ptr|| nbytes<1) return;  
  161.   
  162.     rt.nbytes = recv(hs, ptr, nbytes, BLOCKREADWRITE);  
  163.     if(rt.nbytes>0)  
  164.     {  
  165.         return;  
  166.     }  
  167.     else if(rt.nbytes==0)  
  168.     {  
  169.        rt.nresult=-1;  
  170.     }  
  171.     else  
  172.     {  
  173.         rt.nresult = GetLastSocketError();  
  174.         rt.nresult = ETRYAGAIN(rt.nresult)? 1:-1;  
  175.     }  
  176.   
  177. }  
  178. //  nbytes= the count of bytes sent  
  179. // if timeout occurs, nresult=1  
  180. // if socket error,  nresult=-1,  
  181. // if the other side has disconnected in either block mode or nonblock mode, nresult=-2  
  182. void SocketTrySend(HSocket hs, const char *ptr, int nbytes, int milliseconds, transresult_t &rt)  
  183. {  
  184.     rt.nbytes = 0;  
  185.     rt.nresult = 0;  
  186.     if(!ptr|| nbytes<1) return;  
  187.   
  188.   
  189.     int n;  
  190.     CMyTimeSpan start;  
  191.     while(1)  
  192.     {  
  193.         n = send(hs, ptr+rt.nbytes, nbytes, NONBLOCKREADWRITE|SENDNOSIGNAL);  
  194.         if(n>0)  
  195.         {  
  196.             rt.nbytes += n;  
  197.             nbytes -= n;  
  198.             if(rt.nbytes >= nbytes) {    rt.nresult = 0;  break; }  
  199.         }  
  200.         else if( n==0)  
  201.         {  
  202.             rt.nresult= -2;  
  203.             break;  
  204.         }  
  205.         else  
  206.         {  
  207.             n = GetLastSocketError();  
  208.             if(ETRYAGAIN(n))  
  209.             {  
  210.                 CLightThread::DiscardTimeSlice();  
  211.             }  
  212.             else  
  213.             {  
  214.                 rt.nresult = -1;  
  215.                 break;  
  216.             }  
  217.         }  
  218.         if(start.GetSpaninMilliseconds()>milliseconds)  { rt.nresult= 1; break;}  
  219.     }  
  220. }  
  221. // if timeout occurs, nbytes=-1, nresult=1  
  222. // if socket error, nbyte=-1, nresult=-1  
  223. // if the other side has disconnected in either block mode or nonblock mode, nbytes=0, nresult=-1  
  224. void SocketTryRecv(HSocket hs, char *ptr, int nbytes, int milliseconds, transresult_t &rt)  
  225. {  
  226.     rt.nbytes = 0;  
  227.     rt.nresult = 0;  
  228.     if(!ptr|| nbytes<1) return;  
  229.   
  230.     if(milliseconds>2)  
  231.     {  
  232.         CMyTimeSpan start;  
  233.         while(1)  
  234.         {  
  235.             rt.nbytes = recv(hs, ptr, nbytes, NONBLOCKREADWRITE);  
  236.             if(rt.nbytes>0)  
  237.             {  
  238.                break;  
  239.             }  
  240.             else if(rt.nbytes==0)  
  241.             {  
  242.                 rt.nresult = -1;  
  243.                 break;  
  244.             }  
  245.             else  
  246.             {  
  247.                 rt.nresult = GetLastSocketError();  
  248.                 if( ETRYAGAIN(rt.nresult))  
  249.                 {  
  250.                    if(start.GetSpaninMilliseconds()>milliseconds)  { rt.nresult= 1; break;}  
  251.                    CLightThread::DiscardTimeSlice();  
  252.                 }  
  253.                 else  
  254.                 {  
  255.                     rt.nresult = -1;  
  256.                     break;  
  257.                 }  
  258.             }  
  259.   
  260.         }  
  261.     }  
  262.     else  
  263.     {  
  264.         SocketRecv(hs, ptr, nbytes, rt);  
  265.     }  
  266. }  
  267.   
  268. void SocketClearRecvBuffer(HSocket hs)  
  269. {  
  270. #if defined(_WIN32_PLATFROM_)  
  271.     struct timeval tmOut;  
  272.     tmOut.tv_sec = 0;  
  273.     tmOut.tv_usec = 0;  
  274.     fd_set    fds;  
  275.     FD_ZERO(&fds);  
  276.     FD_SET(hs, &fds);  
  277.     int   nRet = 1;  
  278.     char tmp[100];  
  279.     int rt;  
  280.     while(nRet>0)  
  281.     {  
  282.         nRet= select(FD_SETSIZE, &fds, NULL, NULL, &tmOut);  
  283.         if(nRet>0)  
  284.         {  
  285.            nRet = recv(hs, tmp, 100,0);  
  286.         }  
  287.     }  
  288. #endif  
  289.   
  290. #if defined(_LINUX_PLATFORM_)  
  291.    char tmp[100];  
  292.    while(recv(hs, tmp, 100, NONBLOCKREADWRITE)> 0);  
  293. #endif  
  294. }  
  295.   
  296. int SocketBlock(HSocket hs, bool bblock)  
  297. {  
  298.     unsigned long mode;  
  299.     if( ISSOCKHANDLE(hs))  
  300.     {  
  301. #if defined(_WIN32_PLATFROM_)  
  302.         mode = bblock?0:1;  
  303.         return ioctlsocket(hs,FIONBIO,&mode);  
  304. #endif  
  305.   
  306. #if defined(_LINUX_PLATFORM_)  
  307.         mode = fcntl(hs, F_GETFL, 0);                  //获取文件的flags值。  
  308.         //设置成阻塞模式      非阻塞模式  
  309.         return bblock?fcntl(hs,F_SETFL, mode&~O_NONBLOCK): fcntl(hs, F_SETFL, mode | O_NONBLOCK);  
  310. #endif  
  311.     }  
  312.     return -1;  
  313. }  
  314.   
  315. int SocketTimeOut(HSocket hs, int recvtimeout, int sendtimeout, int lingertimeout)   //in milliseconds  
  316. {  
  317.     int rt=-1;  
  318.     if (ISSOCKHANDLE(hs) )  
  319.     {  
  320.         rt=0;  
  321. #if defined(_WIN32_PLATFROM_)  
  322.         if(lingertimeout>-1)  
  323.         {  
  324.             struct linger  lin;  
  325.             lin.l_onoff = lingertimeout;  
  326.             lin.l_linger = lingertimeout ;  
  327.             rt = setsockopt(hs,SOL_SOCKET,SO_DONTLINGER,(const char*)&lin,sizeof(linger)) == 0 ? 0:0x1;  
  328.         }  
  329.         if(recvtimeout>0 && rt == 0)  
  330.         {  
  331.             rt = rt | (setsockopt(hs,SOL_SOCKET,SO_RCVTIMEO,(char *)&recvtimeout,sizeof(int))==0?0:0x2);  
  332.         }  
  333.         if(sendtimeout>0 && rt == 0)  
  334.         {  
  335.             rt = rt | (setsockopt(hs,SOL_SOCKET, SO_SNDTIMEO, (char *)&sendtimeout,sizeof(int))==0?0:0x4);  
  336.         }  
  337. #endif  
  338.   
  339. #if defined(_LINUX_PLATFORM_)  
  340.    struct timeval timeout;  
  341.         if(lingertimeout>-1)  
  342.         {  
  343.             struct linger  lin;  
  344.             lin.l_onoff = lingertimeout>0?1:0;  
  345.             lin.l_linger = lingertimeout/1000 ;  
  346.             rt = setsockopt(hs,SOL_SOCKET,SO_LINGER,(const char*)&lin,sizeof(linger)) == 0 ? 0:0x1;  
  347.         }  
  348.         if(recvtimeout>0 && rt == 0)  
  349.         {  
  350.             timeout.tv_sec = recvtimeout/1000;  
  351.             timeout.tv_usec = (recvtimeout % 1000)*1000;  
  352.             rt = rt | (setsockopt(hs,SOL_SOCKET,SO_RCVTIMEO,&timeout,sizeof(timeout))==0?0:0x2);  
  353.         }  
  354.         if(sendtimeout>0 && rt == 0)  
  355.         {  
  356.             timeout.tv_sec = sendtimeout/1000;  
  357.             timeout.tv_usec = (sendtimeout % 1000)*1000;  
  358.             rt = rt | (setsockopt(hs,SOL_SOCKET, SO_SNDTIMEO, &timeout,sizeof(timeout))==0?0:0x4);  
  359.         }  
  360. #endif  
  361.     }  
  362.     return rt;  
  363. }  
  364.   
  365.   
  366. int InitializeSocketEnvironment()  
  367. {  
  368. #if defined(_WIN32_PLATFROM_)  
  369.     WSADATA  Ws;  
  370.     //Init Windows Socket  
  371.     if ( WSAStartup(MAKEWORD(2,2), &Ws) != 0 )  
  372.     {  
  373.         return -1;  
  374.     }  
  375. #endif  
  376.     return 0;  
  377. }  
  378. void FreeSocketEnvironment()  
  379. {  
  380. #if defined(_WIN32_PLATFROM_)  
  381.     WSACleanup();  
  382. #endif  
  383. }  
  384. //==============================================================================================================  
  385. //================================================================================================================  
  386. CSockWrap::CSockWrap(int tcpudp)  
  387. {  
  388.     memset(&m_stAddr, 0, sizeof(sockaddr_in));  
  389.     m_tcpudp = tcpudp;  
  390.     m_hSocket = INVALIDSOCKHANDLE;  
  391.     Reopen(false);  
  392. }  
  393.   
  394.   
  395. CSockWrap::~CSockWrap()  
  396. {  
  397.     SocketClose(m_hSocket);  
  398. }  
  399. void CSockWrap::Reopen(bool bForceClose)  
  400. {  
  401.   
  402.     if (ISSOCKHANDLE(m_hSocket) && bForceClose) SocketClose(m_hSocket);  
  403.     if (!ISSOCKHANDLE(m_hSocket) )  
  404.     {  
  405.         m_hSocket=SocketOpen(m_tcpudp);  
  406.     }  
  407.   
  408. }  
  409. void CSockWrap::SetAddress(const char *ip, int port)  
  410. {  
  411.     GetAddressFrom(&m_stAddr, ip, port);  
  412. }  
  413. void CSockWrap::SetAddress(sockaddr_in *addr)  
  414. {  
  415.     memcpy(&m_stAddr, addr, sizeof(sockaddr_in));  
  416. }  
  417.   
  418. int CSockWrap::SetTimeOut(int recvtimeout, int sendtimeout, int lingertimeout)   //in milliseconds  
  419. {  
  420.   return SocketTimeOut(m_hSocket, recvtimeout, sendtimeout, lingertimeout);  
  421. }  
  422.   
  423. int CSockWrap::SetBufferSize(int recvbuffersize, int sendbuffersize)   //in bytes  
  424. {  
  425.     int rt=-1;  
  426.     if (ISSOCKHANDLE(m_hSocket) )  
  427.     {  
  428. #if defined(_WIN32_PLATFROM_)  
  429.         if(recvbuffersize>-1)  
  430.         {  
  431.             rt = setsockopt( m_hSocket, SOL_SOCKET, SO_RCVBUF, ( const char* )&recvbuffersize, sizeofint ) );  
  432.         }  
  433.         if(sendbuffersize>-1)  
  434.         {  
  435.             rt = rt | (setsockopt(m_hSocket,SOL_SOCKET,SO_SNDBUF,(char *)&sendbuffersize,sizeof(int))==0?0:0x2);  
  436.         }  
  437. #endif  
  438.     }  
  439.     return rt;  
  440. }  
  441.   
  442. int CSockWrap::SetBlock(bool bblock)  
  443. {  
  444.     return SocketBlock(m_hSocket, bblock);  
  445. }  
  446. transresult_t CSockWrap::Send(void *ptr, int nbytes)  
  447. {  
  448.     transresult_t rt;  
  449.     SocketSend(m_hSocket, (const char *)ptr, nbytes,rt);  
  450.     return rt;  
  451. }  
  452. transresult_t CSockWrap::Recv(void *ptr, int nbytes )  
  453. {  
  454.     transresult_t rt;  
  455.     SocketRecv(m_hSocket, (char *)ptr, nbytes,rt);  
  456.     return rt;  
  457. }  
  458. transresult_t CSockWrap::TrySend(void *ptr, int nbytes, int milliseconds)  
  459. {  
  460.     transresult_t rt;  
  461.     SocketTrySend(m_hSocket, (const char *)ptr, nbytes,milliseconds, rt);  
  462.     return rt;  
  463. }  
  464. transresult_t CSockWrap::TryRecv(void *ptr, int nbytes, int  milliseconds )  
  465. {  
  466.     transresult_t rt;  
  467.     SocketTryRecv(m_hSocket, (char *)ptr, nbytes,milliseconds, rt);  
  468.     return rt;  
  469. }  
  470.   
  471. void CSockWrap::ClearRecvBuffer()  
  472. {  
  473.     SocketClearRecvBuffer(m_hSocket);  
  474. }  
#include "platform.h"

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include "lightthread.h"
#include "sock_wrap.h"
#include "TimeSpan.h"

#define INVALIDSOCKHANDLE   INVALID_SOCKET

#if defined(_WIN32_PLATFROM_)
#include <windows.h>
#define ISSOCKHANDLE(x)  (x!=INVALID_SOCKET)
#define BLOCKREADWRITE      0
#define NONBLOCKREADWRITE   0
#define SENDNOSIGNAL        0
#define ETRYAGAIN(x)     (x==WSAEWOULDBLOCK||x==WSAETIMEDOUT)
#define gxsprintf   sprintf_s

#endif


#if defined(_LINUX_PLATFORM_)
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define ISSOCKHANDLE(x)    (x>0)
#define BLOCKREADWRITE      MSG_WAITALL
#define NONBLOCKREADWRITE   MSG_DONTWAIT
#define SENDNOSIGNAL        MSG_NOSIGNAL
#define ETRYAGAIN(x)        (x==EAGAIN||x==EWOULDBLOCK)
#define gxsprintf           snprintf

#endif


void GetAddressFrom(sockaddr_in *addr, const char *ip, int port)
{
    memset(addr, 0, sizeof(sockaddr_in));
    addr->sin_family = AF_INET;            /*地址类型为AF_INET*/
    if(ip)
    {
        addr->sin_addr.s_addr = inet_addr(ip);
    }
    else
    {
        /*网络地址为INADDR_ANY,这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP地址,
        这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP地址*/
        addr->sin_addr.s_addr = htonl(INADDR_ANY);
    }
    addr->sin_port = htons(port);   /*端口号*/
}
void GetIpAddress(char *ip, sockaddr_in *addr)
{
    unsigned char *p =(unsigned char *)( &(addr->sin_addr));
    gxsprintf(ip, 17, "%u.%u.%u.%u", *p,*(p+1), *(p+2), *(p+3) );
}

int GetLastSocketError()
{
#if defined(_WIN32_PLATFROM_)
    return WSAGetLastError();
#endif

#if defined(_LINUX_PLATFORM_)
    return errno;
#endif
}

bool IsValidSocketHandle(HSocket handle)
{
    return ISSOCKHANDLE(handle);
}

void SocketClose(HSocket &handle)
{
    if(ISSOCKHANDLE(handle))
    {
#if defined(_WIN32_PLATFROM_)
        closesocket(handle);
#endif

#if defined(_LINUX_PLATFORM_)
        close(handle);
#endif
        handle = INVALIDSOCKHANDLE;
    }
}

HSocket SocketOpen(int tcpudp)
{
    int protocol = 0;
    HSocket hs;
#if defined(_WIN32_PLATFROM_)
    if(tcpudp== SOCK_STREAM) protocol=IPPROTO_TCP;
    else if (tcpudp== SOCK_DGRAM) protocol = IPPROTO_UDP;
#endif
    hs = socket(AF_INET, tcpudp, protocol);
    return hs;
}
int SocketBind(HSocket hs, sockaddr_in *paddr)
{
    return bind(hs, (struct sockaddr *)paddr, sizeof(sockaddr_in));
}
int SocketListen(HSocket hs, int maxconn)
{
    return listen(hs,maxconn);
}
HSocket SocketAccept(HSocket hs, sockaddr_in *paddr)
{
#if defined(_WIN32_PLATFROM_)
    int cliaddr_len = sizeof(sockaddr_in);
#endif
#if defined(_LINUX_PLATFORM_)
    socklen_t cliaddr_len = sizeof(sockaddr_in);
#endif
    return accept(hs, (struct sockaddr *)paddr, &cliaddr_len);
}
//
// if timeout occurs, nbytes=-1, nresult=1
// if socket error, nbyte=-1, nresult=-1
// if the other side has disconnected in either block mode or nonblock mode, nbytes=0, nresult=-1
// otherwise nbytes= the count of bytes sent , nresult=0
void SocketSend(HSocket hs, const char *ptr, int nbytes, transresult_t &rt)
{
    rt.nbytes = 0;
    rt.nresult = 0;
    if(!ptr|| nbytes<1) return;

    //Linux: flag can be MSG_DONTWAIT, MSG_WAITALL, 使用MSG_WAITALL的时候, socket 必须是处于阻塞模式下,否则WAITALL不能起作用
    rt.nbytes = send(hs, ptr, nbytes, BLOCKREADWRITE|SENDNOSIGNAL);
    if(rt.nbytes>0)
    {
        rt.nresult = (rt.nbytes == nbytes)?0:1;
    }
    else if(rt.nbytes==0)
    {
       rt.nresult=-1;
    }
    else
    {
        rt.nresult = GetLastSocketError();
        rt.nresult = ETRYAGAIN(rt.nresult)? 1:-1;
    }
}



// if timeout occurs, nbytes=-1, nresult=1
// if socket error, nbyte=-1, nresult=-1
// if the other side has disconnected in either block mode or nonblock mode, nbytes=0, nresult=-1
void SocketRecv(HSocket hs, char *ptr, int nbytes, transresult_t &rt)
{
    rt.nbytes = 0;
    rt.nresult = 0;
    if(!ptr|| nbytes<1) return;

    rt.nbytes = recv(hs, ptr, nbytes, BLOCKREADWRITE);
    if(rt.nbytes>0)
    {
        return;
    }
    else if(rt.nbytes==0)
    {
       rt.nresult=-1;
    }
    else
    {
        rt.nresult = GetLastSocketError();
        rt.nresult = ETRYAGAIN(rt.nresult)? 1:-1;
    }

}
//  nbytes= the count of bytes sent
// if timeout occurs, nresult=1
// if socket error,  nresult=-1,
// if the other side has disconnected in either block mode or nonblock mode, nresult=-2
void SocketTrySend(HSocket hs, const char *ptr, int nbytes, int milliseconds, transresult_t &rt)
{
    rt.nbytes = 0;
    rt.nresult = 0;
    if(!ptr|| nbytes<1) return;


    int n;
    CMyTimeSpan start;
    while(1)
    {
        n = send(hs, ptr+rt.nbytes, nbytes, NONBLOCKREADWRITE|SENDNOSIGNAL);
        if(n>0)
        {
            rt.nbytes += n;
            nbytes -= n;
            if(rt.nbytes >= nbytes) {    rt.nresult = 0;  break; }
        }
        else if( n==0)
        {
            rt.nresult= -2;
            break;
        }
        else
        {
            n = GetLastSocketError();
            if(ETRYAGAIN(n))
            {
                CLightThread::DiscardTimeSlice();
            }
            else
            {
                rt.nresult = -1;
                break;
            }
        }
        if(start.GetSpaninMilliseconds()>milliseconds)  { rt.nresult= 1; break;}
    }
}
// if timeout occurs, nbytes=-1, nresult=1
// if socket error, nbyte=-1, nresult=-1
// if the other side has disconnected in either block mode or nonblock mode, nbytes=0, nresult=-1
void SocketTryRecv(HSocket hs, char *ptr, int nbytes, int milliseconds, transresult_t &rt)
{
    rt.nbytes = 0;
    rt.nresult = 0;
    if(!ptr|| nbytes<1) return;

    if(milliseconds>2)
    {
        CMyTimeSpan start;
        while(1)
        {
            rt.nbytes = recv(hs, ptr, nbytes, NONBLOCKREADWRITE);
            if(rt.nbytes>0)
            {
               break;
            }
            else if(rt.nbytes==0)
            {
                rt.nresult = -1;
                break;
            }
            else
            {
                rt.nresult = GetLastSocketError();
                if( ETRYAGAIN(rt.nresult))
                {
                   if(start.GetSpaninMilliseconds()>milliseconds)  { rt.nresult= 1; break;}
                   CLightThread::DiscardTimeSlice();
                }
                else
                {
                    rt.nresult = -1;
                    break;
                }
            }

        }
    }
    else
    {
        SocketRecv(hs, ptr, nbytes, rt);
    }
}

void SocketClearRecvBuffer(HSocket hs)
{
#if defined(_WIN32_PLATFROM_)
    struct timeval tmOut;
    tmOut.tv_sec = 0;
    tmOut.tv_usec = 0;
    fd_set    fds;
    FD_ZERO(&fds);
    FD_SET(hs, &fds);
    int   nRet = 1;
    char tmp[100];
    int rt;
    while(nRet>0)
    {
        nRet= select(FD_SETSIZE, &fds, NULL, NULL, &tmOut);
        if(nRet>0)
        {
           nRet = recv(hs, tmp, 100,0);
        }
    }
#endif

#if defined(_LINUX_PLATFORM_)
   char tmp[100];
   while(recv(hs, tmp, 100, NONBLOCKREADWRITE)> 0);
#endif
}

int SocketBlock(HSocket hs, bool bblock)
{
    unsigned long mode;
    if( ISSOCKHANDLE(hs))
    {
#if defined(_WIN32_PLATFROM_)
        mode = bblock?0:1;
        return ioctlsocket(hs,FIONBIO,&mode);
#endif

#if defined(_LINUX_PLATFORM_)
        mode = fcntl(hs, F_GETFL, 0);                  //获取文件的flags值。
        //设置成阻塞模式      非阻塞模式
        return bblock?fcntl(hs,F_SETFL, mode&~O_NONBLOCK): fcntl(hs, F_SETFL, mode | O_NONBLOCK);
#endif
    }
    return -1;
}

int SocketTimeOut(HSocket hs, int recvtimeout, int sendtimeout, int lingertimeout)   //in milliseconds
{
    int rt=-1;
    if (ISSOCKHANDLE(hs) )
    {
        rt=0;
#if defined(_WIN32_PLATFROM_)
        if(lingertimeout>-1)
        {
            struct linger  lin;
            lin.l_onoff = lingertimeout;
            lin.l_linger = lingertimeout ;
            rt = setsockopt(hs,SOL_SOCKET,SO_DONTLINGER,(const char*)&lin,sizeof(linger)) == 0 ? 0:0x1;
        }
        if(recvtimeout>0 && rt == 0)
        {
            rt = rt | (setsockopt(hs,SOL_SOCKET,SO_RCVTIMEO,(char *)&recvtimeout,sizeof(int))==0?0:0x2);
        }
        if(sendtimeout>0 && rt == 0)
        {
            rt = rt | (setsockopt(hs,SOL_SOCKET, SO_SNDTIMEO, (char *)&sendtimeout,sizeof(int))==0?0:0x4);
        }
#endif

#if defined(_LINUX_PLATFORM_)
   struct timeval timeout;
        if(lingertimeout>-1)
        {
            struct linger  lin;
            lin.l_onoff = lingertimeout>0?1:0;
            lin.l_linger = lingertimeout/1000 ;
            rt = setsockopt(hs,SOL_SOCKET,SO_LINGER,(const char*)&lin,sizeof(linger)) == 0 ? 0:0x1;
        }
        if(recvtimeout>0 && rt == 0)
        {
            timeout.tv_sec = recvtimeout/1000;
            timeout.tv_usec = (recvtimeout % 1000)*1000;
            rt = rt | (setsockopt(hs,SOL_SOCKET,SO_RCVTIMEO,&timeout,sizeof(timeout))==0?0:0x2);
        }
        if(sendtimeout>0 && rt == 0)
        {
            timeout.tv_sec = sendtimeout/1000;
            timeout.tv_usec = (sendtimeout % 1000)*1000;
            rt = rt | (setsockopt(hs,SOL_SOCKET, SO_SNDTIMEO, &timeout,sizeof(timeout))==0?0:0x4);
        }
#endif
    }
    return rt;
}


int InitializeSocketEnvironment()
{
#if defined(_WIN32_PLATFROM_)
    WSADATA  Ws;
    //Init Windows Socket
    if ( WSAStartup(MAKEWORD(2,2), &Ws) != 0 )
    {
        return -1;
    }
#endif
    return 0;
}
void FreeSocketEnvironment()
{
#if defined(_WIN32_PLATFROM_)
    WSACleanup();
#endif
}
//==============================================================================================================
//================================================================================================================
CSockWrap::CSockWrap(int tcpudp)
{
    memset(&m_stAddr, 0, sizeof(sockaddr_in));
    m_tcpudp = tcpudp;
    m_hSocket = INVALIDSOCKHANDLE;
    Reopen(false);
}


CSockWrap::~CSockWrap()
{
    SocketClose(m_hSocket);
}
void CSockWrap::Reopen(bool bForceClose)
{

    if (ISSOCKHANDLE(m_hSocket) && bForceClose) SocketClose(m_hSocket);
    if (!ISSOCKHANDLE(m_hSocket) )
    {
        m_hSocket=SocketOpen(m_tcpudp);
    }

}
void CSockWrap::SetAddress(const char *ip, int port)
{
    GetAddressFrom(&m_stAddr, ip, port);
}
void CSockWrap::SetAddress(sockaddr_in *addr)
{
    memcpy(&m_stAddr, addr, sizeof(sockaddr_in));
}

int CSockWrap::SetTimeOut(int recvtimeout, int sendtimeout, int lingertimeout)   //in milliseconds
{
  return SocketTimeOut(m_hSocket, recvtimeout, sendtimeout, lingertimeout);
}

int CSockWrap::SetBufferSize(int recvbuffersize, int sendbuffersize)   //in bytes
{
    int rt=-1;
    if (ISSOCKHANDLE(m_hSocket) )
    {
#if defined(_WIN32_PLATFROM_)
        if(recvbuffersize>-1)
        {
            rt = setsockopt( m_hSocket, SOL_SOCKET, SO_RCVBUF, ( const char* )&recvbuffersize, sizeof( int ) );
        }
        if(sendbuffersize>-1)
        {
            rt = rt | (setsockopt(m_hSocket,SOL_SOCKET,SO_SNDBUF,(char *)&sendbuffersize,sizeof(int))==0?0:0x2);
        }
#endif
    }
    return rt;
}

int CSockWrap::SetBlock(bool bblock)
{
    return SocketBlock(m_hSocket, bblock);
}
transresult_t CSockWrap::Send(void *ptr, int nbytes)
{
    transresult_t rt;
    SocketSend(m_hSocket, (const char *)ptr, nbytes,rt);
    return rt;
}
transresult_t CSockWrap::Recv(void *ptr, int nbytes )
{
    transresult_t rt;
    SocketRecv(m_hSocket, (char *)ptr, nbytes,rt);
    return rt;
}
transresult_t CSockWrap::TrySend(void *ptr, int nbytes, int milliseconds)
{
    transresult_t rt;
    SocketTrySend(m_hSocket, (const char *)ptr, nbytes,milliseconds, rt);
    return rt;
}
transresult_t CSockWrap::TryRecv(void *ptr, int nbytes, int  milliseconds )
{
    transresult_t rt;
    SocketTryRecv(m_hSocket, (char *)ptr, nbytes,milliseconds, rt);
    return rt;
}

void CSockWrap::ClearRecvBuffer()
{
    SocketClearRecvBuffer(m_hSocket);
}

上面的辅助程序实际上包含了对一些常用的socket函数的封装和一个类CSockWrap,如果需要自己组建通讯逻辑,可以直接用这些C风格的函数,CSockWrap实际上就是这样一个应用。发送和接收函数的返回值有点复杂,是一个结构体transresult_t,本文的意思是,如果发生接收/发送错误,直接从函数的返回值大致判断下一步的动作。


四、关于Socket通讯过程的一些讨论

1.关于send函数。Socket中Send函数的意思是只要将应用程序的数据发送到网卡中就算成功,将发送端的网线拔掉与将接收端的网线拔掉,Send函数的返回可能不同,因此它的正常返回不能作为接收方是否收到的判断条件。如果需要确保对方收到信息,只能采用应答式,但这样做可能会降低双方的通讯效率。一般情况下,Send不会阻塞,除非网卡的发送缓冲区已经满了(发送端直接掉线)。

2.关于recv函数。Recv是最常用的阻塞函数,但通常情况下,应设置其为非阻塞(windows将整个Socket连接都设为非阻塞,linux可以有两种方式),因为,如果发送方已经掉线,或者还需要干别的事情,让Recv阻塞显然是不合适的。当然,也可以不用Recv,而用非阻塞的Select函数(本文没有涉及Select函数),其实它们的效果是一样的。

3.关于从send和recv函数的返回值来初步判断网络状态,见SocketSend等函数的注释。

4.采用UDP通讯时,数据包的内容不宜过大,所以UDP特别适合于命令的传输(一次的通讯量小,但可能频繁)。

5.SocketClearRecvBuffer函数一般用于TCP连接,当接收方发觉由”丢包“时,作为”对齐“信息包之用。

跨平台socket C封装类是一种能够在不同操作系统平台上使用的通信库,用于简化网络编程过程中的底层socket操作。它提供了一系列的函数和类,使得开发者可以更方便地创建、连接、读写和关闭socket连接。 跨平台socket C封装类的设计目标是保持平台无关性,即无论在何种操作系统上编译和运行,都能够统一调用相同的接口,实现相同的功能。它兼容各种主流的操作系统,如WindowsLinux和MacOS,可以在这些平台下编写通用的网络应用程序。 在使用跨平台socket C封装类时,开发者只需要通过简单的API调用,即可完成各种socket操作,无需关心不同操作系统的细节。例如,创建一个socket连接只需调用一两个函数,并指定连接所需的IP地址和端口号即可。而读写数据只需使用简单的函数即可完成。 通过跨平台socket C封装类,开发者可以实现跨平台网络应用程序,可以编写各种类型的客户端和服务器程序,实现网络通信、文件传输、实时消息推送等功能。无论是开发网页、移动应用还是桌面程序,只需在不同平台使用相同的封装类接口,即可实现统一的网络通信功能。 综上所述,跨平台socket C封装类是一种能够在不同操作系统平台上使用的通信库,可以大大简化网络编程中的底层socket操作,提供统一的接口,使开发者能够更方便地实现网络应用程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值