诚意
诚意如你,当一诚的态度对待

导航

 

http协议是一种单向的网络协议,在建立连接后,它只允许Browser/UA(UserAgent)向WebServer发出请求资源后,WebServer才能返回相应的数据。而WebServer不能主动的推送数据给Browser/UA

 websocket是实现网络的全双工通信,一次链接,实时通讯,节省带宽

 

引言:

  http1.1版本以后支持持久连接:Connection:keep-alive;虽然说websocket也支持持久化连接,那么我们为什么还使用websocket呢?

  -------》websocket:服务器可主动向客户端推送数据

  -----》应用场景:需要服务器主动向客户端推送数据的地方  

 

 

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)

加密算法脑瓜疼

 

 

 


 

实验一:实现服务端和客户端的通信

服务端

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() #启动

 

 

客户端

<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>

 

 

测试

 

实验二:实现群聊

服务器

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  (不错)

 

posted on 2019-01-10 19:35  诚意  阅读(212)  评论(0编辑  收藏  举报