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::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(data->len), static_cast(data->issued), static_cast(remaining), static_cast(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, in, 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, _currentData, _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-x)