包含到cocos2d-x里的tcpsocket源码

               

转自:http://www.cnblogs.com/elephant-x/archive/2013/09/06/3304903.html

声明:本文参考了langresser发布的blog“跨平台的游戏客户端Socket封装 

Socket处理是异步非阻塞的,所以可以放心的放到主线程处理消息,并且在原作者的基本上进行了系列优化,考虑了客户端可能建立多个SOCKET,因此加入了Manager概念,与cocos2d-x进行了融合。

本文基于cocos2d-x3.0+VS2012

点击下载:network.zip

文件目录结构截图:

 

文件源码:

TCPSocket.h

复制代码
  1 #ifndef __CC_TCPSOCKET_H__  2 #define __CC_TCPSOCKET_H__  3   4 #include "cocos2d.h"  5 #include "ExtensionMacros.h"  6 #include "WorldPacket.h"  7 #ifdef WIN32  8 #include <windows.h>  9 #include <WinSock.h> 10 #pragma comment( lib, "ws2_32.lib" ) 11 #else 12 #include <sys/socket.h> 13 #include <fcntl.h> 14 #include <errno.h> 15 #include <netinet/in.h> 16 #include <arpa/inet.h> 17  18 #define SOCKET int 19 #define SOCKET_ERROR -1 20 #define INVALID_SOCKET -1 21  22 #endif 23  24 NS_CC_EXT_BEGIN 25 #ifndef CHECKF 26 #define CHECKF(x) \ 27     do \ 28 { \ 29     if (!(x)) { \ 30     log_msg("CHECKF", #x, __FILE__, __LINE__); \ 31     return 0; \ 32     } \ 33 } while (0) 34 #endif 35  36 #define _MAX_MSGSIZE 16 * 1024        // 暂定一个消息最大为16k 37 #define BLOCKSECONDS    30            // INIT函数阻塞时间 38 #define INBUFSIZE    (64*1024)        //?    具体尺寸根据剖面报告调整  接收数据的缓存 39 #define OUTBUFSIZE    (8*1024)        //? 具体尺寸根据剖面报告调整。 发送数据的缓存,当不超过8K时,FLUSH只需要SEND一次 40  41 class CC_DLL TCPSocket 42 { 43 public: 44     TCPSocket(void); 45     bool    Create(const char* pszServerIP, int nServerPort, int tagid, int nBlockSec = BLOCKSECONDS, bool bKeepAlive = false); 46     bool    SendMsg(void* pBuf, int nSize); 47     bool    ReceiveMsg(void* pBuf, int& nSize); 48     bool    Flush(void); 49     bool    Check(void); 50     void    Destroy(void); 51     SOCKET    GetSocket(void) const { return m_sockClient; } 52      53     int        getTagID(){ return m_tag; } 54 private: 55     bool    recvFromSock(void);        // 从网络中读取尽可能多的数据 56     bool    hasError();            // 是否发生错误,注意,异步模式未完成非错误 57     void    closeSocket(); 58  59     SOCKET    m_sockClient; 60  61     // 发送数据缓冲 62     char    m_bufOutput[OUTBUFSIZE];    //? 可优化为指针数组 63     int        m_nOutbufLen; 64  65     // 环形缓冲区 66     char    m_bufInput[INBUFSIZE]; 67     int        m_nInbufLen; 68     int        m_nInbufStart;                // INBUF使用循环式队列,该变量为队列起点,0 - (SIZE-1) 69     int        m_tag; 70 }; 71  72 typedef std::function<bool(int,int,WorldPacket&)> ProAllFunc;    // 接收所有协议,自行处理,@socket标识,@协议头,@数据包,返回是否分发 73 typedef std::function<void(int,WorldPacket&)> ProFunc;    // 接收单个协议,@socket标识,@数据包 74 typedef std::function<void(int)> sckFunc;    // 连接成功/断开事件 75  76 #define SCT_CALLBACK_1(func, _Object) std::bind(&func,_Object, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) 77 #define SCT_CALLBACK_2(func, _Object) std::bind(&func,_Object, std::placeholders::_1, std::placeholders::_2) 78 #define SCT_CALLBACK_3(func, _Object) std::bind(&func,_Object, std::placeholders::_1) 79 // 创建SOCKET管理器 80 #define CREATE_TCPSOCKETMGR(pNode)    pNode->addChild(new TCPSocketManager(), 0) 81  82 class CC_DLL TCPSocketManager : 83     public Node 84 { 85 public: 86     TCPSocketManager() 87     {  88         assert(!mSingleton); 89         this->mSingleton = this; 90         // 开启update 91         scheduleUpdate();  92     }; 93     ~TCPSocketManager(){}; 94     // 创建socket并添加到管理器 95     TCPSocket *createSocket(const char* pszServerIP,    // IP地址 96                             int nServerPort,            // 端口 97                             int _tag,                    // 标识ID 98                             int nBlockSec = BLOCKSECONDS, // 阻塞时间ms 99                             bool bKeepAlive = false);100     // 注册协议包101     void    register_process(const uint16 &entry, ProFunc callback);102     // 注册接收所有协议103     void    register_all_process(ProAllFunc callback){ _pProcess = callback; }104     // 注册socket连接成功事件105     void    register_connect(sckFunc callback){ _pOnConnect = callback; }106     // 注册socket断线事件107     void    register_disconnect(sckFunc callback){ _OnDisconnect = callback; }108 109     // 单独添加socket到管理器110     bool    addSocket(TCPSocket *pSocket);111     // 删除socket112     bool    removeSocket(int _tag);113     // 断开socket114     void    disconnect(int _tag);115     // 获取socket116     TCPSocket    *GetSocket(int _tag);117     // 发送消息118     bool    SendPacket(int _tag, WorldPacket *packet);119 120     void    update(float delta);121 122     static TCPSocketManager &getSingleton(){ assert(mSingleton); return *mSingleton;}123 124 private:    125     ProAllFunc _pProcess;126     sckFunc _pOnConnect;127     sckFunc _OnDisconnect;128     std::list<TCPSocket*> m_lstSocket;129     std::map<uint16, ProFunc> _mapProcess;130     static TCPSocketManager * mSingleton;131 };132 133 #define sSocketMgr    TCPSocketManager::getSingleton()134 NS_CC_EXT_END135 136 #endif //__CC_TCPSOCKET_H__
复制代码

TCPSocket.cpp

复制代码
#include "TCPSocket.h"#include "support/zip_support/unzip.h"NS_CC_EXT_BEGINTCPSocket::TCPSocket(){     // 初始化    memset(m_bufOutput, 0, sizeof(m_bufOutput));    memset(m_bufInput, 0, sizeof(m_bufInput));}void TCPSocket::closeSocket(){#ifdef WIN32    closesocket(m_sockClient);    WSACleanup();#else    close(m_sockClient);#endif}bool TCPSocket::Create(const char* pszServerIP, int nServerPort, int tagid, int nBlockSec, bool bKeepAlive /*= FALSE*/){    // 检查参数    if(pszServerIP == 0 || strlen(pszServerIP) > 15) {        return false;    }#ifdef WIN32    WSADATA wsaData;    WORD version = MAKEWORD(2, 0);    int ret = WSAStartup(version, &wsaData);//win sock start up    if (ret != 0) {        return false;    }#endif    // 创建主套接字    m_sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);    if(m_sockClient == INVALID_SOCKET) {        closeSocket();        return false;    }    // 设置SOCKET为KEEPALIVE    if(bKeepAlive)    {        int        optval=1;        if(setsockopt(m_sockClient, SOL_SOCKET, SO_KEEPALIVE, (char *) &optval, sizeof(optval)))        {            closeSocket();            return false;        }    }#ifdef WIN32    DWORD nMode = 1;    int nRes = ioctlsocket(m_sockClient, FIONBIO, &nMode);    if (nRes == SOCKET_ERROR) {        closeSocket();        return false;    }#else    // 设置为非阻塞方式    fcntl(m_sockClient, F_SETFL, O_NONBLOCK);#endif    unsigned long serveraddr = inet_addr(pszServerIP);    if(serveraddr == INADDR_NONE)    // 检查IP地址格式错误    {        closeSocket();        return false;    }    sockaddr_in    addr_in;    memset((void *)&addr_in, 0, sizeof(addr_in));    addr_in.sin_family = AF_INET;    addr_in.sin_port = htons(nServerPort);    addr_in.sin_addr.s_addr = serveraddr;        if(connect(m_sockClient, (sockaddr *)&addr_in, sizeof(addr_in)) == SOCKET_ERROR) {        if (hasError()) {            closeSocket();            return false;        }        else    // WSAWOLDBLOCK        {            timeval timeout;            timeout.tv_sec    = nBlockSec;            timeout.tv_usec    = 0;            fd_set writeset, exceptset;            FD_ZERO(&writeset);            FD_ZERO(&exceptset);            FD_SET(m_sockClient, &writeset);            FD_SET(m_sockClient, &exceptset);            int ret = select(FD_SETSIZE, NULL, &writeset, &exceptset, &timeout);            if (ret == 0 || ret < 0) {                closeSocket();                return false;            } else    // ret > 0            {                ret = FD_ISSET(m_sockClient, &exceptset);                if(ret)        // or (!FD_ISSET(m_sockClient, &writeset)                {                    closeSocket();                    return false;                }            }        }    }    m_nInbufLen        = 0;    m_nInbufStart    = 0;    m_nOutbufLen    = 0;    struct linger so_linger;    so_linger.l_onoff = 1;    so_linger.l_linger = 500;    setsockopt(m_sockClient, SOL_SOCKET, SO_LINGER, (const char*)&so_linger, sizeof(so_linger));    m_tag = tagid;    return true;}bool TCPSocket::SendMsg(void* pBuf, int nSize){    if(pBuf == 0 || nSize <= 0) {        return false;    }    if (m_sockClient == INVALID_SOCKET) {        return false;    }    // 检查通讯消息包长度    int packsize = 0;    packsize = nSize;    // 检测BUF溢出    if(m_nOutbufLen + nSize > OUTBUFSIZE) {        // 立即发送OUTBUF中的数据,以清空OUTBUF。        Flush();        if(m_nOutbufLen + nSize > OUTBUFSIZE) {            // 出错了            Destroy();            return false;        }    }    // 数据添加到BUF尾    memcpy(m_bufOutput + m_nOutbufLen, pBuf, nSize);    m_nOutbufLen += nSize;    return true;}bool TCPSocket::ReceiveMsg(void* pBuf, int& nSize){    //检查参数    if(pBuf == NULL || nSize <= 0) {        return false;    }        if (m_sockClient == INVALID_SOCKET) {        return false;    }    // 检查是否有一个消息(小于2则无法获取到消息长度)    if(m_nInbufLen < 2) {        //  如果没有请求成功  或者   如果没有数据则直接返回        if(!recvFromSock() || m_nInbufLen < 2) {        // 这个m_nInbufLen更新了            return false;        }    }    // 计算要拷贝的消息的大小(一个消息,大小为整个消息的第一个16字节),因为环形缓冲区,所以要分开计算    int packsize = (unsigned char)m_bufInput[m_nInbufStart+2] +        (unsigned char)m_bufInput[(m_nInbufStart + 3) % INBUFSIZE] * 256; // 注意字节序,高位+低位    // 检测消息包尺寸错误 暂定最大16k    if (packsize <= 0 || packsize > _MAX_MSGSIZE) {        m_nInbufLen = 0;        // 直接清空INBUF        m_nInbufStart = 0;        return false;    }    // 检查消息是否完整(如果将要拷贝的消息大于此时缓冲区数据长度,需要再次请求接收剩余数据)    if (packsize > m_nInbufLen) {        // 如果没有请求成功   或者    依然无法获取到完整的数据包  则返回,直到取得完整包        if (!recvFromSock() || packsize > m_nInbufLen) {    // 这个m_nInbufLen已更新            return false;        }    }    // 复制出一个消息    if(m_nInbufStart + packsize > INBUFSIZE) {        // 如果一个消息有回卷(被拆成两份在环形缓冲区的头尾)        // 先拷贝环形缓冲区末尾的数据        int copylen = INBUFSIZE - m_nInbufStart;        memcpy(pBuf, m_bufInput + m_nInbufStart, copylen);        // 再拷贝环形缓冲区头部的剩余部分        memcpy((unsigned char *)pBuf + copylen, m_bufInput, packsize - copylen);        nSize = packsize;    } else {        // 消息没有回卷,可以一次拷贝出去        memcpy(pBuf, m_bufInput + m_nInbufStart, packsize);        nSize = packsize;    }    // 重新计算环形缓冲区头部位置    m_nInbufStart = (m_nInbufStart + packsize) % INBUFSIZE;    m_nInbufLen -= packsize;    return    true;}bool TCPSocket::hasError(){#ifdef WIN32    int err = WSAGetLastError();    if(err != WSAEWOULDBLOCK) {#else    int err = errno;    if(err != EINPROGRESS && err != EAGAIN) {#endif        return true;    }    return false;}// 从网络中读取尽可能多的数据,实际向服务器请求数据的地方bool TCPSocket::recvFromSock(void){    if (m_nInbufLen >= INBUFSIZE || m_sockClient == INVALID_SOCKET) {        return false;    }    // 接收第一段数据    int    savelen, savepos;            // 数据要保存的长度和位置    if(m_nInbufStart + m_nInbufLen < INBUFSIZE)    {    // INBUF中的剩余空间有回绕        savelen = INBUFSIZE - (m_nInbufStart + m_nInbufLen);        // 后部空间长度,最大接收数据的长度    } else {        savelen = INBUFSIZE - m_nInbufLen;    }    // 缓冲区数据的末尾    savepos = (m_nInbufStart + m_nInbufLen) % INBUFSIZE;    //CHECKF(savepos + savelen <= INBUFSIZE);    int inlen = recv(m_sockClient, m_bufInput + savepos, savelen, 0);    if(inlen > 0) {        // 有接收到数据        m_nInbufLen += inlen;                if (m_nInbufLen > INBUFSIZE) {            return false;        }        // 接收第二段数据(一次接收没有完成,接收第二段数据)        if(inlen == savelen && m_nInbufLen < INBUFSIZE) {            int savelen = INBUFSIZE - m_nInbufLen;            int savepos = (m_nInbufStart + m_nInbufLen) % INBUFSIZE;            //CHECKF(savepos + savelen <= INBUFSIZE);            inlen = recv(m_sockClient, m_bufInput + savepos, savelen, 0);            if(inlen > 0) {                m_nInbufLen += inlen;                if (m_nInbufLen > INBUFSIZE) {                    return false;                }                } else if(inlen == 0) {                Destroy();                return false;            } else {                // 连接已断开或者错误(包括阻塞)                if (hasError()) {                    Destroy();                    return false;                }            }        }    } else if(inlen == 0) {        Destroy();        return false;    } else {        // 连接已断开或者错误(包括阻塞)        if (hasError()) {            Destroy();            return false;        }    }    return true;}bool TCPSocket::Flush(void)        //? 如果 OUTBUF > SENDBUF 则需要多次SEND(){    if (m_sockClient == INVALID_SOCKET) {        return false;    }    if(m_nOutbufLen <= 0) {        return true;    }        // 发送一段数据    int    outsize;    outsize = send(m_sockClient, m_bufOutput, m_nOutbufLen, 0);    if(outsize > 0) {        // 删除已发送的部分        if(m_nOutbufLen - outsize > 0) {            memcpy(m_bufOutput, m_bufOutput + outsize, m_nOutbufLen - outsize);        }        m_nOutbufLen -= outsize;        if (m_nOutbufLen < 0) {            return false;        }    } else {        if (hasError()) {            Destroy();            return false;        }    }    return true;}bool TCPSocket::Check(void){    // 检查状态    if (m_sockClient == INVALID_SOCKET) {        return false;    }    char buf[1];    int    ret = recv(m_sockClient, buf, 1, MSG_PEEK);    if(ret == 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值