搞懂WebSocket原理

Posted rayh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了搞懂WebSocket原理相关的知识,希望对你有一定的参考价值。

一、websocket与http

WebSocket是html5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)

首先HTTP有 1.1 和 1.0 之说,也就是所谓的 keep-alive ,把多个HTTP请求合并为一个,但是 Websocket 其实是一个新协议,跟HTTP协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是HTTP协议上的一种补充可以通过这样一张图理解

有交集,但是并不是全部。另外Html5是指的一系列新的API,或者说新规范,新技术。Http协议本身只有1.0和1.1,而且跟Html本身没有直接关系。。
通俗来说,你可以用HTTP协议传输非Html数据,就是这样。再简单来说,层级不一样。

1. 启动服务端

import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((\'127.0.0.1\', 8002))
sock.listen(5)
# 等待用户连接
conn, address = sock.accept()
...
...
...

启动Socket服务器后,等待用户【连接】,然后进行收发数据。

2. 客户端连接

<script type="text/javascript">
    var socket = new WebSocket("ws://127.0.0.1:8002/xxoo");
    ...
</script>

当客户端向服务端发送连接请求时,不仅连接还会发送【握手】信息,并等待服务端响应,至此连接才创建成功!

3. 建立握手

import socket
 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((\'127.0.0.1\', 8002))
sock.listen(5)
# 获取客户端socket对象
conn, address = sock.accept()
# 获取客户端的【握手】信息
data = conn.recv(1024)
...
...
...
conn.send(\'响应【握手】信息\')

请求和响应的【握手】信息需要遵循规则:

  • 从请求【握手】信息中提取 Sec-WebSocket-Key
  • 利用magic_string 和 Sec-WebSocket-Key 进行hmac1加密,再进行base64加密
  • 将加密结果响应给客户端

注:magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11

首先我们来看个典型的 Websocket 握手信息

GET /chatsocket HTTP/1.1
Host: 127.0.0.1:8002
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:63342
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
...
...

熟悉HTTP的童鞋可能发现了,这段类似HTTP协议的握手请求中,多了几个东西。我会顺便讲解下作用。

Upgrade: websocket
Connection: Upgrade

这个就是Websocket的核心了,告诉 Apache 、 nginx 等服务器:注意啦,我发起的是Websocket协议,快点帮我找到对应的助理处理。

Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

首先, Sec-WebSocket-Key 是一个 Base64 encode 的值,这个是浏览器随机生成的,告诉服务器:泥煤,不要忽悠窝,我要验证尼是不是真的是Websocket助理。

然后, Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议。简单理解:今晚我要服务A,别搞错啦~

最后, Sec-WebSocket-Version 是告诉服务器所使用的 Websocket Draft (协议版本)。

***握手需要提取Sec-WebSocket-Key值并加密:

import socket
import base64
import hashlib
 
def get_headers(data):
    """
    将请求头格式化成字典
    :param data:
    :return:
    """
    header_dict = {}
    data = str(data, encoding=\'utf-8\')
 
    for i in data.split(\'\\r\\n\'):
        print(i)
    header, body = data.split(\'\\r\\n\\r\\n\', 1)
    header_list = header.split(\'\\r\\n\')
    for i in range(0, len(header_list)):
        if i == 0:
            if len(header_list[i].split(\' \')) == 3:
                header_dict[\'method\'], header_dict[\'url\'], header_dict[\'protocol\'] = header_list[i].split(\' \')
        else:
            k, v = header_list[i].split(\':\', 1)
            header_dict[k] = v.strip()
    return header_dict
 
 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((\'127.0.0.1\', 8002))
sock.listen(5)
 
conn, address = sock.accept()
data = conn.recv(1024)
headers = get_headers(data) # 提取请求头信息
# 对请求头中的sec-websocket-key进行加密
response_tpl = "HTTP/1.1 101 Switching Protocols\\r\\n" \\
      "Upgrade:websocket\\r\\n" \\
      "Connection: Upgrade\\r\\n" \\
      "Sec-WebSocket-Accept: %s\\r\\n" \\
      "WebSocket-Location: ws://%s%s\\r\\n\\r\\n"
magic_string = \'258EAFA5-E914-47DA-95CA-C5AB0DC85B11\'
value = headers[\'Sec-WebSocket-Key\'] + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode(\'utf-8\')).digest())
response_str = response_tpl % (ac.decode(\'utf-8\'), headers[\'Host\'], headers[\'url\'])
# 响应【握手】信息
conn.send(bytes(response_str, encoding=\'utf-8\'))
...
...
...
建立握手

 

以上是关于搞懂WebSocket原理的主要内容,如果未能解决你的问题,请参考以下文章

看完搞懂Websocket原理

一文让你彻底搞懂 WebSocket 的原理

看完让你彻底搞懂Websocket原理

看完让你彻底搞懂Websocket原理

看完让你彻底搞懂Websocket原理

看完让你彻底搞懂Websocket原理