.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 Codeexports.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的事件名稱。