cocos2d-x版本3.9
纯C++Socket,不用依赖其他库,windows mac iphone android 通用
GameSocket.h
#pragma once #include "cocos2d.h" #ifdef _WIN32 #include <windows.h> #include <WinSock.h> #pragma comment(lib,"ws2_32.lib") #else #include <sys/socket.h> #include <fcntl.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h> #define SOCKET int #define SOCKET_ERROR -1 #define INVALID_SOCKET -1 #endif #include "TypeDefine.h" USING_NS_CC; #define BLOCKSECONDS 30 // INIT函数阻塞时间 #define BUFFER_SIZE (40 * 1024)//可以根据实际情况改大小 class GameSocket { public: GameSocket(void); bool Create(const char* pszServerIP,int nServerPort,int nBlockSec = BLOCKSECONDS,bool bKeepAlive = false); bool SendMsg(void* pBuf,int nSize); bool ReceiveMsg(void* pBuf,int& nSize); bool Check(void); void Destroy(void); SOCKET GetSocket(void) const { return m_sockClient; } private: bool recvFromSock(void); // 从网络中读取尽可能多的数据 bool hasError(); // 是否发生错误,注意,异步模式未完成非错误 void closeSocket(); SOCKET m_sockClient; // 发送数据缓冲 char m_bufOutput[BUFFER_SIZE]; //? 可优化为指针数组 int m_nOutbufLen; // 环形缓冲区 char m_bufInput[BUFFER_SIZE]; int m_nInbufLen; };
<pre name="code" class="cpp">#include "GameSocket.h" GameSocket::GameSocket() { // 初始化 memset(m_bufOutput,sizeof(m_bufOutput)); memset(m_bufInput,sizeof(m_bufInput)); } void GameSocket::closeSocket() { #ifdef WIN32 closesocket(m_sockClient); WSACleanup(); #else close(m_sockClient); #endif } bool GameSocket::Create(const char* pszServerIP,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,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_nOutbufLen = 0; struct linger so_linger; so_linger.l_onoff = 1; so_linger.l_linger = 500; setsockopt(m_sockClient,SO_LINGER,(const char*)&so_linger,sizeof(so_linger)); return true; } bool GameSocket::SendMsg(void* pBuf,int nSize) { if(pBuf == 0 || nSize <= 0) { return false; } if (m_sockClient == INVALID_SOCKET) { return false; } ssize_t outsize = send(m_sockClient,(char*)pBuf,nSize,0); if (outsize < 0) { if (hasError()) { Destroy(); return false; } } return true; } bool GameSocket::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; } } // 检测消息包尺寸错误 if (m_nInbufLen <= 0 || m_nInbufLen > BUFFER_SIZE) { m_nInbufLen = 0; // 直接清空INBUF return false; } // 复制出一个消息 memcpy(pBuf,m_bufInput,m_nInbufLen + sizeof(UINT32)); nSize = m_nInbufLen; // 重新计算环形缓冲区头部位置 m_nInbufLen = 0; return true; } bool GameSocket::hasError() { #ifdef WIN32 int err = WSAGetLastError(); if (err != WSAEWOULDBLOCK) { #else int err = errno; if(err != EINPROGRESS && err != EAGAIN) { #endif return false; } return false; } // 从网络中读取尽可能多的数据,实际向服务器请求数据的地方 bool GameSocket::recvFromSock(void) { if (m_nInbufLen >= BUFFER_SIZE || m_sockClient == INVALID_SOCKET) { return false; } // 接收头 我的消息结构是4个字节消息头 存的是消息大小 后面跟的消息体 可以根据实际情况去改 UINT32 nBuffSize = 0; int inlen = recv(m_sockClient,(char*)&nBuffSize,sizeof(UINT32),0); if(inlen > 0) { // 有接收到数据 if (inlen > BUFFER_SIZE) { return false; } //接收消息体 inlen = recv(m_sockClient,nBuffSize + sizeof(UINT32),0); if (inlen > 0) { m_nInbufLen = inlen; if (m_nInbufLen > BUFFER_SIZE) { 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 GameSocket::Check(void) { // 检查状态 if (m_sockClient == INVALID_SOCKET) { return false; } char buf[1]; ssize_t ret = recv(m_sockClient,buf,1,MSG_PEEK); if(ret == 0) { Destroy(); return false; } else if(ret < 0) { if (hasError()) { Destroy(); return false; } else { // 阻塞 return true; } } else { // 有数据 return true; } return true; } void GameSocket::Destroy(void) { // 关闭 struct linger so_linger; so_linger.l_onoff = 1; so_linger.l_linger = 500; int ret = setsockopt(m_sockClient,sizeof(so_linger)); closeSocket(); m_sockClient = INVALID_SOCKET; m_nInbufLen = 0; m_nOutbufLen = 0; memset(m_bufOutput,sizeof(m_bufInput)); }
TypeDefine.h
#ifndef WIN32 typedef char INT8; typedef short INT16; typedef int INT32; typedef long long INT64; typedef unsigned char UINT8; typedef unsigned short UINT16; typedef unsigned int UINT32; typedef unsigned long long UINT64; #endif
1.连接
<pre name="code" class="cpp">GameSocket* m_pSocket = new GameSocket(); bool isCreate = m_pSocket->Create([ip],[port]); if (!isCreate) { CCLOG(" socket connect[%s:%d] fail!",[ip],[port]); }
2.发消息
bool isSend = m_pSocket->SendMsg([buffer],[buffSize]); if (!isSend) { CCLOG("send world message fail!"); }
3.接收消息
<pre name="code" class="cpp">// 接收消息处理(放到游戏主循环中,每帧处理) if (m_pSocket == nullptr) { return; } if (!m_pSocket->Check()) { m_pSocket = nullptr; // 掉线了 return; } // 接收数据(取得缓冲区中的所有消息,直到缓冲区为空) while (true) { char pbufMsg[BUFFER_SIZE] = { 0 }; int nSize = sizeof(pbufMsg); if (m_pSocket == nullptr) { break; } if (!m_pSocket->ReceiveMsg(pbufMsg,nSize)) { break; } //pbufMsg,nSize 这是接收回来的消息内容和大小 根据实际情况进行处理 }