zoukankan      html  css  js  c++  java
  • Cocos2d-X网络编程(4) Cocos2d中的网络通信协议——Socket通信

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

    Socket基础和通信流程:

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

    cocos封装的3个Socket通信相关类:

    类似http,cocos2d对socket也进行了封装。提供了SocketIO,SIODelegate和SIOClient三个类。
    SocketIO:
    使用单例模式,初始化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的一系列动作,比如,发送数据,监听事件,断开连接等

    发送事件和事件监听:

    除了上面的发送Socket请求外,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'});
      });


    代码实操:

    头文件:

    #ifndef __TestSocketIo_SCENE_H__
    #define __TestSocketIo_SCENE_H__
    
    #include "cocos2d.h"
    #include "networkSocketIO.h"
    USING_NS_CC;
    using namespace cocos2d::network;
    
    class TestSocketIo : public cocos2d::Layer
    	,SocketIO::SIODelegate
    {
    public:
        static cocos2d::Scene* createScene();
        virtual bool init();
    	CREATE_FUNC(TestSocketIo);
        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);
    
    	// 创建Socket
    	SIOClient *client;
    	
    };
    
    #endif // __TestSocketIo_SCENE_H__
    

    源文件:

    #include "TestSocketIoScene.h"
    
    
    Scene* TestSocketIo::createScene()
    {
        auto scene = Scene::create();
        auto layer = TestSocketIo::create();
        scene->addChild(layer);
        return scene;
    }
    
    bool TestSocketIo::init()
    {
        if ( !Layer::init() )
        {
            return false;
        }
    	Size size = Director::getInstance()->getWinSize();
    	client = nullptr;
    
    	auto menu = Menu::create();
    	menu->setPosition(Vec2::ZERO);
    	addChild(menu);
    
    	auto lblInit = Label::create("init socket","Arial",22);
    	auto menuInit = MenuItemLabel::create(lblInit,[=](Ref *sender){
    		// 初始化
    		client = SocketIO::connect("ws://192.168.1.102:3000", *this);
    		// 设置tag区分不同请求
    		client->setTag("init socket");
    		
    		// 使用Socket.io提供的on监听事件
    		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);
    
    	auto lblSendEvent = Label::create("emit event","Arial",22);
    	auto menuSendEvent = MenuItemLabel::create(lblSendEvent,[=](Ref *sender){
    		// 使用Socket.io提供的emit提交事件
    		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){
    	log("onConnect");
    	log("%s connect",client->getTag());
    }
    
    void TestSocketIo::onMessage(SIOClient* client, const std::string& data){
    	log("onMessage");
    	log("%s received content is:%s",client->getTag(),data.c_str());
    }
    
    void TestSocketIo::onClose(SIOClient * client){
    	log("onClose");
    	log("%s is closed",client->getTag());
    }
    void TestSocketIo::onError(SIOClient* client, const std::string& data){
    	log("onError");
    	log("%s error is:%s",client->getTag(),data.c_str());
    }
    

    服务器端:使用的node.js

    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'});
        });
    });
  • 相关阅读:
    古代规模最大的战争:长平之战(做事不能太小气,不同的将领有不同的视角,要智胜,活着很重要)
    聚集索引更新后会不会马上重新排序
    GitHub Pages 搭建流程-基于jekyll-bootstrap
    OpenStack调研
    领域模型设计
    Load ContextCLR 探测
    Sql Server Job 简单使用
    Power Designer导出实体类和NHibernate xml文件
    解决跨域
    性能计数器
  • 原文地址:https://www.cnblogs.com/lmx282110xxx/p/10798715.html
Copyright © 2011-2022 走看看