websocket

http協議是一種單向的網絡協議,在創建鏈接後,它只容許Browser/UA(UserAgent)向WebServer發出請求資源後,WebServer才能返回相應的數據。而WebServer不能主動的推送數據給Browser/UAjavascript

 websocket是實現網絡的全雙工通訊,一次連接,實時通信,節省帶寬html

 

引言:java

  http1.1版本之後支持持久鏈接:Connection:keep-alive;雖說websocket也支持持久化鏈接,那麼咱們爲何還使用websocket呢?web

  -------》websocket:服務器可主動向客戶端推送數據算法

  -----》應用場景:須要服務器主動向客戶端推送數據的地方  json

 

 

var ws = new WebSockdet(ws://127.0.0.1:9527/);
ws.onopen = function(){   #創建ws鏈接後作。。。事情 } ws.onmessage = function(){ } ws.onclose = function(){ } ws.onerror = function(){   #ws鏈接錯誤作。。。事情
  window.location.reload();//刷新頁面
}

 

一:websocket握手-ws鏈接請求創建

import socket, base64, hashlib

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 9527))
sock.listen(5)
# 獲取客戶端socket對象
conn, address = sock.accept()
# 獲取客戶端的【握手】信息
data = conn.recv(1024)
print(data)#ws鏈接請求
"""
GET /ws HTTP/1.1\r\n
Host: 127.0.0.1:9527\r\n
Connection: Upgrade\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36\r\n
Upgrade: websocket\r\n
Origin: http://localhost:63342\r\n
Sec-WebSocket-Version: 13\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n
Cookie: session=a6f96c20-c59e-4f33-84d9-c664a2f29dfc\r\n
Sec-WebSocket-Key: MAZZU5DPIxWmhk/UWL2+BA==\r\n
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n
"""
# 如下動做是有websockethandler完成的
# magic string爲:258EAFA5-E914-47DA-95CA-C5AB0DC85B11


#獲取Sec-WebSocket-Key
def get_headers(data):  
    header_dict = {}
    header_str = data.decode("utf8")
    for i in header_str.split("\r\n"):
        if str(i).startswith("Sec-WebSocket-Key"):
            header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip()

    return header_dict


headers = get_headers(data)  # 提取請求頭信息
#
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
#Sec-WebSocket-Key: MAZZU5DPIxWmhk/UWL2+BA==
value = headers['Sec-WebSocket-Key'] + magic_string   #鑰匙+鎖
print(value)
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())   #保險櫃被打開,在確認保險櫃那麼ws鏈接就創建了

# 對請求頭中的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://127.0.0.1:9527\r\n\r\n"
print(ac.decode('utf-8'))
response_str = response_tpl % (ac.decode('utf-8'))
# 響應【握手】信息
conn.send(response_str.encode("utf8"))
#
while True:
    msg = conn.recv(8096)
    print(msg)

 

二:websocket創建鏈接後服務端接受到消息對消息進行解密

# b'\x81\x83\xceH\xb6\x85\xffz\x85'

hashstr = b'\x81\x83\xceH\xb6\x85\xffz\x85'
# b'\x81    \x83    \xceH\xb6\x85\xffz\x85'

# 將第二個字節也就是 \x83 第9-16位 進行與127進行位運算
payload = hashstr[1] & 127
print(payload)
if payload == 127:
    extend_payload_len = hashstr[2:10]
    mask = hashstr[10:14]
    decoded = hashstr[14:]
# 當位運算結果等於127時,則第3-10個字節爲數據長度
# 第11-14字節爲mask 解密所需字符串
# 則數據爲第15字節至結尾

if payload == 126:
    extend_payload_len = hashstr[2:4]
    mask = hashstr[4:8]
    decoded = hashstr[8:]
# 當位運算結果等於126時,則第3-4個字節爲數據長度
# 第5-8字節爲mask 解密所需字符串
# 則數據爲第9字節至結尾


if payload <= 125:
    extend_payload_len = None
    mask = hashstr[2:6]
    decoded = hashstr[6:]

# 當位運算結果小於等於125時,則這個數字就是數據的長度
# 第3-6字節爲mask 解密所需字符串
# 則數據爲第7字節至結尾

str_byte = bytearray()

for i in range(len(decoded)):
    byte = decoded[i] ^ mask[i % 4]
    str_byte.append(byte)

print(str_byte.decode("utf8"))

腦瓜疼腦瓜疼
View Code

 

三:加密

import struct
msg_bytes = "hello".encode("utf8")
token = b"\x81"
length = len(msg_bytes)

if length < 126:
    token += struct.pack("B", length)
elif length == 126:
    token += struct.pack("!BH", 126, length)
else:
    token += struct.pack("!BQ", 127, length)

msg = token + msg_bytes

print(msg)

加密算法腦瓜疼

 

 

 


 

實驗一:實現服務端和客戶端的通訊

服務端flask

from flask import  Flask,render_template,request
from geventwebsocket.handler  import WebSocketHandler
from geventwebsocket.websocket  import WebSocket
from gevent.pywsgi import WSGIServer


app = Flask(__name__)


@app.route('/index')
def index():
    # request.headers #注意:Sec-Websocket-Key:  Sec-Websocket-Extensions
    user_socket = request.environ.get('wsgi.websocket')#獲取websocket鏈接,就是經過這個給客戶端發消息
    while 1:
        msg = user_socket.receive()
        user_socket.send(''+msg)


if __name__ == '__main__':
    # app.run('0.0.0.0',9527,debug=True)
    http_serv = WSGIServer(('0.0.0.0',9527),app,handler_class=WebSocketHandler)#遇到請求時交給app處理,遇到websocket請求時交給WebSocketHandler處理
    http_serv.serve_forever() #啓動

 

 

客戶端bash

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <script type="application/javascript">
        var ws = new WebSocket('ws://192.168.13.142:9527/index')

        //接收到服務端的消息
        ws.onmessage = function (data) {
            console.log(data.data);
        }
    </script>
</body>
</html>

 

 

測試服務器

 

實驗二:實現羣聊

服務器websocket

from flask import  Flask,render_template,request
from geventwebsocket.handler  import WebSocketHandler
from geventwebsocket.websocket  import WebSocket
from gevent.pywsgi import WSGIServer


app = Flask(__name__)

user_socket_list = []  #存放獲取鏈接的客戶端的websocket

@app.route('/index')
def index():
    # request.headers #注意:Sec-Websocket-Key:  Sec-Websocket-Extensions
    user_socket = request.environ.get('wsgi.websocket')#獲取websocket鏈接,就是經過這個給客戶端發消息
    if user_socket:
        user_socket_list.append(user_socket)
        print(user_socket_list)
    while 1:
        msg = user_socket.receive()
        for u_socket in user_socket_list:
            if u_socket == user_socket: #A客戶端發的,服務器就不給A發送A本身的消息了
                continue
            try:#若是鏈接會話死掉,跳過這個鏈接就能夠了
                u_socket.send(msg)
            except:
                continue

@app.route('/')
def ws():
    return render_template('index.html')


if __name__ == '__main__':
    # app.run('0.0.0.0',9527,debug=True)
    http_serv = WSGIServer(('0.0.0.0',9527),app,handler_class=WebSocketHandler)#遇到請求時交給app處理,遇到websocket請求時交給WebSocketHandler處理
    http_serv.serve_forever() #啓動

 

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>
        <input type="text"  id="msg">
        <button onclick="send_msg()">發送</button>
        <div id='chat_list' style="color: red;width: 500px;height: 500px"></div>

    </div>

    <script type="application/javascript">
        var ws = new WebSocket('ws://192.168.13.142:9527/index')

        //接收到服務端的消息,放入客戶端的顯示信息框中
        ws.onmessage = function (data) {
            console.log(data.data);
            var ptage = document.createElement('p');
            ptage.innerText = data.data;
            document.getElementById('chat_list').appendChild(ptage);
        };

        //點擊發送觸發的事件
        function send_msg() {
            var msg = document.getElementById('msg').value;
            ws.send(msg);
        }
    </script>
</body>
</html>
index.html

 

測試:

 

客戶端訪問這個路徑就能夠羣聊了,羣聊顯示在下方框中

實驗三:實現單聊

服務器

import json
from flask import  Flask,render_template,request
from geventwebsocket.handler  import WebSocketHandler
from geventwebsocket.websocket  import WebSocket
from gevent.pywsgi import WSGIServer


app = Flask(__name__)

user_socket_list = []  #存放獲取鏈接的客戶端的websocket
user_socket_dict = {}  #存放單聊的客戶端

@app.route('/index/<username>')
def index(username):
    # request.headers #注意:Sec-Websocket-Key:  Sec-Websocket-Extensions
    user_socket = request.environ.get('wsgi.websocket')#獲取websocket鏈接,就是經過這個給客戶端發消息
    if user_socket:
        # user_socket_list.append(user_socket)
        user_socket_dict[username]=user_socket
        print(len(user_socket_dict),user_socket_dict)
    while 1:
        msg = user_socket.receive() #收件人  消息  發件人
        msg_dict = json.loads(msg)
        print(msg_dict)
        msg_dict['from_user']=username
        to_user = msg_dict.get('to_user')
        # chat = msg_dict.get('msg')
        u_socket = user_socket_dict.get(to_user)#獲取須要發送的那個客戶端的websocket鏈接
        u_socket.send(json.dumps(msg_dict))

@app.route('/')
def ws():
    return render_template('index.html')


if __name__ == '__main__':
    # app.run('0.0.0.0',9527,debug=True)
    http_serv = WSGIServer(('0.0.0.0',9527),app,handler_class=WebSocketHandler)#遇到請求時交給app處理,遇到websocket請求時交給WebSocketHandler處理
    http_serv.serve_forever() #啓動

 

 

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>
        <input type="text"  id="username"><button onclick="login()">登陸聊天室</button><input type="text"  id="to_user">發送
        <input type="text"  id="msg"><button onclick="send_msg()">發送</button>
        <div id='chat_list' style="color: red;width: 500px;height: 500px"></div>
    </div>

    <script type="application/javascript">
        var ws = null;

        //點擊發送觸發的事件
        function send_msg() {
            var msg = document.getElementById('msg').value;
            var to_user = document.getElementById('to_user').value;
            var send_str = {
                'to_user':to_user,
                'msg':msg
            };
            ws.send(JSON.stringify(send_str));
         }

        //點擊登陸聊天室
        function login() {
            var username = document.getElementById('username').value;
            ws = new WebSocket('ws://192.168.13.142:9527/index/'+username);

            //接收到服務端的消息,放入客戶端的顯示信息框中
            ws.onmessage = function (data) {
                console.log(data.data);
                recv_msg = JSON.parse(data.data);
                ptag = document.createElement("p");
                ptag.innerText= recv_msg.from_user + ":" + recv_msg.msg;
                document.getElementById("chat_list").appendChild(ptag);
            };

         }

    </script>
</body>
</html>

測試

 

 


參考資料

阮一峯

https://www.cnblogs.com/jingmoxukong/p/7755643.html  (不錯)

相關文章
相關標籤/搜索