安装gevent-websocket

  pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ gevent-websocket

 

websocket 工作原理

websocket 握手原理:

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


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

# 对请求头中的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_hand.py
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

</body>
<script type="application/javascript">
  var ws = new WebSocket("ws://127.0.0.1:9527/ws");

</script>
</html>
ws.html

websocket 加密:

import struct
msg_bytes = "the emperor has not been half-baked in the early days of the collapse of the road, today down three points, yizhou weakness, this serious crisis autumn".encode("utf8")
token = b"\x81" # + 数据长度/运算位 + mask/数据长度 + mask/数据 + 数据
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)
websocket加密

websocket 解密:

#b'\x81\x89\xf3\x99\x81-\x15\x05\x01\xcbO\x1be\x97]'
#b'\x81\x85s\x92a\x10\x1b\xf7\r|\x1c'
#b'\x81\x83H\xc0x\xa6y\xf2K'

hashstr = b'\x81\x85s\x92a\x10\x1b\xf7\r|\x1c'
# b'\x81 \x85s \x92a\x10\x1b\xf7  \r|\x1c' <126
# \x85s = 5
hashstr = b'\x81\xfe\x02\xdc\x8d\xe8-\xb2hm\xa5W5u\xc8:\x16\x0c\x95(kt\x87W\x00b\xc52\x01\x0c\x95\x1fdi\xbeW9A\xcb\x1c\x0f\x07\x91>iS\xa7W)A\xc9\n\x06\x0c\x95;h`\xab]1d\xca)\x07\r\x9a,j~\x9fW1b\xc2\x0e\x01\x0e\x80\x16eG\xb7W\x00Y\xcb2(\r\x80*iR\x8cV4c\xca\x15\x06\x0c\x94-nh\xafU\t^\xc9\x0c\x00\r\xa0\x19iQ\xa6Z\nK\xc9\n\x00\x0e\xaa:iR\xa3W\x0bm\xc2\x0e\x01\r\x92\x12hW\xbaV4c\xc8\x11&\r\x92*eR\x86V7f\xc8\x16\x1b\x00\xad7bT\xa1U\x16~\xc5\r0\r\xa8:hP\xb0V4c\xcb\x1c\x07\x01\xac5bT\xa1T!Z\xcb8(\x0c\x949iR\xa3[\x14s\xc9\n\x06\x0c\x94-nh\xafZ"r\xc8\x1c\x11\r\x912hT\x8dW\x11K\xc8"!\x07\x91>iS\x88W\x08a\xc87\x05\r\x95/di\xbaW3_\xc2\x0e\x01\x0e\xac\x10hT\xb5W2\x7f\xc8\x11&\x0c\x949kX\xb9]1d\xc9\n\x00\r\x83.hN\xa9Z\nB\xc5=?\x00\xbb6bT\xa1W1}\xc8$6\r\x89\x03iQ\xa4]1d\xc9\t(\r\x8c,hW\x8dZ=g\xc9\x0b\x06\x00\x9a\x1diQ\xb2Q\rj\xc8\x1c&\x0c\x95\x1fhR\xb1V5E\xc2\x0e\x01\x0c\x92\x03iP\x97V5h\xc9\x0f\x1e\x07\x91>dq\xb2U0r\xc55*\r\xbd\x14bT\xa1V5e\xc8\x1c\x11\r\x910hx\xa1Q\rj\xc59(\x0e\xb1;iU\xb1W(P\xca8"\x0f\x8a#hg\xa7V5R\xc8\r-\r\xbb6eh\xa8]1d\xc8\x1c\x11\x0c\x96*kt\xa4W\x02P\xc5\x1c7\r\xa8\x04h`\xbcZ8g\xc2\x0e\x01\x0c\x96\x17kp\x80[\x14s\xc9\n\x06\r\x94\x01kp\xa3V4c\xca"\x0b\x07\x91>iP\xa0W#t\xc83\x02\x0f\x8a3bT\xa1V0W\xc84\x08\r\x89$hT\xafT>}\xc9\x0b\x12\x0b\xad0iV\xa0V5E\xce2\x0c\x0c\x93?dk\xa3[\x0eE\xcb&5\x0c\x949nh\xacZ9Q\xca\x17\x03\x0b\xad3ey\x8eW\x08i\xca\x1f\x04\x07\x91>kE\x89U\x17n\xc5;"\r\x83,bT\xa1W2\x7f\xc5+\x1c\r\x92\x12jR\x82]1d\xcb*"\x0c\x96\x17hm\xa5W5u\xca\x1c\r\x0e\xa6&iS\x88[\x0c\x7f\xc4+\x16\x0c\x959nh\xafT\tr\xc9\t(\x0c\x95\x08hF\x86V5E\xc9\x0b\x06\x0c\x979bT\xa1V7c\xcb%-\r\x89\x15hX\xa2]1d\xcb0\x04\x0c\x96\x17hz\x85V4c\xc2\x0e\x01\x0f\xa9\x04hx\xa3T\x1bU\xc5\x13\x01\x07\x91>hW\xa8Z\x0eU\xc5\x11%\x00\x8c\x17dp\xb4T1g\xc2\x0e\x01\x0e\xb1;ka\xadW4W\xca)\x07\x0b\xad0'
# print(chushibiao[1],chushibiao[1]&127)
# print(chushibiao[2:4],chushibiao[4:8])
# 将第二个字节也就是 \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()
# b'\x81 \x85s \x92a\x10\x1b \xf7\r|\x1c' <126
for i in range(len(decoded)): # 0  \xf7 ^ \x92a 1 \r ^ \x10 \x1c ^ \x1b
    byte = decoded[i] ^ mask[i % 4]
    str_byte.append(byte)

print(str_byte.decode("utf8"))
websocket 解密

 

自己写一个websocket

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)
"""
b'GET /ws HTTP/1.1\r\n
Host: 127.0.0.1:9527\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\r\n
Accept-Encoding: gzip, deflate\r\n
Sec-WebSocket-Version: 13\r\n
Origin: http://localhost:63342\r\n
Sec-WebSocket-Extensions: permessage-deflate\r\n
Sec-WebSocket-Key: jocLOLLq1BQWp0aZgEWL5A==\r\n
Cookie: session=6f2bab18-2dc4-426a-8f06-de22909b967b\r\n
Connection: keep-alive, Upgrade\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
Upgrade: websocket\r\n\r\n'
"""

# magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'


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


def get_header(data):
    """
     将请求头格式化成字典
     :param data:
     :return:
     """
    header_dict = {}
    data = str(data, encoding='utf-8')

    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


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://127.0.0.1:9527\r\n\r\n"

value = headers['Sec-WebSocket-Key'] + magic_string
print(value)
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
response_str = response_tpl % (ac.decode('utf-8'))
# 响应【握手】信息
conn.send(response_str.encode("utf8"))

while True:
    msg = conn.recv(8096)
    print(msg)
View Code

解密:

# 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-websocket实现聊天功能

websocket单聊群聊简单示例:

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

app=Flask(__name__)

user_socket_list = []

user_socket_dict = {

}

@app.route("/ws/<username>")
def ws(username):
    user_socket = request.environ.get("wsgi.websocket") #type:WebSocket
    if 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)
        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) # type:WebSocket
        u_socket.send(json.dumps(msg_dict))

        # for u_socket in user_socket_list:
        #     if u_socket == user_socket:
        #         continue
        #     try:
        #         u_socket.send(msg)
        #     except:
        #         continue

@app.route("/")
def index():
    return render_template("ws.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)
    http_serv.serve_forever()
View Code
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<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="width: 500px;height: 500px;"></div>

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

  function login() {
    var username = document.getElementById("username").value;
    ws = new WebSocket("ws://192.168.13.213:9527/ws/"+username);
    ws.onmessage = function (data) {
      console.log(data.data);
      var recv_msg = JSON.parse(data.data);
      var ptag = document.createElement("p");
      ptag.innerText= recv_msg.from_user + ":" + recv_msg.msg;
      document.getElementById("chat_list").appendChild(ptag);
    }
  }

  function send_msg() {
    var to_user = document.getElementById("to_user").value;
    var msg = document.getElementById("msg").value;
    var send_str = {
      "to_user" :to_user,
      "msg":msg
    };
    ws.send(JSON.stringify(send_str));
  }
</script>
</html>
ws.html

群聊无昵称

原生js代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>群聊</title>
</head>
<body>
<p>
    <input type="text" id="content">
    <button onclick="send_msg()">发送</button> <!--给按钮绑定点击事件-->
</p>

<div id="chat_list">

</div>
</body>
<script type="application/javascript">
  var ws = new WebSocket("ws://192.168.16.42:9527/my_socket");
  // 监听后端发来的消息,ws.onmessage起到监听的作用,只要有消息过来函数会自动执行
  ws.onmessage = function (eventMessage) {
      console.log(eventMessage.data); // 获取后端发来的消息
      var p = document.createElement("p");
      p.innerText = eventMessage.data;
      document.getElementById("chat_list").appendChild(p); // 将消息内容添加到div内
  };
  // 将我们输入的内容发送给后端
  function send_msg() {
      var content = document.getElementById("content").value;
      ws.send(content);
  };

</script>
</html>
原生js代码

后端逻辑代码

# -*- coding: utf-8 -*-
# @Time    : 2019/7/15 16:42

from flask import render_template,request,Flask
from geventwebsocket.handler import WebSocketHandler  # 提供WS协议处理
from geventwebsocket.server import WSGIServer # 承载服务
from geventwebsocket.websocket import WebSocket  # 语法提示

app = Flask(__name__)

user_socket_list = []
@app.route("/my_socket")
def my_socket():
    # 获取当前客户端与服务器的Socket连接
    user_socket = request.environ.get("wsgi.websocket")  # type:WebSocket
    if user_socket:
            user_socket_list.append(user_socket)
            print(len(user_socket_list),user_socket_list)
        # 1 [<geventwebsocket.websocket.WebSocket object at 0x000001D0D70E1458>]
        # print(user_socket,"OK 连接已经建立好了,接下来发消息吧")
    while 1:
        # 等待前端将消息发送过来
        msg = user_socket.receive()
        print(msg)

        for usocket in user_socket_list:
            try:
                usocket.send(msg)
            except:
                continue

@app.route("/qunliao")
def gc():
    return render_template("qunliao.html")

if __name__ == '__main__':
    http_serv = WSGIServer(("0.0.0.0",9527),app,handler_class=WebSocketHandler) # 这种启动方式和app.run()不冲突,该启动方式发什么请求都可以接受到
    http_serv.serve_forever()
后端逻辑代码

流程

1
2
3
4
5
6
7
1、用户在网页请求http://192.168.16.42:9527/qunliao
2、请求/qunliao这个路由走后端对应的视图函数gc返回qunliao.html这个页面,
3、页面在加载的过程中走到script代码时建立WebSocket连接请求ws://192.168.16.42:9527/my_socket,
4、ws://192.168.16.42:9527/my_socket请求走后端对应的视图函数,获取当前客户端与服务器的socket连接对象,调用该对象的receive方法,等待前端发来消息,
5、前端我们通过给input框绑定点击事件,获取用户输入的内容发送给服务器;
6、后端将前端发来的消息在发送给前端;
7、前端通过ws.onmessage这个事件监听着后端过来的消息,只要有消息就会自动触发函数执行并获取数据;

第一步是浏览器向/quliao这个路径发起请求:

jQuery

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>群聊</title>
</head>
<body>
<p>
    <input type="text" id="content">
    <button id="send_msg" >发送</button> <!--给按钮绑定点击事件-->
</p>

<div id="chat_list">

</div>
</body>
<script src="/static/jquery-3.4.1.js"></script>
<script type="application/javascript">
    var ws = new WebSocket("ws://192.168.16.42:9527/my_socket");
    // 监听后端发来的消息,ws.onmessage起到监听的作用,只要有消息过来函数会自动执行
    ws.onmessage = function (eventMessage) {
      console.log(eventMessage.data);       // 获取后端发来的消息
      var p = document.createElement("p"); // 创建一个p标签
      p.innerText = eventMessage.data;     // 将后端发来的数据添加到p标签内
      $("#chat_list").append(p)            // 将p标签添加到div内
    };
  // 将我们输入的内容发送给后端
    $("#send_msg").click(function () {
        var content = $("#content").val();
        ws.send(content);
    })
</script>
</html>
前端代码
from flask import render_template,request,Flask
from geventwebsocket.handler import WebSocketHandler  # 提供WS协议处理
from geventwebsocket.server import WSGIServer # 承载服务
from geventwebsocket.websocket import WebSocket  # 语法提示
app = Flask(__name__,static_folder="statics",static_url_path="/static")  # 获取静态文件
user_socket_list = []
@app.route("/my_socket")
def my_socket():
    # 获取当前客户端与服务器的Socket连接
    user_socket = request.environ.get("wsgi.websocket")  # type:WebSocket
    if user_socket:
            user_socket_list.append(user_socket)
            print(len(user_socket_list),user_socket_list)
        # 1 [<geventwebsocket.websocket.WebSocket object at 0x000001D0D70E1458>]
        # print(user_socket,"OK 连接已经建立好了,接下来发消息吧")
    while 1:
        # 等待前端将消息发送过来
        msg = user_socket.receive()
        print(msg)
        for usocket in user_socket_list:
            try:
                usocket.send(msg)
            except:
                continue

@app.route("/qunliao")
def gc():
    return render_template("qunliao.html")

if __name__ == '__main__':
    http_serv = WSGIServer(("0.0.0.0",9527),app,handler_class=WebSocketHandler)
    http_serv.serve_forever()
后端代码

 

带群昵称的群聊

通过动态路由参数获取前端传过来的用户名

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>群聊</title>
</head>
<body>
<input type="text" id="username">
<button id="login">登录</button>
<p>
    <input type="text" id="content">
    <button id="send_msg" >发送</button> <!--给按钮绑定点击事件-->
</p>

<div id="chat_list">

</div>
</body>
<script src="/static/jquery-3.4.1.js"></script>
<script type="application/javascript">
    var ws = null; // 创建全局变量,ws多处使用

    $("#login").click(function () {
         var username = $("#username").val();
         console.log(username);
         // 创建一个websocket对象,建立websocket连接,更改了全局的ws,将用户名拼接上
         ws = new WebSocket("ws://192.168.16.42:9527/my_socket/" + username);

        // 监听后端发来的消息,ws.onmessage起到监听的作用,只要有消息过来函数会自动执行
        ws.onmessage = function (eventMessage) {
            console.log(eventMessage.data);       // 获取后端发来的消息
            var str_obj = JSON.parse(eventMessage.data);  // 反序列化,因为我们在发送给后端的时候是json
            var p = document.createElement("p"); // 创建一个p标签
            $(p).text(str_obj.from_user +":"+str_obj.chat);     // 将dom对象转换成jQuery对象,将后端发来的数据添加到p标签内
            $("#chat_list").append(p)            // 将p标签添加到div内
        };
    });

    // 将我们输入的内容发送给后端
    $("#send_msg").click(function () {
        var content = $("#content").val();
        var username = $("#username").val();
        // 将要发送的内容封装成自定义对象
        var sendStr = {
            from_user:username,
            chat:content
        };
        console.log(sendStr);
        // 序列化后发送给后端
        ws.send(JSON.stringify(sendStr));
    });
</script>
</html>
前端代码
from flask import render_template,request,Flask
from geventwebsocket.handler import WebSocketHandler  # 提供WS协议处理
from geventwebsocket.server import WSGIServer # 承载服务
from geventwebsocket.websocket import WebSocket  # 语法提示
app = Flask(__name__,static_folder="statics",static_url_path="/static")
user_socket_dict = {}

# 建立websocket连接时,前端将名字发送过来了
@app.route("/my_socket/<username>")
def my_socket(username):
    # 获取当前客户端与服务器的Socket连接
    user_socket = request.environ.get("wsgi.websocket")  # type:WebSocket

    if user_socket:
        # 以名字为key,连接对象为value添加到字典中
        user_socket_dict[username] = user_socket
    while 1:
        # 等待前端将消息发送过来,此时是json数据
        msg = user_socket.receive()
        for usocket in user_socket_dict.values():
            try:
                # 将收到的信息在发送给所有与服务器建立连接的前端
                usocket.send(msg)
            except:
                continue

@app.route("/qunliao")
def gc():
    return render_template("qunliaonicheng.html")

if __name__ == '__main__':
    http_serv = WSGIServer(("0.0.0.0",9527),app,handler_class=WebSocketHandler)
    http_serv.serve_forever()
后端代码

效果:

websocket实现私聊

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>群聊</title>
</head>
<body>
我的昵称:<input type="text" id="username">
<button id="login">登录</button>
<p><input type="text" id="to_user">发送
    <input type="text" id="content">
    <button id="send_msg" >发送</button> <!--给按钮绑定点击事件-->
</p>

<div id="chat_list">

</div>
</body>
<script src="/static/jquery-3.4.1.js"></script>
<script type="application/javascript">
    var ws = null; // 创建全局变量,ws多处使用

    $("#login").click(function () {
         var username = $("#username").val();
         console.log(username);
         // 创建一个websocket对象,建立websocket连接,更改了全局的ws,将用户名拼接上
         ws = new WebSocket("ws://192.168.16.42:9527/my_socket/" + username);

        // 监听后端发来的消息,ws.onmessage起到监听的作用,只要有消息过来函数会自动执行
        ws.onmessage = function (eventMessage) {
            console.log(eventMessage.data);       // 获取后端发来的消息
            var str_obj = JSON.parse(eventMessage.data);  // 反序列化,因为我们在发送给后端的时候是json
            var p = document.createElement("p"); // 创建一个p标签
            $(p).text(str_obj.from_user +":"+str_obj.chat);     // 将dom对象转换成jQuery对象,将后端发来的数据添加到p标签内
            $("#chat_list").append(p)            // 将p标签添加到div内
        };
    });

    // 将我们输入的内容发送给后端
    $("#send_msg").click(function () {
        var content = $("#content").val();
        var username = $("#username").val();
        var to_user = $("#to_user").val();
        // 将要发送的内容封装成自定义对象
        var sendStr = {
            from_user:username,
            chat:content,
            to_user:to_user,
        };
        console.log(sendStr);
        // 序列化后发送给后端
        ws.send(JSON.stringify(sendStr));
    });
</script>
</html>
前端页面
import json
from flask import render_template,request,Flask
from geventwebsocket.handler import WebSocketHandler  # 提供WS协议处理
from geventwebsocket.server import WSGIServer # 承载服务
from geventwebsocket.websocket import WebSocket  # 语法提示
app = Flask(__name__,static_folder="statics",static_url_path="/static")
user_socket_dict = {}

# 建立websocket连接时,前端将名字发送过来了
@app.route("/my_socket/<username>")
def my_socket(username):
    # 获取当前客户端与服务器的Socket连接
    user_socket = request.environ.get("wsgi.websocket")  # type:WebSocket

    if user_socket:
        # 以名字为key,连接对象为value添加到字典中
        user_socket_dict[username] = user_socket
    while 1:
        # 等待前端将消息发送过来,此时是json数据
        msg = user_socket.receive()
        print(msg)    # {"from_user":"wuchao","chat":"123","to_user":"xiaohei"}
        # 反序列化
        msg_dict = json.loads(msg)
        # 查找字典中前端要发送信息给那个人的名字
        to_username = msg_dict.get("to_user")
        # 获取目标人物的连接地址
        to_user_socket = user_socket_dict.get(to_username)
        # 将信息发送给目标人物
        to_user_socket.send(msg)

@app.route("/siliao")
def gc():
    return render_template("siliao.html")

if __name__ == '__main__':
    http_serv = WSGIServer(("0.0.0.0",9527),app,handler_class=WebSocketHandler)
    http_serv.serve_forever()
后端代码

效果

 

posted on 2019-12-14 17:01  始终不够啊  阅读(660)  评论(0编辑  收藏  举报