系统城装机大师 - 固镇县祥瑞电脑科技销售部宣传站!

当前位置:首页 > 网页制作 > html5 > 详细页面

html5 http的轮询和Websocket原理

时间:2019-12-05来源:系统城作者:电脑系统城

一、HTTP的轮询

Web客户端与服务器之间基于Ajax(http)的常用通信方式,分为 短连接 与 长轮询 。

短连接:客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。

长轮询:客户端像传统轮询一样从服务器请求数据。然而,如果服务器没有可以立即返回给客户端的数据,则不会立刻返回一个空结果,而是保持这个请求等待数据到来(或者恰当的超时:小于ajax的超时时间),之后将数据作为结果返回给客户端。

长轮询机制如下图所示:

二、Websocket基本概念

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

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

在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

三、Websocket 握手原理:

Websocket的握手原理大致可分为以下步骤:

  • 第一步:客户端发起HTTP请求连接
  • 第二步:服务端从请求头中取出Sec-WebSocket-Key的值
  • 第三步:给Sec-WebSocket-Key值 拼接一个magic_string 的到一个新的value
  • 第四步:给新的value先做 sha1加密 再做 base64加密
  • 第五步:拼接一个响应头
  • 第六步:服务器将拼好的响应头发送给客户端
  • 第七步:客户端解密Sec-WebSocket-Accept得到Sec-WebSocket-Key判断是否握手成功

代码实现:


 
  1. import socket, base64, hashlib
  2.  
  3. # 创建socket连接
  4. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  5. # 绑定端地址和口号
  6. sock.bind(('127.0.0.1', 9527))
  7. # 监听
  8. sock.listen(5)
  9. # 获取客户端socket对象
  10. conn, address = sock.accept()
  11. # 获取客户端的【握手】信息
  12. data = conn.recv(1024)
  13. print(data)
  14.  
  15. def get_headers(data):
  16. """从请求头中取出Sec-WebSocket-Key对应的值并返回"""
  17. header_dict = {}
  18. header_str = data.decode("utf8")
  19. for i in header_str.split("\r\n"):
  20. if str(i).startswith("Sec-WebSocket-Key"):
  21. return i.split(":")[1].strip()
  22.  
  23. # 得到Sec-WebSocket-Key对应的值
  24. ws_key = get_headers(data)
  25.  
  26. # 魔法字符串magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11
  27. magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
  28. # 拼接
  29. socket_str = ws_key + magic_string
  30. # sha1加密
  31. socket_str_sha1 = hashlib.sha1(socket_str.encode("utf8")).digest()
  32. # base64加密
  33. socket_str_base64 = base64.b64encode(socket_str_sha1)
  34. # 拼接响应头
  35. response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
  36. "Upgrade:websocket\r\n" \
  37. "Connection: Upgrade\r\n" \
  38. "Sec-WebSocket-Accept: %s\r\n" \
  39. "WebSocket-Location: ws://127.0.0.1:9527\r\n\r\n" % (socket_str_base64.decode("utf8"))
  40. # 服务器发送响应头到客户端
  41. conn.send(response_tpl.encode("utf8"))
  42. # 客户端服务端建立长连接循环接收发送数据
  43. while True:
  44. msg = conn.recv(8096)
  45. print(msg)
 

 
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8.  
  9. </body>
  10. <script type="text/javascript">
  11. ws = new WebSocket("ws://127.0.0.1:9527");
  12. ws.onmessage = function (ev) {
  13. console.log(ev)//用于接收数据
  14. }
  15. </script>
  16. </html>

附带客户端发起HTTP请求的请求头:


 
  1. b'GET /ws/ HTTP/1.1
  2. Host: 127.0.0.1:9527
  3. Connection: Upgrade
  4. Pragma: no-cache
  5. Cache-Control: no-cache
  6. User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.3...
  7. Upgrade: websocket
  8. Origin: http://localhost:63342
  9. Sec-WebSocket-Version: 13
  10. Accept-Encoding: gzip, deflate, br
  11. Accept-Language: zh-CN,zh;q=0.9
  12. Sec-WebSocket-Key: kJXuOKsrl3AR1KeFngRElQ==
  13. Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits'

四、Websocket的加解密方式:

解密方式:


 
  1. # b'\x81\x87\x0e\xc3\xf3\xcd;\xf6\xc6\xf8;\xf6\xc6'==========5555555
  2.  
  3. hashstr = b'\x81\x87\x0e\xc3\xf3\xcd;\xf6\xc6\xf8;\xf6\xc6'
  4.  
  5. # 将第二个字节也就是 \x87 第9-16位 进行与127进行位运算
  6. payload = hashstr[1] & 127
  7.  
  8. # 当位运算结果等于127时,则第3-10个字节为数据长度
  9. # 第11-14字节为mask 解密所需字符串
  10. # 则数据为第15字节至结尾
  11. if payload == 127:
  12. extend_payload_len = hashstr[2:10]
  13. mask = hashstr[10:14]
  14. decoded = hashstr[14:]
  15.  
  16. # 当位运算结果等于126时,则第3-4个字节为数据长度
  17. # 第5-8字节为mask 解密所需字符串
  18. # 则数据为第9字节至结尾
  19. if payload == 126:
  20. extend_payload_len = hashstr[2:4]
  21. mask = hashstr[4:8]
  22. decoded = hashstr[8:]
  23.  
  24. # 当位运算结果小于等于125时,则这个数字就是数据的长度
  25. # 第3-6字节为mask 解密所需字符串
  26. # 则数据为第7字节至结尾
  27. if payload <= 125:
  28. extend_payload_len = None
  29. mask = hashstr[2:6]
  30. decoded = hashstr[6:]
  31.  
  32. str_byte = bytearray()
  33.  
  34. for i in range(len(decoded)):
  35. byte = decoded[i] ^ mask[i % 4]
  36. str_byte.append(byte)
  37.  
  38. print(str_byte.decode("utf8"))

加密方式:


 
  1. import struct
  2. msg_bytes = "5555555".encode("utf8")
  3. token = b"\x81"
  4. length = len(msg_bytes)
  5.  
  6. if length < 126:
  7. token += struct.pack("B", length)
  8. elif length == 126:
  9. token += struct.pack("!BH", 126, length)
  10. else:
  11. token += struct.pack("!BQ", 127, length)
  12.  
  13. msg = token + msg_bytes
  14.  
  15. print(msg)

四、基于flask框架、Websocket协议实现的客户端和服务端链接通信示例:

pip3 install gevent-websocket


 
  1. from flask import Flask, request
  2. from geventwebsocket.websocket import WebSocket
  3. from gevent.pywsgi import WSGIServer
  4. from geventwebsocket.handler import WebSocketHandler
  5.  
  6. app = Flask(__name__)
  7.  
  8.  
  9. @app.route("/ws")
  10. def websocket():
  11. # 得到用户的链接
  12. user_socket = request.environ.get("wsgi.websocket") # type:WebSocket
  13. print("访问成功")
  14. while True:
  15. msg = user_socket.receive() # 接受消息
  16. print(msg)
  17. user_socket.send(msg) # 发送消息
  18.  
  19. if __name__ == '__main__':
  20. # 指定地址、端口号开启Websocket服务
  21. http_serv = WSGIServer(("127.0.0.1", 8001), app, handler_class=WebSocketHandler)
  22. # 启动Websocket服务
  23. http_serv.serve_forever()

html文件:


 
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css"rel="stylesheet">
  7. </head>
  8. <body>
  9.  
  10. <botton class="btn btn-default" onclick="createsocket()">点击创建链接</botton>
  11. <br>
  12.  
  13. <p>请您输入消息:<input type="text" placeholder="输入消息" id="msg"></p>
  14. <buttom class="btn btn-success" onclick="send_msg()">发送消息</buttom>
  15.  
  16. <script>
  17. var ws = null;
  18. function createsocket() {
  19. ws = new WebSocket("ws://127.0.0.1:8001/ws");
  20. ws.onmessage = function (data) {
  21. console.log("从服务端收到的消息=",data.data);
  22. }
  23. }
  24.  
  25. function send_msg() {
  26. var to_msg = document.getElementById("msg").value;
  27. ws.send(to_msg)
  28. }
  29. </script>
  30. </body>
  31. </html>
  • 第一步:运行flask
  • 第二步:运行html文件
  • 第三步:点击创建链接
  • 第四步:输入消息
  • 第五步:点击发送消息

 

客户端.png

服务器端.png

这样我们就简单实现了通过Websocket协议的客户端服务端通信。并且我们可以创建多个链接同时对服务器端通信。

五、基于Websocket实现即时通讯(IM):

服务器代码:


 
  1. from flask import Flask, request
  2. from geventwebsocket.websocket import WebSocket
  3. from gevent.pywsgi import WSGIServer
  4. from geventwebsocket.handler import WebSocketHandler
  5. from geventwebsocket.exceptions import WebSocketError
  6. import json
  7.  
  8. app = Flask(__name__)
  9.  
  10. user_socket_dict = {}
  11. @app.route("/ws/<username>")
  12. def websocket(username):
  13.  
  14. # 得到用户的链接
  15. user_socket = request.environ.get("wsgi.websocket") # type:WebSocket
  16. user_socket_dict[username] = user_socket
  17. print(username+"链接成功!")
  18. while True:
  19. msg = user_socket.receive() # 接受消息
  20. for socket in user_socket_dict.values(): # type:WebSocket
  21. if user_socket != socket:# 自己发消息服务器就不要再给自己回消息了
  22. try:
  23. socket.send(json.dumps({"sender": username, "msg": msg}))
  24. except:
  25. continue
  26.  
  27. if __name__ == '__main__':
  28. # 指定地址、端口号开启Websocket服务
  29. http_serv = WSGIServer(("127.0.0.1", 8001), app, handler_class=WebSocketHandler)
  30. # 启动Websocket服务
  31. http_serv.serve_forever()

html代码:


 
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css"rel="stylesheet">
  7. </head>
  8. <body>
  9. <p>请输入你的昵称:<input type="text" id="username"></p>
  10. <botton class="btn btn-default" onclick="createsocket()">点击创建链接</botton>
  11. <br>
  12. <p>请您输入消息:<input type="text" id="msg"></p>
  13. <buttom class="btn btn-success" onclick="send_msg()">发送消息</buttom>
  14. <br>
  15. <br>
  16. <br>
  17. <div style="border: 2px solid; width: 500px;height: 800px;" id="text_div">
  18. </div>
  19.  
  20. <script>
  21. var ws = null;
  22. var username = null;
  23.  
  24. function createsocket() {
  25. username = document.getElementById("username").value;
  26. ws = new WebSocket("ws://127.0.0.1:8001/ws" + "/" + username);
  27. ws.onmessage = function (data) {
  28. var text_div = document.getElementById("text_div");
  29. var obj_data = JSON.parse(data.data);
  30. var add_msg = "<p>" + obj_data.sender + ":" + obj_data.msg + "</p>";
  31. text_div.innerHTML += add_msg;
  32. }
  33. }
  34.  
  35. function send_msg() {
  36. var to_msg = document.getElementById("msg").value;
  37. var text_div = document.getElementById("text_div");
  38. var add_msg = "<p style='text-align: right'>" + to_msg + ":" + username + "</p>";
  39. text_div.innerHTML += add_msg;
  40. ws.send(to_msg);
  41. }
  42. </script>
  43. </body>
  44. </html>
  • 第一步:运行flask服务器
  • 第二步:运行html文件
  • 第三步:输入昵称,点击创建链接
  • 第四步:输入消息
  • 第五步:点击发送消息

客户端01.png

客户端02.png

服务器端.png

代码是演示代码,有bug有bug,目前主要是用于学习,不可吹毛求疵。有兴趣的可以进一步优化!!!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

分享到:

相关信息

系统教程栏目

栏目热门教程

人气教程排行

站长推荐

热门系统下载