http://blog.csdn.net/langresser/article/details/8646088





  1. #pragma once

  2. #ifdef WIN32

  3. #include <windows.h>

  4. #include <WinSock.h>

  5. #else

  6. #include <sys/socket.h>

  7. #include <fcntl.h>

  8. #include <errno.h>

  9. #include <netinet/in.h>

  10. #include <arpa/inet.h>

  11. #define SOCKET int

  12. #define SOCKET_ERROR -1

  13. #define INVALID_SOCKET -1

  14. #endif

  15. #ifndef CHECKF

  16. #define CHECKF(x) \

  17. do \  

  18. { \  

  19. if (!(x)) { \  

  20.    log_msg("CHECKF", #x, __FILE__, __LINE__); \  

  21. return 0; \  

  22.    } \  

  23. } while (0)  

  24. #endif

  25. #define _MAX_MSGSIZE 16 * 1024      // 暂定一个消息最大为16k

  26. #define BLOCKSECONDS    30          // INIT函数阻塞时间

  27. #define INBUFSIZE   (64*1024)       //? 具体尺寸根据剖面报告调整  接收数据的缓存

  28. #define OUTBUFSIZE  (8*1024)        //? 具体尺寸根据剖面报告调整。 发送数据的缓存,当不超过8K时,FLUSH只需要SEND一次

  29. class CGameSocket {  

  30. public:  

  31.    CGameSocket(void);  

  32. bool    Create(constchar* pszServerIP, int nServerPort, int nBlockSec = BLOCKSECONDS, bool bKeepAlive = false);  

  33. bool    SendMsg(void* pBuf, int nSize);  

  34. bool    ReceiveMsg(void* pBuf, int& nSize);  

  35. bool    Flush(void);  

  36. bool    Check(void);  

  37. void    Destroy(void);  

  38.    SOCKET  GetSocket(void) const { return m_sockClient; }  

  39. private:  

  40. bool    recvFromSock(void);     // 从网络中读取尽可能多的数据

  41. bool    hasError();         // 是否发生错误,注意,异步模式未完成非错误

  42. void    closeSocket();  

  43.    SOCKET  m_sockClient;  

  44. // 发送数据缓冲

  45. char    m_bufOutput[OUTBUFSIZE];    //? 可优化为指针数组

  46. int     m_nOutbufLen;  

  47. // 环形缓冲区

  48. char    m_bufInput[INBUFSIZE];  

  49. int     m_nInbufLen;  

  50. int     m_nInbufStart;              // INBUF使用循环式队列,该变量为队列起点,0 - (SIZE-1)

  51. };  


  1. #include "stdafx.h"

  2. #include "Socket.h"

  3. CGameSocket::CGameSocket()  

  4. {  

  5. // 初始化

  6.    memset(m_bufOutput, 0, sizeof(m_bufOutput));  

  7.    memset(m_bufInput, 0, sizeof(m_bufInput));  

  8. }  

  9. void CGameSocket::closeSocket()  

  10. {  

  11. #ifdef WIN32

  12.    closesocket(m_sockClient);  

  13.    WSACleanup();  

  14. #else

  15.    close(m_sockClient);  

  16. #endif

  17. }  

  18. bool CGameSocket::Create(constchar* pszServerIP, int nServerPort, int nBlockSec, bool bKeepAlive /*= FALSE*/)  

  19. {  

  20. // 检查参数

  21. if(pszServerIP == 0 || strlen(pszServerIP) > 15) {  

  22. returnfalse;  

  23.    }  

  24. #ifdef WIN32

  25.    WSADATA wsaData;  

  26. WORD version = MAKEWORD(2, 0);  

  27. int ret = WSAStartup(version, &wsaData);//win sock start up

  28. if (ret != 0) {  

  29. returnfalse;  

  30.    }  

  31. #endif

  32. // 创建主套接字

  33.    m_sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  

  34. if(m_sockClient == INVALID_SOCKET) {  

  35.        closeSocket();  

  36. returnfalse;  

  37.    }  

  38. // 设置SOCKET为KEEPALIVE

  39. if(bKeepAlive)  

  40.    {  

  41. int     optval=1;  

  42. if(setsockopt(m_sockClient, SOL_SOCKET, SO_KEEPALIVE, (char *) &optval, sizeof(optval)))  

  43.        {  

  44.            closeSocket();  

  45. returnfalse;  

  46.        }  

  47.    }  

  48. #ifdef WIN32

  49. DWORD nMode = 1;  

  50. int nRes = ioctlsocket(m_sockClient, FIONBIO, &nMode);  

  51. if (nRes == SOCKET_ERROR) {  

  52.        closeSocket();  

  53. returnfalse;  

  54.    }  

  55. #else

  56. // 设置为非阻塞方式

  57.    fcntl(m_sockClient, F_SETFL, O_NONBLOCK);  

  58. #endif

  59.    unsigned long serveraddr = inet_addr(pszServerIP);  

  60. if(serveraddr == INADDR_NONE)   // 检查IP地址格式错误

  61.    {  

  62.        closeSocket();  

  63. returnfalse;  

  64.    }  

  65.    sockaddr_in addr_in;  

  66.    memset((void *)&addr_in, 0, sizeof(addr_in));  

  67.    addr_in.sin_family = AF_INET;  

  68.    addr_in.sin_port = htons(nServerPort);  

  69.    addr_in.sin_addr.s_addr = serveraddr;  

  70. if(connect(m_sockClient, (sockaddr *)&addr_in, sizeof(addr_in)) == SOCKET_ERROR) {  

  71. if (hasError()) {  

  72.            closeSocket();  

  73. returnfalse;  

  74.        }  

  75. else// WSAWOLDBLOCK

  76.        {  

  77.            timeval timeout;  

  78.            timeout.tv_sec  = nBlockSec;  

  79.            timeout.tv_usec = 0;  

  80.            fd_set writeset, exceptset;  

  81.            FD_ZERO(&writeset);  

  82.            FD_ZERO(&exceptset);  

  83.            FD_SET(m_sockClient, &writeset);  

  84.            FD_SET(m_sockClient, &exceptset);  

  85. int ret = select(FD_SETSIZE, NULL, &writeset, &exceptset, &timeout);  

  86. if (ret == 0 || ret < 0) {  

  87.                closeSocket();  

  88. returnfalse;  

  89.            } else// ret > 0

  90.            {  

  91.                ret = FD_ISSET(m_sockClient, &exceptset);  

  92. if(ret)     // or (!FD_ISSET(m_sockClient, &writeset)

  93.                {  

  94.                    closeSocket();  

  95. returnfalse;  

  96.                }  

  97.            }  

  98.        }  

  99.    }  

  100.    m_nInbufLen     = 0;  

  101.    m_nInbufStart   = 0;  

  102.    m_nOutbufLen    = 0;  

  103. struct linger so_linger;  

  104.    so_linger.l_onoff = 1;  

  105.    so_linger.l_linger = 500;  

  106.    setsockopt(m_sockClient, SOL_SOCKET, SO_LINGER, (constchar*)&so_linger, sizeof(so_linger));  

  107. returntrue;  

  108. }  

  109. bool CGameSocket::SendMsg(void* pBuf, int nSize)  

  110. {  

  111. if(pBuf == 0 || nSize <= 0) {  

  112. returnfalse;  

  113.    }  

  114. if (m_sockClient == INVALID_SOCKET) {  

  115. returnfalse;  

  116.    }  

  117. // 检查通讯消息包长度

  118. int packsize = 0;  

  119.    packsize = nSize;  

  120. // 检测BUF溢出

  121. if(m_nOutbufLen + nSize > OUTBUFSIZE) {  

  122. // 立即发送OUTBUF中的数据,以清空OUTBUF。

  123.        Flush();  

  124. if(m_nOutbufLen + nSize > OUTBUFSIZE) {  

  125. // 出错了

  126.            Destroy();  

  127. returnfalse;  

  128.        }  

  129.    }  

  130. // 数据添加到BUF尾

  131.    memcpy(m_bufOutput + m_nOutbufLen, pBuf, nSize);  

  132.    m_nOutbufLen += nSize;  

  133. returntrue;  

  134. }  

  135. bool CGameSocket::ReceiveMsg(void* pBuf, int& nSize)  

  136. {  

  137. //检查参数

  138. if(pBuf == NULL || nSize <= 0) {  

  139. returnfalse;  

  140.    }  

  141. if (m_sockClient == INVALID_SOCKET) {  

  142. returnfalse;  

  143.    }  

  144. // 检查是否有一个消息(小于2则无法获取到消息长度)

  145. if(m_nInbufLen < 2) {  

  146. //  如果没有请求成功  或者   如果没有数据则直接返回

  147. if(!recvFromSock() || m_nInbufLen < 2) {     // 这个m_nInbufLen更新了

  148. returnfalse;  

  149.        }  

  150.    }  

  151. // 计算要拷贝的消息的大小(一个消息,大小为整个消息的第一个16字节),因为环形缓冲区,所以要分开计算

  152. int packsize = (unsigned char)m_bufInput[m_nInbufStart] +  

  153.        (unsigned char)m_bufInput[(m_nInbufStart + 1) % INBUFSIZE] * 256; // 注意字节序,高位+低位

  154. // 检测消息包尺寸错误 暂定最大16k

  155. if (packsize <= 0 || packsize > _MAX_MSGSIZE) {  

  156.        m_nInbufLen = 0;        // 直接清空INBUF

  157.        m_nInbufStart = 0;  

  158. returnfalse;  

  159.    }  

  160. // 检查消息是否完整(如果将要拷贝的消息大于此时缓冲区数据长度,需要再次请求接收剩余数据)

  161. if (packsize > m_nInbufLen) {  

  162. // 如果没有请求成功   或者    依然无法获取到完整的数据包  则返回,直到取得完整包

  163. if (!recvFromSock() || packsize > m_nInbufLen) { // 这个m_nInbufLen已更新

  164. returnfalse;  

  165.        }  

  166.    }  

  167. // 复制出一个消息

  168. if(m_nInbufStart + packsize > INBUFSIZE) {  

  169. // 如果一个消息有回卷(被拆成两份在环形缓冲区的头尾)

  170. // 先拷贝环形缓冲区末尾的数据

  171. int copylen = INBUFSIZE - m_nInbufStart;  

  172.        memcpy(pBuf, m_bufInput + m_nInbufStart, copylen);  

  173. // 再拷贝环形缓冲区头部的剩余部分

  174.        memcpy((unsigned char *)pBuf + copylen, m_bufInput, packsize - copylen);  

  175.        nSize = packsize;  

  176.    } else {  

  177. // 消息没有回卷,可以一次拷贝出去

  178.        memcpy(pBuf, m_bufInput + m_nInbufStart, packsize);  

  179.        nSize = packsize;  

  180.    }  

  181. // 重新计算环形缓冲区头部位置

  182.    m_nInbufStart = (m_nInbufStart + packsize) % INBUFSIZE;  

  183.    m_nInbufLen -= packsize;  

  184. returntrue;  

  185. }  

  186. bool CGameSocket::hasError()  

  187. {  

  188. #ifdef WIN32

  189. int err = WSAGetLastError();  

  190. if(err != WSAEWOULDBLOCK) {  

  191. #else

  192. int err = errno;  

  193. if(err != EINPROGRESS && err != EAGAIN) {  

  194. #endif

  195. returntrue;  

  196.    }  

  197. returnfalse;  

  198. }  

  199. // 从网络中读取尽可能多的数据,实际向服务器请求数据的地方

  200. bool CGameSocket::recvFromSock(void)  

  201. {  

  202. if (m_nInbufLen >= INBUFSIZE || m_sockClient == INVALID_SOCKET) {  

  203. returnfalse;  

  204.    }  

  205. // 接收第一段数据

  206. int savelen, savepos;           // 数据要保存的长度和位置

  207. if(m_nInbufStart + m_nInbufLen < INBUFSIZE)  {   // INBUF中的剩余空间有回绕

  208.        savelen = INBUFSIZE - (m_nInbufStart + m_nInbufLen);        // 后部空间长度,最大接收数据的长度

  209.    } else {  

  210.        savelen = INBUFSIZE - m_nInbufLen;  

  211.    }  

  212. // 缓冲区数据的末尾

  213.    savepos = (m_nInbufStart + m_nInbufLen) % INBUFSIZE;  

  214.    CHECKF(savepos + savelen <= INBUFSIZE);  

  215. int inlen = recv(m_sockClient, m_bufInput + savepos, savelen, 0);  

  216. if(inlen > 0) {  

  217. // 有接收到数据

  218.        m_nInbufLen += inlen;  

  219. if (m_nInbufLen > INBUFSIZE) {  

  220. returnfalse;  

  221.        }  

  222. // 接收第二段数据(一次接收没有完成,接收第二段数据)

  223. if(inlen == savelen && m_nInbufLen < INBUFSIZE) {  

  224. int savelen = INBUFSIZE - m_nInbufLen;  

  225. int savepos = (m_nInbufStart + m_nInbufLen) % INBUFSIZE;  

  226.            CHECKF(savepos + savelen <= INBUFSIZE);  

  227.            inlen = recv(m_sockClient, m_bufInput + savepos, savelen, 0);  

  228. if(inlen > 0) {  

  229.                m_nInbufLen += inlen;  

  230. if (m_nInbufLen > INBUFSIZE) {  

  231. returnfalse;  

  232.                }    

  233.            } elseif(inlen == 0) {  

  234.                Destroy();  

  235. returnfalse;  

  236.            } else {  

  237. // 连接已断开或者错误(包括阻塞)

  238. if (hasError()) {  

  239.                    Destroy();  

  240. returnfalse;  

  241.                }  

  242.            }  

  243.        }  

  244.    } elseif(inlen == 0) {  

  245.        Destroy();  

  246. returnfalse;  

  247.    } else {  

  248. // 连接已断开或者错误(包括阻塞)

  249. if (hasError()) {  

  250.            Destroy();  

  251. returnfalse;  

  252.        }  

  253.    }  

  254. returntrue;  

  255. }  

  256. bool CGameSocket::Flush(void)       //? 如果 OUTBUF > SENDBUF 则需要多次SEND()

  257. {  

  258. if (m_sockClient == INVALID_SOCKET) {  

  259. returnfalse;  

  260.    }  

  261. if(m_nOutbufLen <= 0) {  

  262. returntrue;  

  263.    }  

  264. // 发送一段数据

  265. int outsize;  

  266.    outsize = send(m_sockClient, m_bufOutput, m_nOutbufLen, 0);  

  267. if(outsize > 0) {  

  268. // 删除已发送的部分

  269. if(m_nOutbufLen - outsize > 0) {  

  270.            memcpy(m_bufOutput, m_bufOutput + outsize, m_nOutbufLen - outsize);  

  271.        }  

  272.        m_nOutbufLen -= outsize;  

  273. if (m_nOutbufLen < 0) {  

  274. returnfalse;  

  275.        }  

  276.    } else {  

  277. if (hasError()) {  

  278.            Destroy();  

  279. returnfalse;  

  280.        }  

  281.    }  

  282. returntrue;  

  283. }  

  284. bool CGameSocket::Check(void)  

  285. {  

  286. // 检查状态

  287. if (m_sockClient == INVALID_SOCKET) {  

  288. returnfalse;  

  289.    }  

  290. char buf[1];  

  291. int ret = recv(m_sockClient, buf, 1, MSG_PEEK);  

  292. if(ret == 0) {  

  293.        Destroy();  

  294. returnfalse;  

  295.    } elseif(ret < 0) {  

  296. if (hasError()) {  

  297.            Destroy();  

  298. returnfalse;  

  299.        } else {    // 阻塞

  300. returntrue;  

  301.        }  

  302.    } else {    // 有数据

  303. returntrue;  

  304.    }  

  305. returntrue;  

  306. }