zoukankan      html  css  js  c++  java
  • .net , java webSocket 连接 Socket.io (1.4.4版本) 问题

      .net版Socketio4net类库和java版socket.io-java-client类库 连接socket.io 1.4版本都不行,网上大多是socket.io 0.9版本的,socket.io 更新之后就不支持了。本人已研究

    成功连接socket.io 1.4版本的方法,例子采用C#。

      一、socket.io 几个重要要文件

       1、node_modulessocket.io ode_modulesengine.io ode_modulesengine.io-parserlibindex.js  

    var packets = exports.packets = {
        open:     0    // non-ws
      , close:    1    // non-ws
      , ping:     2
      , pong:     3
      , message:  4
      , upgrade:  5
      , noop:     6
    };

        这几个是定义消息类型的 websocket连接的时候在open事件里需要发送一个send("5["simple",{"name":"simple"}]"); 类型为5的消息。

        

    exports.decodePacket = function (data, binaryType, utf8decode) {
      // String data
       console.log('解析数据'+data);
      if (typeof data == 'string' || data === undefined) {
        if (data.charAt(0) == 'b') {
          return exports.decodeBase64Packet(data.substr(1), binaryType);
        }
    
        var type = data.charAt(0);
         
        if (utf8decode) {
          try {
            data = utf8.decode(data);
          } catch (e) {
            return err;
          }
        }
        console.log('解析数据3:'+type);
        if (Number(type) != type || !packetslist[type]) {
          return err;
        }
    
        if (data.length > 1) {
          return { type: packetslist[type], data: data.substring(1) };
        } else {
          return { type: packetslist[type] };
        }
      }
    
      // Binary data
      if (binaryType === 'arraybuffer') {
        var type = data[0];
        var intArray = new Uint8Array(data.length - 1);
        for (var i = 1; i < data.length; i++) {
          intArray[i - 1] = data[i];
        }
        return { type: packetslist[type], data: intArray.buffer };
      }
      var type = data[0];
      return { type: packetslist[type], data: data.slice(1) };
    };
    View Code

       从客户端发过来的消息会从这里解析出来,得到消息类型。

       2、node_modulessocket.io ode_modulesengine.iolibsocket.js

         从上面解析出来的消息字符串会到这里

    Socket.prototype.onPacket = function (packet) {
        console.log('engine.io-lib-Socket.js==OnPacket///'+packet);
      if ('open' == this.readyState) {
        // export packet event
        debug('packet');
        this.emit('packet', packet);
    
        // Reset ping timeout on any packet, incoming data is a good sign of
        // other side's liveness
        this.setPingTimeout();
        console.log('engine.io-lib-Socket.js==OnPacket>>>'+packet.type);//upgrade
        switch (packet.type) {
    
          case 'ping':
            debug('got ping');
            this.sendPacket('pong');
            this.emit('heartbeat');
            break;
    
          case 'error':
            this.onClose('parse error');
            break;
    
          case 'message':
            this.emit('data', packet.data);
            this.emit('message', packet.data);
            break;
        }
      } else {
        debug('packet received with closed socket');
        console.log('packet received with closed socket');
      }
    };
    View Code

       3、node_modulessocket.io ode_modulessocket.io-parserindex.js

    function decodeString(str) {
        console.log('socket.io-parser-index.js-encodeAsString4---'+str);
      var p = {};
      var i = 0;
    
      // look up type
      p.type = Number(str.charAt(0));
      if (null == exports.types[p.type]) return error();
    
      // look up attachments if type binary
      if (exports.BINARY_EVENT == p.type || exports.BINARY_ACK == p.type) {
         console.log("---------1");
        var buf = '';
        while (str.charAt(++i) != '-') {
          buf += str.charAt(i);
          if (i == str.length) break;
        }
        if (buf != Number(buf) || str.charAt(i) != '-') {
          throw new Error('Illegal attachments');
        }
        p.attachments = Number(buf);
      }
      
      // look up namespace (if any)
      if ('/' == str.charAt(i + 1)) {
        p.nsp = '';
        while (++i) {
          var c = str.charAt(i);
          if (',' == c) break;
          p.nsp += c;
          if (i == str.length) break;
        }
      } else {
        p.nsp = '/';
      }
    
      // look up id
      var next = str.charAt(i + 1);
      if ('' !== next && Number(next) == next) {
        p.id = '';
        while (++i) {
          var c = str.charAt(i);
          if (null == c || Number(c) != c) {
            --i;
            break;
          }
          p.id += str.charAt(i);
          if (i == str.length) break;
        }
        p.id = Number(p.id);
      }
    
      // look up json data
      if (str.charAt(++i)) {
        try {
          console.log("---------21/"+str.substr(i));
          p.data = json.parse(str.substr(i));
        } catch(e){
          return error();
        }
      }
      console.log(p);
      debug('decoded %s as %j', str, p);
      return p;
    }
    View Code
    exports.types = [
      'CONNECT',
      'DISCONNECT',
      'EVENT',
      'ACK',
      'ERROR',
      'BINARY_EVENT',
      'BINARY_ACK'
    ];
    
    /**
     * Packet type `connect`.
     *
     * @api public
     */
    
    exports.CONNECT = 0;
    
    /**
     * Packet type `disconnect`.
     *
     * @api public
     */
    
    exports.DISCONNECT = 1;
    
    /**
     * Packet type `event`.
     *
     * @api public
     */
    
    exports.EVENT = 2;
    
    /**
     * Packet type `ack`.
     *
     * @api public
     */
    
    exports.ACK = 3;
    
    /**
     * Packet type `error`.
     *
     * @api public
     */
    
    exports.ERROR = 4;
    
    /**
     * Packet type 'binary event'
     *
     * @api public
     */
    
    exports.BINARY_EVENT = 5;
    
    /**
     * Packet type `binary ack`. For acks with binary arguments.
     *
     * @api public
     */
    
    exports.BINARY_ACK = 6;
    View Code

        然后消息会传递到这里,再解析它。

         4、node_modulessocket.io ode_modulessocket.io-parser ode_modulescomponent-emitterindex.js

        最后消息会到这里找到对应的回调函数。

    Emitter.prototype.emit = function(event){
        
     // console.log(arguments);
     //  console.log("event");
      //console.log(event);
      // console.log("_callbacks");
      // console.log( this._callbacks);
      this._callbacks = this._callbacks || {};
      var args = [].slice.call(arguments, 1)
        , callbacks = this._callbacks[event];
        //console.log('args');
        //console.log(args);
        //console.log('callbacks');
        
      if (callbacks) {
         //  console.log('回调 正确');
        callbacks = callbacks.slice(0);
        
        console.log(callbacks);
        for (var i = 0, len = callbacks.length; i < len; ++i) {
          callbacks[i].apply(this, args);
           // console.log('执行 正确');
        }
      }
      else
      {
          console.log('回调 出错');
      }
    
      return this;
    };
    View Code

       二、socket.io授权

        1、.net授权获取sid

           授权地址http://127.0.0.1:3000/socket.io/?eio=3&transport=polling&t=1404421022936,0.9版本的socket.io授权不一样,通过这个授权地址返回

             sessionid,如下格式 0{"sid":"BrB2vsiK79ZoLdMcAAAK","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":60000},解析得到sid.

    protected SocketIOHandshake requestHandshake(Uri uri)
            {
                string value = string.Empty;
                string errorText = string.Empty;
                SocketIOHandshake handshake = null;
    
                using (WebClient client = new WebClient())
                { 
                    try
                    {
                        client.Headers.Add("cookie:io=3435456567567567355");
                       // client.Headers.Add("cookie:express.sid=3435456567567567355");
                        //client.Headers.Add("cookie:sid=3435456567567567355");
                        value = client.DownloadString("http://127.0.0.1:3000/socket.io/?eio=3&transport=polling&t=1404421022936");
                        int ii = value.IndexOf("",");
                        int im = value.IndexOf("":"");
                        value = value.Substring(im+3, ii-im-3);
                        //value = "3435456567567567355";
                        //value = client.DownloadString(string.Format("{0}://{1}:{2}/socket.io/1/{3}", uri.Scheme, uri.Host, uri.Port, uri.Query)); // #5 tkiley: The uri.Query is available in socket.io's handshakeData object during authorization
                        value = value+":55000:60000:websocket";
                        if (string.IsNullOrEmpty(value))
                            errorText = "Did not receive handshake string from server";
                    }
                    catch (Exception ex)
                    {
                        errorText = string.Format("Error getting handsake from Socket.IO host instance: {0}", ex.Message);
                        //this.OnErrorEvent(this, new ErrorEventArgs(errMsg));
                    }
                }
                if (string.IsNullOrEmpty(errorText))
                    handshake = SocketIOHandshake.LoadFromString(value);
                else
                {
                    handshake = new SocketIOHandshake();
                    handshake.ErrorMessage = errorText;
                }
    
                return handshake;
            }
    View Code

          以下是socket.io接收到的授权消息,能够取到客户端传来的cookie,可以用过控制重复登录。

    io.set('authorization', function(handshakeData, callback) {
        // callback(handshakeData, true);    
         callback(null, true);
        return
        if (handshakeData.headers.cookie) {
            //console.log(handshakeData.headers.cookie);
            handshakeData.cookie = cookie.parse(handshakeData.headers.cookie);
            //console.log(handshakeData.cookie);
            handshakeData.cookie['express.sid']=handshakeData.cookie.io;
            handshakeData.sessionID = handshakeData.cookie['express.sid'];
            //console.log(handshakeData.sessionID);
            //console.log(handshakeData.cookie['express.sid']);
            
            console.log("handshakeData:" + handshakeData.headers.cookie + "-----" + handshakeData.cookie);
        
            //var connect_sid = handshakeData.cookie['connect.sid'];
            //console.log("connect_sid="+connect_sid);
            handshakeData.session = handshakeData.sessionID;
            if (handshakeData.cookie['express.sid'] == handshakeData.sessionID) {
              console.log('1-true');
              return callback(null, true);
            }
            //return callback('Cookie is invalid.', false);
        } 
        else {
            console.log('12-err');
            //return callback('No cookie transmitted.', false);
        }     
    });
    View Code

      三、websocket 连接

         websocket连接地址ws://127.0.0.1:3000/socket.io/?eio=3&t=124324324324&transport=websocket&sid=" + this.HandShake.SID,这个很重要

    public void Connect()
            {
                lock (padLock)
                {
                    if (!(this.ReadyState == WebSocketState.Connecting || this.ReadyState == WebSocketState.Open))
                    {
                        try
                        {
                            this.ConnectionOpenEvent.Reset();
                            this.HandShake = this.requestHandshake(uri);// perform an initial HTTP request as a new, non-handshaken connection
    
                            if (this.HandShake == null || string.IsNullOrWhiteSpace(this.HandShake.SID) || this.HandShake.HadError)
                            {
                                this.LastErrorMessage = string.Format("Error initializing handshake with {0}", uri.ToString());
                                this.OnErrorEvent(this, new ErrorEventArgs(this.LastErrorMessage, new Exception()));
                            }
                            else
                            {
                                String sss = "ws://127.0.0.1:3000/socket.io/?eio=3&t=124324324324&transport=websocket&sid=" + this.HandShake.SID;
                                //sss = "ws://127.0.0.1:3000/socket.io/?transport=polling&t=12434324324324&sid=" + this.HandShake.SID;
                                //string.Format("{0}://{1}:{2}/socket.io/1/websocket/{3}", wsScheme, uri.Host, uri.Port, this.HandShake.SID)
                                string wsScheme = (uri.Scheme == Uri.UriSchemeHttps ? "wss" : "ws");
                                this.wsClient = new WebSocket(
                                    sss,
                                    string.Empty,
                                    this.socketVersion);
                                this.wsClient.EnableAutoSendPing = false; // #4 tkiley: Websocket4net client library initiates a websocket heartbeat, causes delivery problems
                                this.wsClient.Opened += this.wsClient_OpenEvent;
                                this.wsClient.MessageReceived += this.wsClient_MessageReceived;
                                this.wsClient.Error += this.wsClient_Error;
    
                                this.wsClient.Closed += wsClient_Closed;
    
                                this.wsClient.Open();
                            }
                        }
                        catch (Exception ex)
                        {
                            Trace.WriteLine(string.Format("Connect threw an exception...{0}", ex.Message));
                            this.OnErrorEvent(this, new ErrorEventArgs("SocketIO.Client.Connect threw an exception", ex));
                        }
                    }
                }
            }
    View Code

        连接之后在open 事件里需要发送一个类型为5(upgrade 心跳)的消息websocket.send("5["simple",{"name":"simple"}]");,然后websocket会收到一个“40”消息,

       40代表连接功能了,可以进行通信了。

      一般发送消息的格式为:"42["simple",{"name":"tstssss"}]"

      42:代表消息类型,simple为socket.io的事件名称。

      四、定时发送心跳数据

          授权的时候能获取到"pingInterval":25000,"pingTimeout":60000 心跳间隔和超时的时间,需要每隔 pingInterval 时间 发送一次心跳数据才能保存不断开连接。

        send("5:::");

      五、带回调函数的方法

        服务器回调:

        socket.io 服务器端给客户端发送数据带回调函数如下:

      socket.emit('newuser','newuser-data',function(m,d){
            console.log(m);
            console.log(d);
        });

       客户端接收到的数据形式如下: 420["newuser","newuser-data"] 或 4290203["newuser","newuser-data"]

       其中4代表:message,2代表:event ,0 ,90203 代表:回调函数的事件ID号,事件ID号是不固定的

       如果客户端收到消息,服务器需要触发回调函数时:

       this.send("430["newuser",{"name":"simple"}]");

       this.send("4390203["newuser",{"name":"simple"}]");

       其中 3代表:ack 回调, “newuser”必须和原有名字一致。

        客户端回调:

      

        socket.on('messageAck', function (data,fn) {
            console.log(data);
            //console.log(fn);
            fn('aaa','bb','cc',{id:1});
        });

       客户端发送 this.send("423["messageAck","ssssssssssssssssss"]"); ,3 代表消息ID

    服务器收到信息之后 回立马发送  “433["messageAck",.........]” 到客户端

        

        

            

  • 相关阅读:
    将博客搬至CSDN
    smarty不渲染html页面
    开篇
    html的入门——从标签开始(1)
    java内部编码
    用base64Encoder进行编码 和base64Decoder解码
    序列化和反序列化
    HttpServletResponse类
    配置Tomcat服务器数据连接池
    SVN修改地址
  • 原文地址:https://www.cnblogs.com/netuml/p/5141345.html
Copyright © 2011-2022 走看看