GVKun编程网logo

[Cocos2d-x]在Cocos2d-x 3.x版本中如何通过WebSocket连接服务器进行数据传输

13

对于想了解[Cocos2d-x]在Cocos2d-x3.x版本中如何通过WebSocket连接服务器进行数据传输的读者,本文将提供新的信息,并且为您提供关于cocos2d--WebSocket分析、c

对于想了解[Cocos2d-x]在Cocos2d-x 3.x版本中如何通过WebSocket连接服务器进行数据传输的读者,本文将提供新的信息,并且为您提供关于cocos2d--WebSocket分析、cocos2d-iphone – 在Cocos2d中替换场景时不会触发dealloc、cocos2d-iphone – 如何在cocos2d中显示CCNode的边框?、cocos2d-iphone – 如何在cocos2d中测试精灵碰撞?的有价值信息。

本文目录一览:

[Cocos2d-x]在Cocos2d-x 3.x版本中如何通过WebSocket连接服务器进行数据传输

[Cocos2d-x]在Cocos2d-x 3.x版本中如何通过WebSocket连接服务器进行数据传输

首先新建一个空的文件夹,通过npm安装nodejs-websocket

npm install nodejs-websocket

新建app.js文件:

var ws = require("nodejs-websocket");
ws.createServer(function(conn){
    conn.on("text",function (str) {
        console.log("get the message: "+str);
        conn.sendText("the server got the message");
    })
    conn.on("close",function (code,reason) {
        console.log("connection closed");
    });
    conn.on("error",68)">"an error !");
    });
}).listen(8001);

通过node app.js启动,这样服务器就搭建好了。

Cocos2d-x

  • 首先在头文件中include头文件:
#include "network/WebSocket.h"
  • 实现WebSocket的委托:
class HelloWorld : public cocos2d::Layer,public cocos2d::network::WebSocket::Delegate
  • 四个委托中定义的函数接口以及一个用来连接的socketClient对象:
// for virtual function in websocket delegate
virtual void onopen(cocos2d::network::WebSocket* ws);
virtual void onMessage(cocos2d::network::WebSocket* ws,const cocos2d::network::WebSocket::Data& data);
virtual void onClose(cocos2d::network::WebSocket* ws);
virtual void onError(cocos2d::network::WebSocket* ws,const cocos2d::network::WebSocket::ErrorCode& error);

// the websocket io client
cocos2d::network::WebSocket* _wsiClient;
  • 初始化client:
_wsiClient = new cocos2d::network::WebSocket();
_wsiClient->init(*this,"ws://localhost:8001");
  • 在cpp文件中实现这些函数:
// 开始socket连接
void HelloWorld::onopen(cocos2d::network::WebSocket* ws)
{
    cclOG("Onopen");
}

// 接收到了消息
void HelloWorld::onMessage(cocos2d::network::WebSocket* ws,const cocos2d::network::WebSocket::Data& data)
{
        std::string textStr = data.bytes;
        textStr.c_str();
        cclOG(textStr.c_str());
}

// 连接关闭
void HelloWorld::onClose(cocos2d::network::WebSocket* ws)
{
    if (ws == _wsiClient)
    {
        _wsiClient = NULL;
    }
    CC_SAFE_DELETE(ws);
    cclOG("onClose");
}

// 遇到错误
void HelloWorld::onError(cocos2d::network::WebSocket* ws,const cocos2d::network::WebSocket::ErrorCode& error)
{
    if (ws == _wsiClient)
    {
        char buf[100] = {0};
        sprintf(buf,68)">"an error was fired,code: %d",error);
    }
    cclOG("Error was fired,error code: %d",error);
}

还有一个使用SocketIO的方案,尚未尝试,明天测试一下:

// Require HTTP module (to start server) and Socket.IO
var http = require('http'),io = require('socket.io');

// Start the server at port 8080
var server = http.createServer(function(req,res){ 

    // Send HTML headers and message
    res.writeHead(200,{ 'Content-Type': 'text/html' }); 
    res.end('<h1>Hello Socket lover!</h1>');
});
server.listen(8080);

// Create a Socket.IO instance,passing it our server
var socket = io.listen(server);

// Add a connect listener
socket.on('connection',function(client){ 

    // Create periodical which ends a message to the client every 5 seconds
    var interval = setInterval(function() {
        client.send('This is a message from the server! ' + new Date().getTime());
    },5000);

    // Success! Now listen to messages to be received
    client.on('message',function(event){ 
        console.log('Received message from client!',event);
    });
    client.on('disconnect',function(){
        clearInterval(interval);
        console.log('Server has disconnected');
    });

});

cocos2d--WebSocket分析

cocos2d--WebSocket分析

WebSocket初始化之后,就可以send了,创建一个新的线程并且循环udpate,线程函数循环onSubThreadLoop,update发送消息给Delegate

线程函数循环onSubThreadLoop 判断是否要destory或者触发拿发送的数据

int WebSocket::onSubThreadLoop()
{
    if (_readyState == State::CLOSED || _readyState == State::CLOSING)
    {
        libwebsocket_context_destroy(_wsContext);
        // return 1 to exit the loop.
        return 1;
    }

    if (_wsContext && _readyState != State::CLOSED && _readyState != State::CLOSING)
    {
        libwebsocket_service(_wsContext,0);//触发LWS_CALLBACK_CLIENT_WRITEABLE,去拿发送到server的数据
    }

    // Sleep 50 ms
    std::this_thread::sleep_for(std::chrono::milliseconds(50));

    // return 0 to continue the loop.
    return 0;
}

//WebSocket收到消息
int WebSocket::onSocketCallback(struct libwebsocket_context *ctx,struct libwebsocket *wsi,int reason,void *user,void *in,ssize_t len)
{
    //cclOG("socket callback for %d reason",reason);
    CCASSERT(_wsContext == nullptr || ctx == _wsContext,"Invalid context.");
    CCASSERT(_wsInstance == nullptr || wsi == nullptr || wsi == _wsInstance,"Invaild websocket instance.");

    switch (reason) 
    {
        case LWS_CALLBACK_DEL_POLL_FD:
        case LWS_CALLBACK_PROTOCOL_DESTROY:
        case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
            {
                WsMessage* msg = nullptr;
                if (reason == LWS_CALLBACK_CLIENT_CONNECTION_ERROR
                    || (reason == LWS_CALLBACK_PROTOCOL_DESTROY && _readyState == State::CONNECTING)
                    || (reason == LWS_CALLBACK_DEL_POLL_FD && _readyState == State::CONNECTING)
                    )
                {
                    msg = new (std::nothrow) WsMessage();
                    msg->what = WS_MSG_TO_UITHREAD_ERROR;
                    _readyState = State::CLOSING;  //先设置为CLOSING,下一次循环的时候,会destory,才会变成CLOSE
                }
                else if (reason == LWS_CALLBACK_PROTOCOL_DESTROY && _readyState == State::CLOSING)
                {
                    msg = new (std::nothrow) WsMessage();
                    msg->what = WS_MSG_TO_UITHREAD_CLOSE;
                }

                if (msg)
                {
                    _wsHelper->sendMessagetoUIThread(msg);
                }
            }
            break;
        case LWS_CALLBACK_CLIENT_ESTABLISHED:
            {
                WsMessage* msg = new (std::nothrow) WsMessage();
                msg->what = WS_MSG_TO_UITHREAD_OPEN;
                _readyState = State::OPEN;

                /* * start the ball rolling,* LWS_CALLBACK_CLIENT_WRITEABLE will come next service */
                //每次libwebsocket_service(loop调用)之后,会触发LWS_CALLBACK_CLIENT_WRITEABLE
                libwebsocket_callback_on_writable(ctx,wsi);  
                _wsHelper->sendMessagetoUIThread(msg);
            }
            break;


        case LWS_CALLBACK_CLIENT_WRITEABLE:
            {

                std::lock_guard<std::mutex> lk(_wsHelper->_subThreadWsMessageQueueMutex);

                std::list<WsMessage*>::iterator iter = _wsHelper->_subThreadWsMessageQueue->begin();

                int bytesWrite = 0;
                for (; iter != _wsHelper->_subThreadWsMessageQueue->end();)
                {
                    WsMessage* subThreadMsg = *iter;

                    if ( WS_MSG_TO_SUBTRHEAD_SENDING_STRING == subThreadMsg->what
                      || WS_MSG_TO_SUBTRHEAD_SENDING_BINARY == subThreadMsg->what)
                    {
                        Data* data = (Data*)subThreadMsg->obj;

                        const size_t c_bufferSize = WS_WRITE_BUFFER_SIZE;

                        size_t remaining = data->len - data->issued; // 有可能一次发不完,分多次
                        size_t n = std::min(remaining,c_bufferSize );
                        //fixme: the log is not thread safe
// cclOG("[websocket:send] total: %d,sent: %d,remaining: %d,buffer size: %d",static_cast<int>(data->len),static_cast<int>(data->issued),static_cast<int>(remaining),static_cast<int>(n));

                        //数据前后加PADDING
                        unsigned char* buf = new unsigned char[LWS_SEND_BUFFER_PRE_PADDING + n + LWS_SEND_BUFFER_POST_PADDING];

                        memcpy((char*)&buf[LWS_SEND_BUFFER_PRE_PADDING],data->bytes + data->issued,n);

                        int writeProtocol;

                        if (data->issued == 0) {  //第一次发送指定writeProtocol为LWS_WRITE_TEXT或者LWS_WRITE_BINARY
                            if (WS_MSG_TO_SUBTRHEAD_SENDING_STRING == subThreadMsg->what)
                            {
                                writeProtocol = LWS_WRITE_TEXT;
                            }
                            else
                            {
                                writeProtocol = LWS_WRITE_BINARY;
                            }

                            // If we have more than 1 fragment
                            if (data->len > c_bufferSize)
                                writeProtocol |= LWS_WRITE_NO_FIN;  //说明这还不是最后部分数据
                        } else {
                            // we are in the middle of fragments
                            writeProtocol = LWS_WRITE_CONTINUATION;  //不是第一个发送的部分。
                            // and if not in the last fragment
                            if (remaining != n)
                                writeProtocol |= LWS_WRITE_NO_FIN;  //说明这还不是最后部分数据
                        }
                        //发送数据,设置writeProtocol类型,具体解析交给WebSocket去做吧。
                        bytesWrite = libwebsocket_write(wsi,&buf[LWS_SEND_BUFFER_PRE_PADDING],n,(libwebsocket_write_protocol)writeProtocol);
                        //fixme: the log is not thread safe
// cclOG("[websocket:send] bytesWrite => %d",bytesWrite);

                        // Buffer overrun?
                        if (bytesWrite < 0)
                        {
                            break;
                        }
                        // Do we have another fragments to send?
                        else if (remaining != n) //没有全部发送,还有一些,记录以及发送的数据issued,下次跳过issued这么多数据
                        {
                            data->issued += n;
                            break;
                        }
                        // Safely done!
                        else  //说明本次的data全部发送完成,移除之
                        {
                            CC_SAFE_DELETE_ARRAY(data->bytes);
                            CC_SAFE_DELETE(data);
                            CC_SAFE_DELETE_ARRAY(buf);
                            _wsHelper->_subThreadWsMessageQueue->erase(iter++);
                            CC_SAFE_DELETE(subThreadMsg);
                        }
                    }
                }

                /* get notified as soon as we can write again */

                libwebsocket_callback_on_writable(ctx,wsi);
            }
            break;

        case LWS_CALLBACK_CLOSED:
            {
                //fixme: the log is not thread safe
// cclOG("%s","connection closing..");

                _wsHelper->quitSubThread();

                if (_readyState != State::CLOSED)
                {
                    WsMessage* msg = new (std::nothrow) WsMessage();
                    _readyState = State::CLOSED;
                    msg->what = WS_MSG_TO_UITHREAD_CLOSE;
                    _wsHelper->sendMessagetoUIThread(msg);
                }
            }
            break;

        case LWS_CALLBACK_CLIENT_RECEIVE:  //有新的数据来了
            {
                if (in && len > 0)
                {
                    // Accumulate the data (increasing the buffer as we go)
                    if (_currentDataLen == 0)
                    {
                        _currentData = new char[len];
                        memcpy (_currentData,in,len);
                        _currentDataLen = len;
                    }
                    else
                    {//分配更多的内存,并且保存之前的数据
                        char *new_data = new char [_currentDataLen + len];
                        memcpy (new_data,_currentData,_currentDataLen);
                        memcpy (new_data + _currentDataLen,len);
                        CC_SAFE_DELETE_ARRAY(_currentData);
                        _currentData = new_data;
                        _currentDataLen = _currentDataLen + len;
                    }

                    _pendingFrameDataLen = libwebsockets_remaining_packet_payload (wsi);//说明还有滞留的数据,下次才能收到

                    if (_pendingFrameDataLen > 0)
                    {
                        //cclOG("%ld bytes of pending data to receive,consider increasing the libwebsocket rx_buffer_size value.",_pendingFrameDataLen);
                    }

                    // If no more data pending,send it to the client thread
                    if (_pendingFrameDataLen == 0)//为0,说明没有更多的数据
                    {
                        WsMessage* msg = new (std::nothrow) WsMessage();
                        msg->what = WS_MSG_TO_UITHREAD_MESSAGE;

                        char* bytes = nullptr;
                        Data* data = new (std::nothrow) Data();

                        if (lws_frame_is_binary(wsi))
                        {

                            bytes = new char[_currentDataLen];
                            data->isBinary = true;
                        }
                        else
                        {
                            bytes = new char[_currentDataLen+1];
                            bytes[_currentDataLen] = '\0';
                            data->isBinary = false;
                        }

                        memcpy(bytes,_currentDataLen);

                        data->bytes = bytes;
                        data->len = _currentDataLen;
                        msg->obj = (void*)data;

                        CC_SAFE_DELETE_ARRAY(_currentData);
                        _currentData = nullptr;
                        _currentDataLen = 0;

                        _wsHelper->sendMessagetoUIThread(msg);
                    }
                }
            }
            break;
        default:
            break;

    }

    return 0;
}

}

cocos2d-iphone – 在Cocos2d中替换场景时不会触发dealloc

cocos2d-iphone – 在Cocos2d中替换场景时不会触发dealloc

出于某种原因,在更换场景时不会触发我的cclayer的dealloc.这是替换场景的代码:

[[CCDirector sharedDirector] replaceScene:[CCFadeTransition transitionWithDuration:2.0f scene:[HelloWorld scene]]];

按下按钮时会触发上面的代码.

我已经在dealloc方法中放置了一个永远不会触发的NSLog.

更新1:

我最后通过在更换场景之前手动释放内存来解决问题.

解决方法

当我第一次开始使用cocos2d时,我遇到了同样的问题.
在我的情况下,我被添加为目标委托自我,这意味着增加了对自我的引用计数.

[[CCTouchdispatcher shareddispatcher] addTargetedDelegate:self priority:2] swallowstouches:NO];

我通过删除所有委托来解决这个问题(你也可以指定特定的委托):

[[CCTouchdispatcher shareddispatcher] removeAllDelegates];

cocos2d-iphone – 如何在cocos2d中显示CCNode的边框?

cocos2d-iphone – 如何在cocos2d中显示CCNode的边框?

作为我之前关于 displaying the anchor point的问题的后续问题,我将CCSprite子类化并更改了其绘制方法如下:
[super draw];
ccDrawColor4F(0,1,1);
ccDrawCircle(self.anchorPointInPoints,20,8,YES);

这很好.为了额外的信用,我添加了以下内容来显示它的边框:

CGRect bb = self.boundingBox;
CGPoint vertices[4] = {
    [self convertToNodeSpace:ccp(bb.origin.x,bb.origin.y)],[self convertToNodeSpace:ccp(bb.origin.x + bb.size.width,bb.origin.y + bb.size.height)],[self convertToNodeSpace:ccp(bb.origin.x,};
ccDrawpoly(vertices,4,YES);

这也很好,直到我重申一个精灵:

CGPoint oldPosition = [sprite convertToWorldspace:sprite.position];
[sprite removeFromParentAndCleanup:NO];
[parentSprite addChild:sprite];
sprite.position = [sprite convertToNodeSpace:oldPosition];

小精灵现在处于正确的位置,其定位点应该在哪里,但是边框将在错误的地方.我究竟做错了什么?

节点的边界框相对于其父节点.绘制方法绘制在节点的本地空间中. convertToNodeSpace:将坐标从世界空间转换为本地空间,而不是从父级空间转换.

当您将节点重新分配给具有不同来源的父节点,同时保持该节点的相同“世界”位置时,其边界框的起始点将更改.

你的错误是,你对待你的小精灵的边框,好像它的坐标在世界空间.

第二,你不需要做转换x空间的舞蹈来画一个精灵的边框.在项目的cocos2d文件夹中打开ccConfig.h文件并进行更改

#define CC_SPRITE_DEBUG_DRAW 0

行到

#define CC_SPRITE_DEBUG_DRAW 1

第三,sprite.position点的坐标是相对于它的父,而不是精灵.当您调用[node convertToWorldspace:aPoint]时,它会将aPoint视为节点的本地空间.如果要获取节点位置的世界坐标,则应在节点的父节点上调用convertToWorldspace:[node.parent convertToWorldspace:node.position].

cocos2d-iphone – 如何在cocos2d中测试精灵碰撞?

cocos2d-iphone – 如何在cocos2d中测试精灵碰撞?

如何开始实现sprite碰撞的类?

解决方法

正如Eric指出的那样,CGRectIntersectsRect是测试两个重叠区域的方法.使用CCNode类的boundingBox方法为每个sprite(或其他节点)获取正确的边界框.

在这里看到我的答案:
Collision Detection in Cocos2d game?

今天的关于[Cocos2d-x]在Cocos2d-x 3.x版本中如何通过WebSocket连接服务器进行数据传输的分享已经结束,谢谢您的关注,如果想了解更多关于cocos2d--WebSocket分析、cocos2d-iphone – 在Cocos2d中替换场景时不会触发dealloc、cocos2d-iphone – 如何在cocos2d中显示CCNode的边框?、cocos2d-iphone – 如何在cocos2d中测试精灵碰撞?的相关知识,请在本站进行查询。

本文标签: