Web 在线数据建模

.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_modules\socket.io\node_modules\engine.io\node_modules\engine.io-parser\lib\index.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_modules\socket.io\node_modules\engine.io\lib\socket.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_modules\socket.io\node_modules\socket.io-parser\index.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_modules\socket.io\node_modules\socket.io-parser\node_modules\component-emitter\index.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",.........]” 到客户端

    

    

        

 

posted @ 2016-01-19 11:06  NetUML大数据搜索  阅读(4437)  评论(8编辑  收藏  举报
Web 在线数据建模