cocos网络编程C++版本(视频笔记)

一、概括介绍

1、概括介绍:cocos使用三种协议实现网络通信(http、socket、websocket)

(1)socket 套接字

Socket,俗称网络套接字,本身并不是协议,而是一个调用接口,是对TCP/IP协议的封装和应用,。提供了一系列方法方便开发者进行网络通讯。

TCP/IP协议是使用最早的通讯协议,它是传输层协议,主要解决数据如何在网络中传输。
Socket中又分为流模式与数据报模式,即TCP与UDP两种方式。
TCP : Transmission Control Protocol,传输控制协议,是面向连接的协议,也就是说,在收发数据前,必须和对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来,其中的过程非常复杂,但也是最安全的。
UDP : User Data Protocol,用户数据报协议。传输数据之前源端和终端不建立连接,发送端直接把数据发送到网络,接收端把消息段放在队列中,应用程序每次从队列中读一个消息段。

(2)Http 协议

HTTP协议也叫超文本传输协议。是互联网广泛使用的通信协议,常用于B/S架构中。
HTTP连接使用的是短连接形式,也就是“请求-响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。
HTTP常用的有2种数据提交方式,分别是GET、POST
1> get是从服务器上获取(查询)数据,post是向服务器传送数据;
2> get传送的数据量较小,不能大于2KB,post传送的数据量较大,一般认为不受限;
3> get安全性非常低;post安全性相对来说就比较好了;

(3)websocket 协议

WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信。实现浏览器与服务器的即时通讯。

二、http协议(2-5)

使用Http协议的步骤有6个:

(1)创建HttpClient实例(首先要创建request对象,并设置完成后,在创建client对象,把request对象添加进去);

(2)设置某种链接方法的类型(GET、POST),通过setUrl传入待连接的地址;(request对象中设置)

(3)设置响应回调函数,读取response;(是request对象中设置的回调函数调用,回调函数的两个参数包括response和client对象)

(4)添加请求到HttpClient任务队列;

(5)释放连接;(在client对象发送request请求之后要释放连接,因为http是请求-响应的运作方式)

(6)对得到的内容进行处理。(在回调函数中处理)


2、使用cocos2d封装的HttpRequest设置请求消息

HttpRequest:
是一种数据类型,它提供了一些方法用来定义或获取HTTP请求的参数。常用方法包括下面几种。
设置请求连接,url是网址:
void setUrl(const char * url);
设置请求类型:
void setRequestType(Type type)
Type是Cocos2d-x定义的一个枚举类型,包括5种类型。
enum class Type
    {
        GET,
        POST,
        PUT,
        DELETE,
        UNKNOWN,
};
设置回调函数:
void setResponseCallback(Ref * pTarget,SEL_HttpResponse pSelector )
设置请求的数据,参数buffer是提交的数据,len是请求数据的长度:
void setRequestData(const char* buffer, unsigned int len);
请求网址:
http://httpbin.org/ip

3、使用cocos2d封装的HttppClient发送请求

HttpClient:
  用来控制请求相关的参数,比如发送请求,设置请求超时时间。它使用单例模型。这一模式的目的是使得类的一个对象成为系统中的唯一实例。cocos2d中多处用到这一模型。
比如Director对象,创建Director::getInstance()。
又比如,音频处理对象: SimpleAudioEngine::getInstance();
   常用方法:
发送请求:
   send(HttpRequest* request)
设置连接超时时间:
  setTimeoutForConnect(int value) 

4、使用cocos2d封装的HttpResponse处理返回的结果

HttpResponse:
包含服务器返回的数据等信息。使用HttpResponse提供的方法可以获取这些数据。
常用方法:
std::vector<char>* getResponseData();//获取数据,存储在vector中
 获取请求返回的数据:
getResponceState
 获取返回状态,返回值是一个整数,200表示请求成功,400表示服务器错误,404表示服务器上找不到相应的文件。
 issucced() ,用来判断是否返回成功

5、使用post方式提交数据和设置请求参数 

1、如何使用post方式提交数据

2、如何区别不同的请求
    发送请求时设置tag,在回调函数里面获取请求的tag
3、设置Content-Type

在http请求头里面设置content-type
    Content-Type,内容类型。用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件。
 设置格式: Content-Type: application/json; 

charset=utf-8
如果未指定 ContentType,默认为TEXT/HTML。

4、sendImmediate和send的区别

默认是按代码顺序确定优先级,使用snedImmediate将当前请求放到队列的最前面

下面是C++代码:

TestHttpScene.h 文件
#ifndef __TestHttp_SCENE_H__
#define __TestHttp_SCENE_H__

#include "cocos2d.h"
//需要包含三个类的头文件
#include "network\HttpRequest.h"
#include "network\HttpClient.h"
#include "network\HttpResponse.h"

USING_NS_CC;
using namespace cocos2d::network;//需要network命名空间,network属于cocos2d明明空间内,Cocos2d-x封装了3个类来处理HTTP请求,HttpRequest、HttpClient和HttpResponse。它们在命名空间cocos2d::network中定义,所以使用时要先声明该命名空间。
class TestHttp : public cocos2d::Layer//一般的类都继承自Layer
{
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();
    
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);

	void complete(HttpClient *client,HttpResponse *response);
    
    // implement the "static create()" method manually
    CREATE_FUNC(TestHttp);
};

#endif // __TestHttp_SCENE_H__
TestHttpScene.cpp 文件
#include "TestHttpScene.h"
#include "curl\include\win32\curl\curl.h"

Scene* TestHttp::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();//创建场景
    
    // 'layer' is an autorelease object
    auto layer = TestHttp::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 TestHttp::init()
{
    //
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
       //1、设置了一个类型为GET的request对象
	auto request = new HttpRequest();//使用new创建一个request对象
	request->setUrl("http://httpbin.org/ip");//设置请求的服务器网址
	request->setTag("type get");//cocos2d 使用标签设置,区分不同的请求 
	//设置请求类型
	request->setRequestType(HttpRequest::Type::GET);//这里设置request的类型为GET
	char data[50] = "data";//创建data对象,用于存储接收的信息
	request->setRequestData(data,strlen(data));//设置接收数组的对象和长度
	request->setResponseCallback(CC_CALLBACK_2(TestHttp::complete,this));//设置回调函数。CC_CALLBACK_2标识两个参数的宏,第一个是毁掉函数,第二个是
               //当前的request对象,这里的回调函数是complete,注意,回调函数的写法,要写上作用域,不能加括号,因为是函数指针

	//2、创建httpclient对象
	auto client = HttpClient::getInstance();//创建一个client单例模式实例
	client->setTimeoutForConnect(60);//设置客户端接受信息的等待时间
	client->setTimeoutForRead(100);//设置科幻段接受信息的读取时间
	client->send(request);//client对象发送设置好的请求对象
	request->release();//发送完成后要记得取消掉

	//1.2设置另外一个request请求,是post方式
	auto postReq = new HttpRequest();
	postReq->setTag("type post");//设置这种请求的标签
	postReq->setUrl("http://httpbin.org/post");//设置Url,这个地址是使用post方式的一个接口
	postReq->setRequestType(HttpRequest::Type::POST);//设置请求类型
//设置请求头信息
	std::vector<std::string> header;//设置请求头信息,这里使用vector
	header.push_back("Content-Type:application/json;charset=utf-8");//请求头信息,content-type类型是json,编码方式是utf-8
	postReq->setHeaders(header);//定义好请求头传递到request对象
	const char* reqData = "controller=cocos2d&action=testHttpPost";//post提交一般需要向服务器传递数据,这里定义数据,这是mvc的网络方式
	postReq->setRequestData(reqData,strlen(reqData));//设置好发送到服务器的数据,包含数据和长度
//区分不同的请求,可以设置不同的回调函数,也可以在一个回调函数使用标签方式区分不同的请求
	postReq->setResponseCallback(CC_CALLBACK_2(TestHttp::complete,this));//两个请求使用了同一个回调函数

	auto client2 = HttpClient::getInstance();//创建client对象
	client2->sendImmediate(postReq);//发送这个post请求,这里使用这个函数,将这个请求的优先级提前到最前面
	postReq->release();//释放该请求

    return true;
}
void TestHttp::complete(HttpClient *client,HttpResponse *response)//回调函数,第一个是client对象,第二个是response对象,都是指针
{
	log("request tag is:%s",response->getHttpRequest()->getTag());//在一个回调函数中获取请求的标签,区分不同的请求。这里使用log打印出这个标签
	log("response code is:%d",response->getResponseCode());//打印状态码,标识是否传输成功200表示请求成功,400表示服务器错误,
//404表示服务器上找不到相应的文件
	if(response->isSucceed())//如果信息返回成功,对数据做处理
	{
		/*std::vector<char> * data = response->getResponseData();//这种方法返回的数据是vector<char>的数组
		log("response data is:");//打印
		for (int i = 0; i < data->size(); i++)//使用循环打印返回的数据,这种打印方式可读性不好,每行一个字符
		{
			log("%c",(*data)[i]);
		}*/
//另外一种打印方式,显示结果较整洁
		std::vector<char> * data = response->getResponseData();//获得response的服务器返回对象
		std::stringstream oss;//把返回的数据输出到字符串流中
		for (int i = 0; i < data->size(); i++)
		{
			oss<<(*data)[i];
		}
		std::string str = oss.str();//把字符串流转变成标准的string字符串
		log("response data is:%s",str.c_str());//把字符串转化成C格式的字符串

		
	}else
	{
		log("error msg is:%s",response->getErrorBuffer());//如果信息返回失败,获取错误信息
	}
}

三、使用websocket进行网络通讯(6-8)

6、websocket代理类和方法

websocket具有socket和http两者的特点,由于他是长连接形式,他的使用和socket使用类似。

   cocos2d同样对websocket进行了封装。WebSocket和WebSocket::Delegate
WebSocket:
   WebSocket类提供了跟websocket相关操作的方法。它的作用包括:
   1、创建socket对象

使用new创建对象
   2、向服务器发送数据,可以是文本数据,也可以是二进制数据

使用新的方法重载发送文本数据和二进制数据
   3、判断链接状态  

如果链接错误,有
       enum class ErrorCode
    {
        TIME_OUT,//连接超时
        CONNECTION_FAILURE,//连接失败
        UNKNOWN,//未知错误
    };
   还有其他错误等等。
WebSocket::Delegate:
   类似Socket。WebSocket::Delegate提供了4个纯虚函数,在使用websocket时,需要先继承Delegate类和实现这4个虚函数。
   
   1、virtual void onOpen(WebSocket* ws) ; 
          当打开websocket连接时调用
   2、virtual void onMessage(WebSocket* ws, const Data& data) ;
          当接收到数据时调用,这个函数相当于http中的response,和回调函数的作用
   3、virtual void onClose(WebSocket* ws) ;
          当关闭连接时调用
   4、virtual void onError(WebSocket* ws, const ErrorCode& error) ;
          当发送数据但没有建立连接,或者是收到断开连接的信号时调用。
代码讲解:继承实现这四个函数

7、创建websocket对象并发送文本数据

创建对象:
   cocos2d::network::WebSocket* wsSendText = new network::WebSocket(),使用new操作符
初始化请求地址:
 wsSendText ->init(*this, "ws://echo.websocket.org")。
初始化成功之后,发送数据:
wsSendText ->send("Hello WebSocket, I'm a text message.");//里面的参数是一个字符串

如果网址不成功,会先调用onError再调用onClose。

8、接受数据并发送二进制数据

在http中,发送什么数据,就返回什么类型的数据

当服务器向客户端发送数据,客户端会调用onMessage方法接收返回的数据
数据使用WebSocket::Data封装。它是cocos2d定义的一个结构体。结果如下:
struct Data
    {
        Data():bytes(nullptr), len(0), issued(0), isBinary(false){}
        char* bytes;//表示返回的数据
        ssize_t len, issued;//标识返回数据的长度
        bool isBinary;//标识返回的数据是二进制还是文本数据
    };
它包含四个字段:bytes存放数据,len存放数据的长度,isBinary用来标识bytes中存放的数据是文本数据还是二进制数据。
使用Data结构体得到数据后,就可以使用这些数据了。

代码如下:

TestWebSocketScene.h文件

#ifndef __TestWebSocketScene_SCENE_H__
#define __TestWebSocketScene_SCENE_H__

#include "cocos2d.h"
#include "network\WebSocket.h"//包含头文件
USING_NS_CC;

using namespace cocos2d::network;//命名空间
class TestWebSocket : public cocos2d::Layer
	,WebSocket::Delegate//继承
{
	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();
   //继承以下四个虚函数
		virtual void onOpen(WebSocket *ws);//websocket的指针对象
		virtual void onMessage(WebSocket *ws,const WebSocket::Data &data);//websocket指针对象,和websocekt封装的数据,引用方式
		virtual void onClose(WebSocket *ws);//websocket指针对象
		virtual void onError(WebSocket *ws,const WebSocket::ErrorCode& error);//websocket指针对象,和错误的引用,注意这里要用到作用域运算符websocket::,用来代表出错的提示信息

		// implement the "static create()" method manually
		CREATE_FUNC(TestWebSocket);//
	private:
		WebSocket *wsText;//定义私有websocket成员变量
};

#endif // __TestWebSocketScene_SCENE_H__

TestWebSocketScene.cpp文件

#include "TestWebSocketScene.h"


Scene* TestWebSocket::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();
    
    // 'layer' is an autorelease object
    auto layer = TestWebSocket::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 TestWebSocket::init()//初始化函数
{
    //
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
	Size size = Director::getInstance()->getWinSize();
	wsText = nullptr;//将websocket成员变量初始化为空指针

	auto menu = Menu::create();//创建菜单,点击这个菜单,初始化一个socket对象,第二个向服务器发送数据
	menu->setPosition(Vec2::ZERO);//定位到屏幕的左下角
	addChild(menu);//添加到游戏场景里面
//点击以下菜单项,创建一个websocket对象
	auto lblSendText = Label::create("init websocket text","Arial",22);//创建文本标签
	auto menuSendText = MenuItemLabel::create(lblSendText,[=](Ref *sender){//创建一个文本菜单项,第一个参数是文本标签,第二个参数是一个回调函数
//初始化一个websocket对象		
        wsText = new WebSocket();
		if( !wsText->init(*this,"ws://echo.websocket.org")){//调用inital方法创建请求地址,第一个是代理类对象,第二个是请求地址。初始化成功返回true,初始化失败,需要把wsText对象富成空指针
			CC_SAFE_DELETE(wsText);//删除wsText指针,设置websocket对象成空指针
		};
	});
	menuSendText->setPosition(size/2);//把该菜单项设置在中央
	menu->addChild(menuSendText);//把该菜单项添加到菜单里面
//点击以下菜单项,发送数据
	auto lblSend = Label::create("sent text","Arial",22);//创建文本标签,发送文本数据
	auto menuSend = MenuItemLabel::create(lblSend,[=](Ref *sender){//创建文本菜单项
		if (wsText!=nullptr)
		{
			wsText->send("hello websocket.");
		}
	});
	menuSend->setPosition(size.width/2,size.height/2-50);//设置未知
	menu->addChild(menuSend);//把菜单项添加到菜单里面
//发送二进制数据
	auto lblSendBinary = Label::create("sent Binary","Arial",22);
	auto menuSendBinary = MenuItemLabel::create(lblSendBinary,[=](Ref *sender){
		if (wsText!=nullptr)
		{
			char buffer[] = "hello \0 binary";//发送二进制数据,首先要创建二进制数据
			wsText->send((unsigned char *)buffer,sizeof(buffer));//这里的参数有,数据和参数的长度
		}
	});
	menuSendBinary->setPosition(size.width/2,size.height/2-100);
	menu->addChild(menuSendBinary);

    return true;
}
//在可执行文件中实现这四个继承的函数
void TestWebSocket::onOpen(WebSocket *ws){
	log("webSocket (%p) open",ws);//打印信息,websocket的地址
	if (ws==wsText)//为了方便查看,使用这种方式,可读性更高,没有settag区分websocket实例
	{
		log("WebSocket send Text open");
	}
	
}

void TestWebSocket::onMessage(WebSocket *ws,const WebSocket::Data &data){
	log("webSocket (%p) receive message",ws);//打印信息,表示接受的信息,注意const前缀
	if (!data.isBinary)//判断数据是二进制还是文本数据
	{//文本数据
		log("received message is :%s",data.bytes);//发送文本数据
	}else//二进制数据的处理
	{
		std::string result;
		for (int i = 0; i < data.len; i++)
		{
			if (data.bytes[i]!='\0')
			{
				result +=data.bytes[i];
			}else
			{
				result += "\'\\0\'";
			}
		}
		log("received binary data is: %s",result.c_str());
	}
}

void TestWebSocket::onClose(WebSocket *ws){
	log("webSocket (%p) close",ws);//打印信息,标识哪一个websocket被关闭了
	if (ws==wsText)//为了方便查看,使用这种方式,可读性更高
	{
		log("WebSocket send Text close");
	}
}

void TestWebSocket::onError(WebSocket *ws,const WebSocket::ErrorCode &error){
	log("WebSocket (%p) error",ws);//标识哪一个websocket发生错误,注意const前缀
	if (ws==wsText)//为了方便查看,使用这种方式,可读性更高
	{
		log("WebSocket send Text error");
	}
}

四、使用socket进行网络通讯(9-12)

9、socket通信流程和cocos2d提供的三个类

Socket几个定义: 
(1)IP地址:即依照TCP/IP协议分配给本地主机的网络地址,两个进程要通讯,任一进程首先要知道通讯对方的位置,即对方的IP。

(2)端口号:用来辨别本地通讯进程,一个本地的进程在通讯时均会占用一个端口号,不同的进程端口号不同,因此在通讯前必须要分配一个没有被访问的端口号。

在服务端两个进程共用一个端口

(3)连接:指两个进程间的通讯链路。
客户/服务器模式:
在TCP/IP网络应用中,通信的两个进程间相互作用的主要模式是客户/服务器(Client/Server, C/S)模式,即客户向服务器发出服务请求,服务器接收到请求后,提供相应的服务。
Socket通信流程
服务端:
1、服务器端先初始化Socket,创建一个socket对象
2、当一个套接字用socket()创建后,存在一个名字空间(地址族),但它没有被命名。bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字号联系起来,即将名字赋予套接字,以指定本地半相关
3、对端口进行监听(listen)
4、调用accept阻塞,等待客户端连接。服务段处于阻塞状态时,客户端的连接请求会被服务端响应
客户端:
1、客户端初始化一个Socket
2、然后连接服务器(connect)
3、如果连接成功,这时客户端与服务器端的连接就建立了。
4、客户端发送数据请求(send),服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据。
5、最后关闭连接,一次交互结束。


类似http,cocos2d对socket也进行了封装。提供了SocketIO,SIODelegate和SIOClient三个类。
SocketIO
:
connet方法,创建一个指定目标服务器的socket,并返回SIOClient对象。
使用单例模式,初始化Socket,获取SIOClient。
SIODelegate:
使用Socket协议,首先要继承SIODelegate,并且还要实现SIODelegate的4个虚函数
// 当打开socket连接时会调用这个函数
virtual void onConnect(cocos2d::network::SIOClient* client);
// 当接收到数据时会调用这个函数
virtual void onMessage(cocos2d::network::SIOClient* client, const std::string& data);
// 当socket关闭时,会调用这个函数
virtual void onClose(cocos2d::network::SIOClient* client);
// 当连接错误或接收到错误信号时会调用这个函数
virtual void onError(cocos2d::network::SIOClient* client, const std::string& data);
SIOClient:
处理Socket的一系列动作,比如,发送数据send,监听事件commit,断开连接disconnect等

10、实现SIODelegate虚函数

使用Socket协议,首先要继承SIODelegate,并且还要实现SIODelegate的4个虚函数

11、使用socket发送接受数据

使用 SocketIO的静态成员函数connect创建SIOClient实例。
SIOClient调用send方法发送数据。
uri:ws://channon.us:3000

12、发送事件和事件监听

Socket.io提供了事件监听的机制,使用emit提交事件和数据,使用on监听事件和获取接收到的数据。
比如客户端添加如下代码
client.emit("login","[{\"name\":\"myname\",\"pwd\":\"mypwd\"}]")
执行该代码后会向服务端发送login事件,并把用户名和密码传递给服务器。
服务器端通过执行下面的代码,用来监听login事件,并获取传递过来的数据,然后在调用emit方法向客户端发送事件和数据。客户端使用on进行监听。这样就实现了客户端和服务端之间的数据通信。
socket.on('login', function(obj){
   //向所有客户端广播用户加入
   io.emit('loginresult', {message:'login success'});
  });
 testevent   echotest

代码:

#ifndef __TestSocketIo_SCENE_H__
#define __TestSocketIo_SCENE_H__

#include "cocos2d.h"

#include "network\SocketIO.h"


USING_NS_CC;
using namespace cocos2d::network;

class TestSocketIo : public cocos2d::Layer
	,SocketIO::SIODelegate
{
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();
    
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    //继承和实现SIODelegate四个虚函数
	void onConnect(SIOClient* client);
	void onMessage(SIOClient* client, const std::string& data);
	void onError(SIOClient* client, const std::string& data);
	void onClose(SIOClient* client);

    // implement the "static create()" method manually
    CREATE_FUNC(TestSocketIo);

	SIOClient *client;//创建指针对象
	
};

#endif // __TestSocketIo_SCENE_H__

.cpp

#include "TestSocketIoScene.h"


Scene* TestSocketIo::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();
    
    // 'layer' is an autorelease object
    auto layer = TestSocketIo::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 TestSocketIo::init()
{
    //
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
	Size size = Director::getInstance()->getWinSize();
	client = nullptr;
//创建一个菜单
	auto menu = Menu::create();
	menu->setPosition(Vec2::ZERO);
	addChild(menu);//添加菜单到游戏场景里面
//创建菜单项(点击),点击则初始化socket连接
	auto lblInit = Label::create("init socket","Arial",22);//创建文本标签
	auto menuInit = MenuItemLabel::create(lblInit,[=](Ref *sender){//C++ 11匿名函数,回调函数
		client = SocketIO::connect("ws://192.168.1.102:3000", *this);//静态函数
		client->setTag("init socket");
		client->on("loginresult",[=](SIOClient *client,const std::string &data){//在初始化时,设定监听器
			log("login result is :%s",data.c_str());
		});
	});
	menuInit->setPosition(size/2);
	menu->addChild(menuInit);
//创建菜单项(点击),点击则发送数据
	auto lblSend = Label::create("send message","Arial",22);
	auto menuSend = MenuItemLabel::create(lblSend,[=](Ref *sender){
		client->send("hello socket.io");
	});
	menuSend->setPosition(size.width/2,size.height/2-50);
	menu->addChild(menuSend);
//创建菜单项(点击),向服务器请求login事件,
	auto lblSendEvent = Label::create("emit event","Arial",22);
	auto menuSendEvent = MenuItemLabel::create(lblSendEvent,[=](Ref *sender){
		client->emit("login","[{\"name\":\"myname\",\"pwd\":\"mypwd\"}]");
	});
	menuSendEvent->setPosition(size.width/2,size.height/2-100);
	menu->addChild(menuSendEvent);

    return true;
}

void TestSocketIo::onConnect(SIOClient* client){//参数对象是SIOClient类型
	log("onConnect");
	log("%s connect",client->getTag());//使用getTag区分是哪个请求
}

void TestSocketIo::onMessage(SIOClient* client, const std::string& data){//当服务器返回数据的时候,自动调用该方法
	log("onMessage");
	log("%s received content is:%s",client->getTag(),data.c_str());//这里也是用了getTag区分是哪个请求,使用c_str转换成c字符串
}

void TestSocketIo::onClose(SIOClient * client){//参数对象是SIOClient类型
	log("onClose");
	log("%s is closed",client->getTag());//用了getTag区分是哪个请求关闭了
}
void TestSocketIo::onError(SIOClient* client, const std::string& data){
	log("onError");
	log("%s error is:%s",client->getTag(),data.c_str());//这里也是用了getTag区分是哪个请求,使用c_str转换成c字符串
}

var io = require('socket.io').listen(3000,'192.168.1.102');
console.log('Server on port 3000...');
io.sockets.on('connection',function(socket)
{
    //向客户端发送消息
    socket.send('Hello Cocos2d-x');
    //注册message事件
    socket.on('message',function(data)
    {
        console.log(data);
    });

    //注册callServerEvent事件,便于客户端调用
    socket.on('login',function(data0)
    {
        console.log(data0);
        //向客户端发送消息,处罚客户端的callClientEvent事件
        socket.emit('loginresult',{message:'login success'});
    });
});

五、使用rapidjson解析数据(13-17)

13、json基础及28种解析库性能对比

JSON 概念和特点:
    JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
    JSON 是轻量级的文本数据交换格式,类似 XML,但比 XML 更小、更快,更易解析
    JSON 独立于语言 *
    JSON 具有自我描述性,更易理解  。
JSON的创建方式与创建 JavaScript 对象的代码相同。
Json的格式:
{
    "project_type": "javascript",
    "debugMode" : 1,
    "showFPS" : true,
    "frameRate" : 60,
    "id" : "gameCanvas",
    "renderMode" : 0,
    "engineDir":"frameworks/cocos2d-html5",
    "modules" : ["cocos2d", "extensions"],
    "jsList" : [
        "src/loader/loaderRes.js",
        "src/loader/loaderScene.js"
    ]
}
JSON 值可以是:
       数字(整数或浮点数)
       字符串(在双引号中)
       逻辑值(true 或 false)
       数组(在方括号中)
       对象(在花括号中)
       null。
JSON 是 JavaScript 原生格式。JavaScript可以直接使用JSON,不需要调用api或工具包。
由于JSON的易用性,其他语言也提供了强大的JSON 解析器和 JSON 库。c++语音有28种json库。下面的图表是对28种json库的评估:
符合标准程度(越高越好):
解析至DOM的时间(越低越好):
把DOM生成含换行及缩进的JSON的时间(越低越好):
可执行文件(把JSON解析至DOM,然后统计JSON类型)的大小(越低越好):

14、使用Rapidjson解析字符串成json格式数据

1、创建一个rapidjson::Document对象rapidJson的相关操作都在Document类中
   rapidjson::Document
2、调用Parse方法解析json数据
   doc.Parse<0>(data.c_str());
3、使用HasParseError()判断解析出来的document是否正确。解析正确才能做进一步操作。

15、使用rapidjson读取和更改数据

在rapidJson中,使用rapidjson::Value代表json数据中的值。
rapidjson::Value其实就是js中的var,可以把rapidjson::Value当作int,也可以当作string,也可以当作数组等。对于定义rapidjson::Value value,只是一个定义,还没有决定其数据类型,如果明确value的类型,再转成相应的格式。
Value的类型包括int,Double,string,bool,array,obejct,null。
json使用key-value的方式,读取数据时使用脚标的方式,在获取脚本之前,还可以使用HasMember方法判断是否有这个键值对:
rapidjson::Value  &value=doc["hello"];
然后判断value的类型:value.IsString()判断是否是字符串,IsArray()判断是否是数组等
判断好数据的类型后,可以调用对应的方法获取c/c++数据类型。比如如果value是字符串,则可以通过getString方法获取字符串的值。
并且可以通过SetString方法修改该value的值。
除了修改value的值,也可以修改value的类型,通过set+类型的方法设置,有下面这些方法可以设置value的值。
vall.SetArray()
vall.SetArrayRaw()
vall.SetBool()
vall.SetDouble()
vall.SetInt()
vall.SetNull()
vall.SetObject()
vall.SetString()
vall.SetStringRaw()
vall.SetUint();
vall.SetUint64()

16、把json数据转成字符串

使用rapidjson::Writer 把 rapidjson::Value 转成符合json数据格式的字符串:
先声明一个rapidjson::StringBuffer类型的数据
    rapidjson::StringBuffer  buffer;
再创建一个rapidjson::Writer对象
    rapidjson::Writer<rapidjson::StringBuffer>  writer(buffer);
    doc.Accept(writer); 
然后就可以通过buffer.GetString()获取c/c++的字符串了。
把字符串打印到控制台,或写入到文件中

17、使用rapidjson添加、删除数据

Rapidjson除了能够读取和修改数据,还能添加和删除数据。
添加数据时,先获取一个分配器
  rapidjson::Document::AllocatorType  &allocator = doc.GetAllocator();
添加一个数:
doc.AddMember("age",15,allocator);
添加一个字符串:
doc.AddMember("name","liao li",allocator);
///添加一个null对象
rapidjson::Value nullObject(rapidjson::kNullType);
doc.AddMember("null",nullObject,allocator);///<往分配器中添加一个对象
添加一个对象:
rapidjson::Value  object(rapidjson::kObjectType);///<创建数组里面对象。
object.AddMember("age",40,allocator);
object.AddMember("name","father ",allocator);
doc.AddMember("father",object,allocator);
///添加一个数组对象
rapidjson::Value  array(rapidjson::kArrayType);///<创建一个数组对象
array.PushBack(1,allocator);
array.PushBack(2,allocator);
array.PushBack(3,allocator);
doc.AddMember("array",array,allocator)
///在已有的数组中添加一个成员对象
rapidjson::Value  &aArray1=doc["array"];
aArray1.PushBack(4,allocator);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值