背景
公司的同事自己用socket io实现websocket服务,我用node js websocket去连接发现连接不上,于是我就找我同事。我同事说:必须要用socket io client连接才可以。我觉得很奇怪,一个weocket框架怎么还不能满足普通的实现,我说你是不是代码写错了,后面发现是我理解错了,我同事也理解错了,我们当初定义协议走websocket 通信,协议就直接走json 或者其他自定义协议即可。
自己花了半天时间看帮助文档和代码开发
分析过程
- 直接分析源代码调试
- 看官方的帮助文档,全部看完
socket io 到底是什么?
socket io是一个网络通信框架,依赖websocekt,并没有实现websocket,对应业务层逻辑,跟我们自己依赖系统套接字(tcp协议)实现自定义协议是业务层一样。如果我们用websocket实现自定义协议,别人用websocket必须按照我们定自定义协议通信,所以socket io 服务,不能用原生的websocket连接,我自己用127.0.0.1:3000/socket.io/?transport=websocket&EIO=4 虽然可以建立的链接,但发送消息就立即断开,因为包的格式不对。
它定义协议包,我们只要emit 提交包就可以了,我们可以给基本类型,或者给对象或者Buffer(二进制数据),基本就不用管协议了。
socket io 几个我用到特性
emit 投递数据包
//客户端监听
socket.emit("hello", { a: "b", c: [] });
//服务监听hello 消息
server.io.on("hello", (a,b,c)=>{
})
这个因为自定义协议,所以包的定义就不要了,投递内容就可以了。
纯websocket 不能emit 投递数据,不是给网络使用的,所以这个特性用起来就特别舒服,减少一些工作量。
不然我们就要在服务器端 在onmessage 来自己解释数据包了,那么就要定义id字段,来判断是什么类型的。具体包的协议我没有研究,如果自己就用二进制来传递,解包就必须一个一个写。socket io 能判断传递类型,那么说明就是在网络协议里面传了类型,不然无法知道什么类型。
身份验证
客户端在连接时候可以给socket auth复制,比喻传递一个对象{token: "验证密钥"},也可以传递函数,用函数的返回值灵活获取。
如果你token有过期时间,那么建议传递函数,因为要更新token.
官方的例子
import { io } from "socket.io-client";
const socket = io("ws://example.com/my-namespace", {
reconnectionDelayMax: 10000,
auth: {
token: "123"
},
query: {
"my-key": "my-value"
}
});
重连
如果服务器重启,或者客户端断网,那么socket io 自己会进行重连。如果服务器主动disconnet的话是不会进行重连的。
重连得到时候默认传递之前给的token,如果token有更新就校验失败(如果过期的话),所以还是建议传递函数。
request-respond的模式支持 类似http request- respond 一一对应
你可以提交一个包,最后一个参数是一个函数话,服务器会通过通过回调函数传递参数(肯定不真调用回调,看起来像而已,2个跨网络 怎么可能直接调用呢,这个就点像ipc)
socket.emit("task1",data,call_back);
原理
创建任务id跟你数据包一起传递给服务器,但服务器会传送数据也会带上这个任务ID,但检测有任务id进行比对,找到当初回调函数,调用即可。 一些好多网络库也是这么开发的 ,通过任务ID实现。
http 半双工,只能一问一答(协议决定),http 发送请求后就一直等待结果,结果无法访问时候,可能不能发送内容(http 1.1)http 2.0 我就不知道了,如果实现全双工的话,估计也是通过类似task id逻辑是实现
socket io 实现回调代码
/**
* Called upon a server acknowlegement.
*
* @param packet
* @private
*/
onack(packet) {
const ack = this.acks[packet.id];
if ("function" === typeof ack) {
debug("calling ack %s with %j", packet.id, packet.data);
ack.apply(this, packet.data);
delete this.acks[packet.id];
}
else {
debug("bad ack %s", packet.id);
}
}
//emit(ev, ...args)
//最后一个参数如果函数,创建packet,服务器响应时候,传送回来即可
//这样子就可以变成Http
if ("function" === typeof args[args.length - 1]) {
const id = this.ids++;
debug("emitting packet with ack id %d", id);
const ack = args.pop();
this._registerAckCallback(id, ack);
packet.id = id;
}
room的概念
我没有写,但以后用到,这样子方便对用户进行过滤,这个业务方便对用户进行快速操作而已,不用自己进行for循环遍历比较了
不支持websocket会用http 轮训模式
默认就是轮询,所以建议直接设置transport 为 ['websocket', 'polling'] 2个服务器。但我没有测试,如果开始用轮询是否会升级到websocket,没有验证这个需求。
socket io 简单总结
socket io 是一个网络框架,封装一些业务功能,所以你不能用纯websocket连接,具备验证,重连,房间,自动解包特性,加快业务开发能力。市面太多用来聊天软件,我觉得主要原因是浏览器兼容性,他可以一套代码兼容所有浏览器能力(可以在不支持websocket浏览器跑),因为聊天网页不知道用户在什么浏览器上,但我感觉这个特性在未来会越来越弱,因为浏览器版本升级,win7 等系统份额越来越少,那么以后系统浏览器标准化,感觉这个框架就比较弱了,因为稍微有实力公司自己在websocket 写一套业务很简单,不过在socket io 上二次开发也简单。
现在互联网是一个好的时代,什么业务都基本对应的开源代码,什么东西都参考,除了小部分高端或者底层东西没有。对于小公司来说太方便了,开发速度嘎嘎的。
感谢那些为开源做出贡献的那些人