首页 > 代码库 > cocos2d-x3.2 Socket传输Json字符串

cocos2d-x3.2 Socket传输Json字符串

这里使用客户端用的是C++的简单封装,参考http://blog.csdn.net/langresser_king/article/details/8646088这篇文章。

原文地址:http://blog.csdn.net/qqmcy/article/details/39155541

代码下载:http://download.csdn.net/detail/qqmcy/7884273

服务器端用的JAVA编写。测试服务器代码:http://download.csdn.net/detail/qqmcy/7884273 这个服务器代码只适合测试使用。

使用方法:

HelloWorldScene.h

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"
#include "Tools/SocketModel/DJAsyncSocket.h"


class HelloWorld : public cocos2d::Layer ,public DJAsyncSocketDelegate
{
public:
    // there's no 'id' in cpp, so we recommend returning the class instance pointer
    static cocos2d::Scene* createScene();

    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init();  
    
    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);
    
    void menuCloseCallback(Ref* sender);
    

private:
    DJAsyncSocket* socket_asy;
    
    virtual void update(float delta);
    
    virtual void onSocketDidReadData(DJAsyncSocket* sock ,char* data);
    
    
};

#endif // __HELLOWORLD_SCENE_H__

HelloWorldScene.cpp

#include "HelloWorldScene.h"
#include "Tools/JsonData/MakeJson.h"


USING_NS_CC;

#define IP "127.0.0.1";

Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();
    
    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }

     
    
    socket_asy = new DJAsyncSocket();
    socket_asy->setDelegate(this);
    socket_asy->create("127.0.0.1", 8000);
    

    
    
    auto visibleSize = Director::getInstance()->getVisibleSize();
    auto origin = Director::getInstance()->getVisibleOrigin();

    
    auto closeItem = MenuItemImage::create(
                                           "choose_btn_light.png",
                                           "choose_btn_light.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));
    
    closeItem->setPosition(origin + Vec2(visibleSize) - Vec2(closeItem->getContentSize() / 2));
    
    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);
    this->scheduleUpdate();
    
    return true;
}


void HelloWorld::menuCloseCallback(Ref* sender)
{
    
    MakeJson* mjson = MakeJson::create();
    
    

    std::string content = mjson->getTestJson();
    content.append("\n");
    
    
    
//    const char* str_cnt = content.c_str();
    int len = (int)content.length();
    
    //"{ \"hello\" : \"world\" }";
//    char* str  = nullptr;//"{\"reciver\":\"555\",\"sender\":\"f\"}\n";
    char* str = (char*)malloc((len)* sizeof(char));
    content.copy(str, len,0);
    
//    log("content = %s,length = %lu ,Len = %d",content.c_str(),strlen(str),len);
 
    socket_asy->sendMsg(str, len);
    socket_asy->flush();
    
//    free(str);
//    content = nullptr;
    
    
    
    
    
}


void HelloWorld::update(float delta)
{
    
    // 接收消息处理(放到游戏主循环中,每帧处理)
    if (!socket_asy) {
        return;
    }
    
    // 接收数据(取得缓冲区中的所有消息,直到缓冲区为空)
    while (true)
    {
        char buffer[_MAX_MSGSIZE] = { 0 };
        int nSize = sizeof(buffer);
        char* pbufMsg = buffer;
        if(socket_asy == NULL)
        {
            break;
        }
        if (!socket_asy->receiveMsg(pbufMsg, nSize)) {
            break;
        }
        
    }
    
    
}

void HelloWorld::onSocketDidReadData(DJAsyncSocket *sock, char *data)
{
    log("data = http://www.mamicode.com/%s",data);>
DJAsyncSocket.h

//
//  DJAsyncSocket.h
//  cpp4
//
//  Created by 杜甲 on 14-9-5.
//
//

#ifndef __cpp4__DJAsyncSocket__
#define __cpp4__DJAsyncSocket__

#if WIN32
#include <windows.h>
#include <WinSock.h>
#else
#include <sys/socket.h>
#include <fcntl.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>



#define SOCKET int
#define SOCKET_ERROR  -1
#define INVALID_SOCKET -1


#endif
#ifndef CHECHF
#define CHECKF(x) do { if(!(x)) {log("CHECK",#x,__FILE__,__LINE__);return 0; }} while(0)

#endif

#define  _MAX_MSGSIZE   8 * 1024       //暂定一个消息最大为16K
#define BLOCKSECONDS        30          //INIT函数阻塞时间
#define INBUFSIZE       (64 * 1024)     //  接收数据缓存
#define OUTBUFSIZE      (8  * 1024)     //  发送数据的缓存,当不超过8K时,FLUSH只需要SEND一次

#include "cocos2d.h"
USING_NS_CC;

class DJAsyncSocket;


class DJAsyncSocketDelegate
{
public:
 
    virtual void onSocketDidReadData(DJAsyncSocket* sock ,char* data) = 0;

};



class DJAsyncSocket :public Ref
{
public:
    DJAsyncSocket();
    
    bool create(const char* pszServerIP , int nServerPort , int nBlockSec = BLOCKSECONDS , bool bKeepAlice = false);
    bool sendMsg(void* pBuf , int nSize);
    bool receiveMsg(void* pBuf , int& nSize);
    bool flush(void);
    bool check(void);
    void destory(void);
    
    SOCKET getSocket(void) const ;
private:
    CC_SYNTHESIZE(DJAsyncSocketDelegate*, _delegate, Delegate);
    
    bool recvFromSock(void);    //从网络中读取尽可能多的数据
    bool hasError(void);    //是否发生错误,注意,异步模式未完成非错误
    void closeSocket(void);
    
    SOCKET  m_sockClient;
    
    //发送数据缓冲
    char    m_bufOutput[OUTBUFSIZE];
    int     m_nOutbufLen;
    
    
    //环形缓冲区
    char    m_bufInput[INBUFSIZE];
    int     m_nInbufLen;
    int     m_nInbufStart;          //INBUF使用循环式队列,该变量为队列起点,0 - (SIZE  - 1)
};















#endif /* defined(__cpp4__DJAsyncSocket__) */


DJAsyncSocket.cpp

//
//  DJAsyncSocket.cpp
//  cpp4
//
//  Created by 杜甲 on 14-9-5.
//
//

#include "DJAsyncSocket.h"

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


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



bool DJAsyncSocket::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) {
                    closeSocket();
                    return false;
                }
            }
            
        }
    }
    m_nInbufLen     = 0;
    m_nInbufStart   = 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 DJAsyncSocket::hasError()
{
#ifdef WIN32
	int err = WSAGetLastError();
	if(err != WSAEWOULDBLOCK) {
        return true;
    }
    
    
#else
    int err = errno;
    if(err != EINPROGRESS && err != EAGAIN) {
        return true;
    }
    
#endif
    
    return false;
}



bool DJAsyncSocket::sendMsg(void *pBuf, int nSize)
{
    if (pBuf == 0 || nSize <= 0) {
        return false;
    }
    
    if (m_sockClient == INVALID_SOCKET) {
        return false;
    }
    
    //检查通讯消息包长度
    int packsize = 0;
    packsize = nSize;
    
    //检测BUF溢出
    if (m_nOutbufLen + nSize > OUTBUFSIZE) {
        //立即发送OUTBUF中的数据,以清空OUTBUF
        flush();
        if (m_nOutbufLen + nSize > OUTBUFSIZE) {
            destory();
            return false;
        }
    }
    
    memcpy(m_bufOutput + m_nOutbufLen, pBuf, nSize);
    m_nOutbufLen += nSize;
    
    return true;
}

bool DJAsyncSocket::flush()
{
    if (m_sockClient == INVALID_SOCKET) {
        return false;
    }
    
    if (m_nOutbufLen <= 0) {
        return true;
    }
    
    //发送一段数据
    int outsize;
    
    outsize = send(m_sockClient, m_bufOutput, m_nOutbufLen, 0);
    if (outsize > 0){
        //删除已发送的部分
        if (m_nOutbufLen - outsize > 0) {
            memcpy(m_bufOutput, m_bufOutput + outsize, m_nOutbufLen - outsize);
        }
        m_nOutbufLen -= outsize;
        if (m_nOutbufLen < 0) {
            return false;
        }
        
        
    }else{
        if (hasError()) {
            destory();
            return false;
        }
    }
    return true;
}


bool DJAsyncSocket::check()
{
    //检查状态
    if (m_sockClient == INVALID_SOCKET) {
        return false;
    }
    
    
    
    char buf[1];
    int ret = recv(m_sockClient, buf, 1, MSG_PEEK);
    if (ret == 0) {
        destory();
        return false;
    }else if (ret < 0){
        if (hasError()) {
            destory();
            return false;
        }else{
            return true;
        }
    }else{
        return true;
    }
    
    return true;
}


bool DJAsyncSocket::receiveMsg(void *pBuf, int &nSize)
{
    //检查参数
    if (pBuf == NULL || nSize <= 0) {
        return false;
    }
    
    if (m_sockClient == INVALID_SOCKET) {
        return false;
    }
//     log("m_bufInput = %s,m_nInbufLen = %d",m_bufInput,m_nInbufLen);
    //检查是否有一个消息(小于2则无法获取到消息长度)
    if (m_nInbufLen < 2) {
        //  如果没有请求成功    或者  如果没有数据则直接返回
        if (!recvFromSock() || m_nInbufLen < 2) {   //这个m_nInbufLen更新了
            
            return false;
        }
    }
    
    if (_delegate) {
        _delegate->onSocketDidReadData(this, m_bufInput);
    }
    
       //计算要拷贝的消息的大小(一个消息,大小为整个消息的第一个16字节),因为环形缓冲区,所以要分开结算
    int packsize = (unsigned char) m_bufInput[m_nInbufStart] + (unsigned char)m_bufInput[(m_nInbufStart + 1) % INBUFSIZE] * 256;    //注意字节序,高位 + 低位

    //检测消息包尺寸错误 暂定最大16k
    if (packsize <= 0 || packsize > _MAX_MSGSIZE) {
        m_nInbufLen     = 0;    //直接清空INBUF
        m_nInbufStart   = 0;
        return false;
    }
    
    //检查消息是否完整(如果将要拷贝的消息大于此时缓冲区数据长度,需要再次请求接收剩余数据)
    
    if (packsize > m_nInbufLen) {
        //如果没有请求成功      或者  依然无法获取到完整的数据包   则返回,直到取得wanzbao
        if (!recvFromSock() || packsize > m_nInbufLen) {    //这个m_nInbufLen已更新
            return false;
        }
    }
    
    
    

    
    //  复制出一个消息
    if (m_nInbufStart + packsize > INBUFSIZE) {
        //  如果一个消息有回卷(被拆成两份在环形缓冲区的头尾)
        //  先拷贝环形缓冲区末尾的数据
        int copylen = INBUFSIZE - m_nInbufStart;
        memcpy(pBuf, m_bufInput + m_nInbufStart, copylen);
        
        //再考贝环形缓冲区头部的剩余部分
        memcpy((unsigned char*) pBuf + copylen, m_bufInput, packsize - copylen);
        nSize = packsize;
    }else
    {
        //  消息没有回卷,可以一次拷贝出去
        memcpy(pBuf, m_bufInput + m_nInbufStart, packsize);
        nSize = packsize;
    }
//    log("m_bufInput = %s",m_bufInput);
    m_nInbufStart = (m_nInbufStart + packsize) % INBUFSIZE;
    m_nOutbufLen -= packsize;
    return true;
}

bool DJAsyncSocket::recvFromSock()
{
    if (m_nInbufLen >= INBUFSIZE || m_sockClient == INVALID_SOCKET) {
        return false;
    }
    
    //接收第一段数据
    int savelen , savepos;          //数据要保存的长度和位置
    if (m_nInbufStart + m_nInbufLen < INBUFSIZE) {  //INBUF中的剩余空间有回绕
        savelen = INBUFSIZE - (m_nInbufStart + m_nInbufLen);    //后部空间长度,最大接收数据的长度
    }else{
        savelen = INBUFSIZE - m_nInbufLen;
    }
    
    //  缓冲区数据的末尾
    savepos = (m_nInbufStart + m_nInbufLen) % INBUFSIZE;
//    CHECKF(savepos + savelen < INBUFSIZE);
    int inlen = recv(m_sockClient, m_bufInput + savepos, savelen, 0);
    
    
    
    if (inlen > 0) {
        //有接收到数据
        m_nInbufLen += inlen;
        
        if (m_nInbufLen > INBUFSIZE) {
            return false;
        }
        
        //接收第二段数据(一次接收没有完成,接收第二段数据)
        if (inlen == savelen && m_nInbufLen < INBUFSIZE) {
            int savelen = INBUFSIZE - m_nInbufLen;
            int savepos = (m_nInbufStart + m_nInbufLen) % INBUFSIZE;
//            CHECKF(savepos + savelen <= INBUFSIZE);
            inlen = recv(m_sockClient, m_bufInput + savepos, savelen, 0);
            if (inlen > 0) {
                m_nInbufLen += inlen;
                if (m_nInbufLen > INBUFSIZE) {
                    return false;
                }
            }else if (inlen == 0){
                destory();
                return false;
            }else{
                if (hasError()) {
                    destory();
                    return false;
                }
            }

        }
        
    }
    
    return true;
}


void DJAsyncSocket::destory()
{
    //  关闭
    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_nInbufStart = 0;
    m_nOutbufLen  = 0;
    
    memset(m_bufOutput, 0, sizeof(m_bufOutput));
    memset(m_bufInput,  0, sizeof(m_bufInput));
}

下面是制作JSON数据的类

MakeJson.h

//
//  MakeJson.h
//  cpp4
//
//  Created by 杜甲 on 14-9-9.
//
//

#ifndef __cpp4__MakeJson__
#define __cpp4__MakeJson__

#include "cocos2d.h"
#include "json/document.h"
#include "json/writer.h"
#include "json/stringbuffer.h"

USING_NS_CC;

class MakeJson : public Ref
{
public:
    CREATE_FUNC(MakeJson);
    
    virtual bool init();
    std::string getTestJson();
    
 
    
};
#endif /* defined(__cpp4__MakeJson__) */

MakeJson.cpp

//
//  MakeJson.cpp
//  cpp4
//
//  Created by 杜甲 on 14-9-9.
//
//

#include "MakeJson.h"

bool MakeJson::init()
{
    bool bRef = false;
    do {
        bRef = true;
    } while (0);
    return bRef;
}

std::string MakeJson::getTestJson()
{
   
    
    rapidjson::Document d1;
    d1.SetObject();
    rapidjson::Document::AllocatorType& allocator = d1.GetAllocator();
    rapidjson::Value array(rapidjson::kArrayType);
    rapidjson::Value object(rapidjson::kObjectType);
    
    
//    object.AddMember("id", 1, allocator);
//    object.AddMember("name", "xiaonan", allocator);
//    object.AddMember("age", "111", allocator);
//    array.PushBack(object, allocator);
    d1.AddMember("reciver", "111", allocator);
    d1.AddMember("sender", "111", allocator);
    d1.AddMember("content", "神奇dfsa", allocator);
    
//    d1.AddMember("player", array, allocator);
    
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> write(buffer);
    d1.Accept(write);

    return StringUtils::format("%s",buffer.GetString());
    
    
}






cocos2d-x3.2 Socket传输Json字符串