cocos2d-x socket


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;

};



GameSocket.cpp

<pre name="code" class="cpp">#include "GameSocket.h"

GameSocket::GameSocket()
{
    // 初始化
    memset(m_bufOutput, 0, sizeof(m_bufOutput));
    memset(m_bufInput, 0, sizeof(m_bufInput));
}

void GameSocket::closeSocket()
{
#ifdef WIN32
    closesocket(m_sockClient);
    WSACleanup();
#else
    close(m_sockClient);
#endif
}

bool GameSocket::Create(const char* pszServerIP, int nServerPort, 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_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));
    
    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, m_bufInput, 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, SOL_SOCKET, SO_LINGER, (const char*)&so_linger, sizeof(so_linger));
        
    closeSocket();
        
    m_sockClient = INVALID_SOCKET;
    m_nInbufLen = 0;
    m_nOutbufLen = 0;
        
    memset(m_bufOutput, 0, sizeof(m_bufOutput));
    memset(m_bufInput, 0, 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 这是接收回来的消息内容和大小 根据实际情况进行处理
	}


 
 

 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值