【Cocos2d-x/入门】使用TCPSocket进行通讯

  • 说在前面

操作系统:win10 64位

vs版本:2017

python版本:2.7

Cocos2d-x版本:3.17

  • 关于cocos2sx中的通信

 都不是底层的socket通信;所以要自己写tcp socket通信类,而且要跨android平台的话要借助NDK,里面有各种需要的头文件

 

  • 代码 

可以根据自己的需求进行封装,懒得自己写直接到网上找;

使用的时候,将client定义在AppDelegate中,这样在整个应用的生命周期中都可以使用;


#ifndef  _APP_DELEGATE_H_
#define  _APP_DELEGATE_H_

#include "cocos2d.h"
#include "SocketClient.h"


/**
@brief    The cocos2d Application.

Private inheritance here hides part of interface from Director.
*/
class  AppDelegate : private cocos2d::Application
{
private:

public:

    SocketClient * _client;

    AppDelegate();
    virtual ~AppDelegate();

    virtual void initGLContextAttrs();


    /**
    @brief    Implement Director and Scene init code here.
    @return true    Initialize success, app continue.
    @return false   Initialize failed, app terminate.
    */
    virtual bool applicationDidFinishLaunching();

    /**
    @brief  Called when the application moves to the background
    @param  the pointer of the application
    */
    virtual void applicationDidEnterBackground();

    /**
    @brief  Called when the application reenters the foreground
    @param  the pointer of the application
    */
    virtual void applicationWillEnterForeground();

	/*
	@brief  init socket connect
	*/
    void initConnect();
    void onRecv(const char* data, int count);
    void onDisconnect();

};

#endif // _APP_DELEGATE_H_

void AppDelegate::initConnect()
{
   _client = SocketClient::construct();
   _client->onRecv = CC_CALLBACK_2(AppDelegate::onRecv, this);
   _client->onDisconnect = CC_CALLBACK_0(AppDelegate::onDisconnect, this);
   if (!_client->connectServer("127.0.0.1", 20896))
   {
   	    log("Client connect error");
   }
}

/*do with meassages*/
void AppDelegate::onRecv(const char* data, int count)
{
	u_int PackLength;
	DataBuffer.append(data, count);

	for (;;)
	{
		//这里对data进行处理,count为data的字节长度
	}

}

void AppDelegate::onDisconnect()
{
	log("Client disconnect");
}

 例如在某个场景中需要发送数据时可以这样写:

AppDelegate* app = (AppDelegate *)Application::getInstance();

char head[5] = { 0 };
head[0] = 0x17; head[1] = 0x01;
head[2] = 5; head[3] = 0x00;
head[4] = i1 * 16 + j1;

app->_client->sendMessage(head, sizeof(head));

而在接收到某些数据后需要场景做出反应,可以使用自定义事件来实现;

以注册为例,在注册场景中通过sendMessage()将用户名密码发送给服务器,服务器处理完后把结果返回,在AppDelegate的onrecv函数中

void AppDelegate::regEventDispatcher(u_int type)
{
	int a = type;
	EventCustom event("user_reg_event");
	event.setUserData(static_cast<void*>(&a));
	Director::getInstance()->getEventDispatcher()->dispatchEvent(&event);
}

void AppDelegate::onRecv(const char* data, int count)
{
	u_int PackLength;
	DataBuffer.append(data, count);

	for (;;)
	{
		if (DataBuffer.length() < 4)
			break;

		if ((PackLength = (u_char)DataBuffer[2] + ((u_char)DataBuffer[3]) * 256) > DataBuffer.length())
			break;

		switch ((u_char)DataBuffer[0])
		{
		case 0x72:
			if (DataBuffer[1] < 0x03)
				regEventDispatcher((u_int)DataBuffer[1]);
			else
				changeKeyEventDispatcher((u_int)DataBuffer[1]);
			break;
		default:
			break;
		}

		if (DataBuffer.length() == PackLength)
		{
			DataBuffer = "";
			break;
		}
        else
		    DataBuffer = DataBuffer.substr(PackLength);
	}

}

在reg场景中init函数中定义

_listener = EventListenerCustom::create("user_reg_event", [=](EventCustom* event) {
		int* eventSign = static_cast<int*>(event->getUserData());

		switch (*eventSign)
		{
		case 0:messageBox->setString("Username Existed!"); break;
		case 1:
			messageBox->setString("Success!");
			this->scheduleOnce(schedule_selector(RegScene::EnterLoginScene), 1.0f); break;
		default:
			break;
		}

		//CC_SAFE_DELETE(eventSign);
	});

	Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(_listener, 1);
  • socket 
#ifndef __SOCKET_BASE_H__ 
#define __SOCKET_BASE_H__ 
#include "cocos2d.h" 
#include <list> 
#include <thread> 
USING_NS_CC; 
 
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) 
#include <WinSock2.h> 
#pragma comment(lib, "WS2_32.lib") 
#define HSocket SOCKET 
 
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) 
#include <errno.h>
#include <arpa/inet.h>		// for inet_** 
#include <netdb.h>			// for gethost** 
#include <netinet/in.h>		// for sockaddr_in 
#include <sys/types.h>		// for socket 
#include <sys/socket.h>		// for socket 
#include <unistd.h> 
#include <stdio.h>		    // for printf 
#include <stdlib.h>			// for exit 
#include <string.h>			// for bzero
#include <fcntl.h>
#define HSocket int 
#endif  
 
 
enum MessageType 
{ 
	DISCONNECT, 
	RECEIVE, 
	NEW_CONNECTION 
}; 
 
class SocketMessage 
{ 
private: 
	MessageType msgType; 
	Data* msgData; 
 
public: 
	SocketMessage(MessageType type, unsigned char* data, int dataLen) 
	{ 
		msgType = type; 
		msgData = new Data; 
		msgData->copy(data, dataLen); 
	} 
 
	SocketMessage(MessageType type) 
	{ 
		msgType = type; 
		msgData = nullptr; 
	} 
 
	Data* getMsgData() { return msgData; } 
	MessageType getMsgType() { return msgType; } 
 
	~SocketMessage() 
	{ 
		if (msgData) 
			CC_SAFE_DELETE(msgData); 
	} 
}; 
 
class SocketBase : public Ref 
{ 
public: 
	SocketBase(); 
	~SocketBase(); 
 
	bool nonBlock(HSocket socket); 
 
protected: 
	void closeConnect(HSocket socket); 
	bool error(HSocket socket); 
	 
protected: 
	std::mutex _mutex; 
 
private: 
	bool _bInitSuccess; 
}; 
 
#endif
#include "SocketBase.h"   
   
SocketBase::SocketBase()   
{   
    _bInitSuccess = false;   
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)   
    WORD wVersionRequested;   
    wVersionRequested = MAKEWORD(2, 0);   
    WSADATA wsaData;   
    int nRet = WSAStartup(wVersionRequested, &wsaData);   
    if (nRet != 0)    
    {   
        fprintf(stderr, "Initilize Error!\n");   
        return;   
    }   
    _bInitSuccess = true;   
#endif   
   
}   
   
SocketBase::~SocketBase()   
{   
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)   
    if (_bInitSuccess)   
    {   
        WSACleanup();   
    }   
#endif    
}   
   
void SocketBase::closeConnect(HSocket socket)   
{   
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)   
    close(socket);   
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)   
    closesocket(socket);   
#endif   
}   
   
bool SocketBase::error(HSocket socket)   
{   
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)   
    return socket == SOCKET_ERROR;   
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)   
    return socket < 0;   
#endif   
}   
   
bool SocketBase::nonBlock(HSocket socket)   
{   
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)   
   
    int flags;   
    flags = fcntl(socket, F_GETFL, 0);   
    flags |= O_NONBLOCK;
    if (fcntl(socket, F_SETFL, flags) < 0)   
    {   
        return false;   
    }   
#else   
    u_long ulOn;   
    ulOn = 1;   
    if (ioctlsocket(socket, FIONBIO, &ulOn) == SOCKET_ERROR)   
    {   
        return false;   
    }   
#endif   
   
    return true;   
}  

 

#ifndef __SOCKET_CLIENT_H__ 
#define __SOCKET_CLIENT_H__ 
#include "SocketBase.h" 
 
class SocketClient : public SocketBase 
{ 
public: 
	static SocketClient* construct(); 
	void destroy(); 
 
	bool connectServer(const char* serverIP, unsigned short port); 
	void sendMessage(const char* data, int count); 
 
	std::function<void(const char* data, int count)> onRecv; 
	std::function<void()> onDisconnect; 
 
	void update(float dt); 
 
CC_CONSTRUCTOR_ACCESS: 
	SocketClient(void); 
	~SocketClient(void); 
 
private: 
	bool initClient(); 
	void recvMessage(); 
	void clear(); 
 
private: 
	HSocket _socketServer; 
	HSocket _socektClient; 
	std::list<SocketMessage*> _UIMessageQueue;
	std::mutex   _UIMessageQueueMutex; 
}; 
 
#endif
#include "SocketClient.h"   
   
SocketClient* SocketClient::construct()   
{   
    SocketClient* client = new SocketClient;   
    return client;   
}   
   
void SocketClient::destroy()   
{   
    delete this;   
}   
   
SocketClient::SocketClient(void) :   
    onRecv(nullptr),   
    _socektClient(0)   
{   
}   
   
SocketClient::~SocketClient(void)   
{   
    this->clear();   
}   
   
void SocketClient::clear()   
{   
    if (_socektClient != 0)   
    {   
        _mutex.lock();   
        this->closeConnect(_socektClient);   
        _mutex.unlock();   
    }   
   
    for (auto msg : _UIMessageQueue)   
    {   
        CC_SAFE_DELETE(msg);   
    }   
    _UIMessageQueue.clear();   
   
    Director::getInstance()->getScheduler()->unscheduleAllForTarget(this);   
}   
   
bool SocketClient::initClient()   
{   
    this->clear();   
   
    _socektClient = socket(AF_INET, SOCK_STREAM, 0);   
    if (error(_socketServer))   
    {   
        log("init client error!");   
        _socektClient = 0;   
        return false;   
    }   
       
    Director::getInstance()->getScheduler()->scheduleUpdate(this, 0, false);   
    return true;   
}   
   
bool SocketClient::connectServer(const char* serverIP, unsigned short port)   
{   
    if (!this->initClient())   
    {   
        return false;   
    }   
   
    struct sockaddr_in serverAddr;   
    memset(&serverAddr, 0, sizeof(struct sockaddr_in));   
   
    serverAddr.sin_family = AF_INET;   
    serverAddr.sin_port = htons(port);   
    serverAddr.sin_addr.s_addr = inet_addr(serverIP);   
   
    int ret = 0;   
    ret = connect(_socektClient, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr));   
    if (ret < 0)   
    {   
        _socektClient = 0;   
        return false;   
    }   
   
    std::thread recvThread(&SocketClient::recvMessage, this);   
    recvThread.detach();   
   
    return true;   
}   
   
void SocketClient::recvMessage()   
{   
    char recvBuf[1024];   
    int ret = 0;   
    while (true)   
    {   
        ret = recv(_socektClient, recvBuf, sizeof(recvBuf), 0);   
        if (ret < 0)   
        {   
            log("recv error!");   
            break;   
        }   
        if (ret > 0 && onRecv != nullptr)   
        {   
            std::lock_guard<std::mutex> lk(_UIMessageQueueMutex);  
            SocketMessage * msg = new SocketMessage(RECEIVE, (unsigned char*)recvBuf, ret);  
            _UIMessageQueue.push_back(msg);   
        }   
    }   
    _mutex.lock();   
    this->closeConnect(_socektClient);   
    if (onDisconnect != nullptr)   
    {   
        std::lock_guard<std::mutex> lk(_UIMessageQueueMutex);  
        SocketMessage * msg = new SocketMessage(DISCONNECT);  
        _UIMessageQueue.push_back(msg);   
    }   
    _socektClient = 0;   
    _mutex.unlock();   
}   
   
void SocketClient::sendMessage(const char* data, int count)   
{   
    if (_socektClient != 0)   
    {   
        int ret = send(_socektClient, data, count, 0);   
        if (ret < 0)   
        {   
            log("send error!");   
        }   
    }   
}   
   
void SocketClient::update(float dt)   
{   
    if (_UIMessageQueue.size() == 0)   
    {   
        return;   
    }   
   
    _UIMessageQueueMutex.lock();   
   
    if (_UIMessageQueue.size() == 0)   
    {   
        _UIMessageQueueMutex.unlock();   
        return;   
    }   
   
    SocketMessage *msg = *(_UIMessageQueue.begin());   
    _UIMessageQueue.pop_front();   
   
    switch (msg->getMsgType())   
    {   
    case DISCONNECT:   
        if (onDisconnect)   
        {   
            this->onDisconnect();   
        }   
        break;   
    case RECEIVE:   
        if (onRecv)   
        {   
            this->onRecv((const char*)msg->getMsgData()->getBytes(), msg->getMsgData()->getSize());   
        }   
        break;   
    default:  
        break;   
    }   
   
    CC_SAFE_DELETE(msg);   
    _UIMessageQueueMutex.unlock();   
}  

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值