一般网络编程都叫Socket编程,网络编程通常采用的是经典的客户端-服务器模型(CS 模型)。

网络编程无非简单来看就是 TCP编程 和 UDP编程。

Socket

每一次建立 socket 连接后,都会生成一个 SessionId,便于标识通讯中的客户端和服务端。(SessionId 应保证分布式唯一)

socket 简介

Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket 其实就是一个门面模式,它把复杂的 TCP/IP 协议族隐藏在 Socket 接口后面,对用户来说,一组简单的接口就是全部,让 Socket 去组织数据,以符合指定的协议。

套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。 套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。 网络套接字是IP地址与端口的组合。

socket

阮一峰老师曾讲到:socket就是插座。服务器的socket,就是服务器提供插座,等着客户端的插头插进来。一旦插入完成,服务器-客户端的通信就建立了。 ​​​​

socket 作用

socket(套接字)作用:就是应用层获取传输层的协议,是应用层和传输层沟通的桥梁。

socket作用

socket 类型

常用的 Socket 类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。

  • 流式,二进制流,是一种面向连接的 Socket,针对于面向连接的 TCP 服务应用。应用场景:im 通讯,安全
  • 数据报式 Socket 是一种无连接的 Socket,对应于无连接的 UDP 服务应用。应用场景:短信、视频直播,传输速度快

简单对比一下

  • TCP:比较靠谱,面向连接,安全,可靠的传输方式 ,但是比较慢
  • UDP:不是太靠谱,不可靠的,丢包不会重传,但是比较快

CS 模型

c: client,s: server

socket cs模型

WebSocket

简介

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

实现思路

  1. 监听:首先要挂起一个进程来监听来自客户端的请求
  2. 握手:对于第一次合法的请求,发送合法的header回去
  3. 保持连接:有新消息到了就广播出去。直到客户端断开
  4. 接受另一个请求,重复2和3

特性

  • 建立在 TCP 连接之上
  • 较少的控制开销
  • 更强的实时性
  • 保持连接状态
  • 可以发送文本、二进制数据
  • 没有同源限制

连接过程

websocket 的连接还是比较简单的,只要进行 一次握手,就可以进行后续的通讯了,这里分为三步:

  1. http get 请求将 http 请求升级为 websocket 请求
  2. 数据通讯
  3. 断开链接(可由任一方中断)

websocket 连接过程

握手协议

在第一步的时候进行了握手,这里来看一下请求的详情。

客户端使用 http 请求升级切换到 Websocket 协议

1
2
3
4
5
6
7
8
9
// 客户端请求头:wss://xxx.com/socket.io/?EIO=3&transport=websocket
GET / HTTP/1.1
Connection: Upgrade // 指示这是一个升级请求
Upgrade: websocket // 指定一项或多项协议名,按优先级排序,以逗号分隔。如升级为websocket协议
Host: localhost:3000 // 请求host
Origin: http://binnie.com // 请求来源
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits // 用于指定一个或多个请求服务器使用的协议级WebSocket扩展。
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ== // 向服务器提供所需的信息,以确认客户端有权请求升级到 WebSocket。此请求升级的密钥。如果客户端愿意,它会添加它,并且服务器将在响应中包含它自己的密钥,客户端将在向您发送升级响应之前对其进行验证。【服务器响应的Sec-WebSocket-Accept标头将具有基于指定的计算的值key】
Sec-WebSocket-Version: 13 // 指定客户端希望使用的 WebSocket 协议版本,以便服务器可以确认其端是否支持该版本。[目前 WebSocket 协议的最新最终版本是第 13]
1
2
3
4
5
// 服务端响应头
HTTP/1.1 101 Switching Protocols // status_code=101,表示握手成功,websocket连接已经建立完成。
Upgrade: websocket // 与请求对应
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s= // 使用 Sec-WebSocket-Key 计算得来,为了尽量避免普通HTTP请求被误认为Websocket协议

帧格式

WebSocket协议使用帧(Frame)收发数据【帧,听不懂?就是比特位!一位二进制数就是一比特位。值只有0和1,一字节是8比特位】

客户端发送给服务端的帧必须通过4字节的掩码(Masking-key)加密,服务端收到消息后,用掩码对数据帧的Payload Data进行异或运算解码得到数据,如果服务端收到未经掩码加密的数据帧,则应该马上关闭该WebSocket。

服务端发给客户端的数据则不需要掩码加密,客户端如果收到了服务端的掩码加密的数据,则也必须关闭它。

帧

上图为websocket的帧格式,下面我们一个个来分析 这里从左往右看,横向为32位(bit)

  • FIN:最后的片段
  • RSV1-3:拓展定义
  • opcode:帧类型,其中控制帧:0x8 (Close), 0x9 (Ping), and 0xA (Pong),数据帧主要有:0x1 (Text), 0x2 (Binary)
  • MASK:数据是否掩码
  • Payload len:负载长度
  • Extended payload length:拓展负载长度
  • Masking-key:掩码
  • Playload Data:负载数据

所以,websocket传递数据时最小的请求头为 FIN+RSV1-3+opcode+MASK+Payload len+Masking-key = 6 byte(字节),剩下的就都是实际数据,相比于http头部小得多。

websocket前后端通信的时候,丢包怎么处理,怎么监控

todo: 待研究

  • 集群部署聊天(实现横向可扩展)
  • 采用消息确认机制,确认消息稳定的送达

socket.io

点击前往查看源码示例

go-socket.io 踩坑记录

概览

socket.io 本身是一个 Node.js 的包,使用 long polling 或者 websocket 的方式提供持续的网络连接服务。由于简单好用,很多开发者为其开发了不同语言的 SDK,比如 Java、C++、Go 等。

go-socket.io Golang版后端 socket.io 的实现框架。

socket.io 优势

socket.io 会自动选择合适双向通信协议,仅仅需要程序员对套接字的概念有所了解。

socket.io 劣势

socket.io并不是一个基本的、独立的、能够回退到其它实时协议的WebSocket库,它实际上是一个依赖于其它实时传输协议的自定义实时传输协议的实现。该协议的协商部分使得支持标准WebSocket的客户端不能直接连接到Socket.io服务器,并且支持Socket.io的客户端也不能与非Socket.io框架的WebSocket或Comet服务器通信。因而,Socket.io要求客户端与服务器端均须使用该框架。

github.com/googollee/go-socket.io

WebSocket第三方框架,基于开源项目 github.com/gorilla/websocket 开发的。客户端代码只能用 socket.io 框架,这样就会存在 go-socket.io 与 socket.io 的兼容性问题。

github.com/googollee/go-socket.io@v1.6.1 内存泄漏问题:

  • 原因:由于v1.6.1版本的session未回收,断连socket conn存在内存中,导致内存不断上升。
  • 解决:在v1.6.2版本修复,socket连接断开回收该链接。Session. Remove(sid)
  • 结果:目前系统socketio已经稳定1w连接/1G内存

概念

  • namespace 和 room 是同一个服务端socket多路复用的体现。

    namespace、room 和 socket 的关系为:socket会属于某一个room,如果没有指定,那么会有一个default的room。这个room又会属于某个namespace,如果没有指定,那么就是默认的namespace /.

  • Adapter:适配器是一个服务器端的组件,负责将事件广播给所有或一个子集的客户端。

Socket 与 WebSocket 的区别

本质上来讲,两者并没有什么关联,只是名称类似、作用类似而已!【两者都能做即时通讯】

  1. Socket 是位于应用层和传输控制层之间的一组接口,用户可以通过 Socket 来操作底层 TCP/IP 协议族通信。而 WebSocket 是一个完整的应用层协议。(相比WebSocket,Socket更底层)
  2. Socket 使用 TCP 协议,WebSocket 使用 WebSocket 协议。
  3. 建立持久性连接时,Socket 需要三次握手,WebSocket 只需要一次握手。
  4. WebSocket需要处理跨域问题,Socket则不需要。

WebSocket 与 HTTP 的关系

相同点 1. 都是一样基于TCP的,都是可靠性传输协议。 2. 都是应用层协议。 不同点 1. WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。HTTP是单向的。 2. WebSocket是需要握手进行建立连接的。 联系 WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的。

参考文章