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 = %s",data);

    auto path = FileUtils::getInstance()->getWritablePath();
    log("%s",path.c_str());

    //在这个路径下添加一个json文件
    path.append("test.json");

    FILE* file = fopen(path.c_str(), "wb");
    if (file) {
        fputs(data, file);
        fclose(file);
    }

}

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());

}
时间: 2024-11-18 15:16:39

cocos2d-x3.2 Socket传输Json字符串的相关文章

Socket传输JSON数据

//请求登录的JSON数据 { "request":"login", "data": { "username":"zhangsan", "password":"123", "client":"iphone", "other":"" } } //请求查询时候的JSON数据 1.通过姓名查询

RestTemplate传输值为null的属性、利用FastJson将属性中有空值null的对象转化成Json字符串

一个pojo类: import lombok.Data; @Data public class Friend { private String name; private int age; private String sex; } 初始化一个Friend对象,该对象属性为"sex"对应的值设置为null: public class FriendTest { private Friend friend = new Friend(); @Before public void init()

C++ socket 传输不同类型数据的四种方式

使用socket传输组织好的不同类型数据,有四种不同的方式(我知道的嘿嘿): a. 结构体 b. Json序列化 c. 类对象 d. protobuf 下面逐一整理一下,方便以后进行项目开发. 1. 使用结构体 假设需要传送的结构体如下: struct person { char name[20]; // 注意:CPU访问内存的时候,对访问地址有对齐的要求,一般为2的幂次方.所以,有的数据被填充,以满足对齐要求. int age; float high; }; 可在发送数据的地方对数据进行处理,

js中把JSON字符串转换成JSON对象最好的方法

在JS中将JSON的字符串解析成JSON数据格式,一般有两种方式: 1.一种为使用eval()函数. 2. 使用Function对象来进行返回解析. 第一种解析方式:使用eval函数来解析,并且使用jQuery的each方法来遍历 用jQuery解析JSON数据的方法,作为jQuery异步请求的传输对象,jQuery请求后返回的结果是json对象,这里考虑的都是服务器返回JSON形式的字符串的形式,对于利用JSONObject等插件封装的JSON对象,与此亦是大同小异,这里不再做说明. 这里首先

[C#技术参考]Socket传输结构数据

最近在做一个机器人项目,要实时的接收机器人传回的坐标信息,并在客户端显示当前的地图和机器人的位置.当然坐标的回传是用的Socket,用的是C++的结构体表示的坐标信息.但是C#不能像C++那样很easy的把字节数组byte[]直接的转换成结构,来发送和接收.在C#中要多做一些工作.但是在C或者C++中这是一件很容易的事,只需要一个函数: void *memcpy(void *dest, const void *src, size_t n);//从源src所指的内存地址的起始位置开始拷贝n个字节到

springMVC和Json的交互(就是后台解析json字符串和向前台传递json字符串的过程)

springMVC中和json的交互:主要分为两点,一是传递过来的是json字符串,springMVC会先进行解析,二是传递过来的是key:value那么springMVC可以不进行解析,这里的解析指的是json和java对象之间的转换 java对象:就是实体类的对象 首先先来看一张交互的图: 分析: 1.从这个图中我们可以看出来,前台请求的数据如果是json字符串,我们需要用@RequestBody先将json字符串转成java对象,然后前台传输的是java对象的时候就需要用@Response

JavaScript对象与JSON字符串互相转换详解

JSON是目前互联网数据传输所采用的的主要格式,很多编程语言都有针对JSON的解析器和序列化器,在web前台领域,js对象与JSON字符串的互相转换需求也较为常见. 此文主要讲解js对象和JSON字符串互相转换的方法和一些细节 首先如果需要在开发中使用JSON传输及相应转换需要在html中引入json.js或者json2.js,下载地址:https://github.com/douglascrockford/JSON-js 1.js对象转JSON字符串(序列化) var jsonText = J

【荐】使用eval()、new Function()将JSON字符串转换为JSON对象

在JS中将JSON的字符串解析成JSON数据格式,一般有两种方式: 1.一种为使用eval()函数. 2. 使用Function对象来进行返回解析. 第一种解析方式:使用eval函数来解析,并且使用jQuery的each方法来遍历 用jQuery解析JSON数据的方法,作为jQuery异步请求的传输对象,jQuery请求后返回的结果是json对象,这里考虑的都是服务器返回JSON形式的字符串的形式,对于利用JSONObject等插件封装的JSON对象,与此亦是大同小异,这里不再做说明. 这里首先

【Android进阶】Gson解析json字符串的简单应用

在客户端与服务器之间进行数据传输,一般采用两种数据格式,一种是xml,一种是json.这两种数据交换形式各有千秋,比如使用json数据格式,数据量会比较小,传输速度快,放便解析,而采用xml数据格式,如果使用SAX解析方式,则可以一边加载一边解析,对于数据量比较大的应用,比较适合.今天主要整理一下使用Gson解析json格式字符串的简单使用. 首先,必须导入外部包 /** * * @author ZhaoKaiQiang * * @time 2014年5月4日 */ public class J