• Python中的WebSocket


    一、Websockets介绍

        随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了。近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端的通信功能,使服务端也能主动向客户端发送数据。
      我们知道,传统的HTTP协议是无状态的,每次请求(request)都要由客户端(如 浏览器)主动发起,服务端进行处理后返回response结果,而服务端很难主动向客户端发送数据;这种客户端是主动方,服务端是被动方的传统Web模式 对于信息变化不频繁的Web应用来说造成的麻烦较小,而对于涉及实时信息的Web应用却带来了很大的不便,如带有即时通信、实时数据、订阅推送等功能的应 用。在WebSocket规范提出之前,开发人员若要实现这些实时性较强的功能,经常会使用折衷的解决方法:轮询(polling)和Comet技术。其实后者本质上也是一种轮询,只不过有所改进。
      轮询是最原始的实现实时Web应用的解决方案。轮询技术要求客户端以设定的时间间隔周期性地向服务端发送请求,频繁地查询是否有新的数据改动。明显地,这种方法会导致过多不必要的请求,浪费流量和服务器资源。
      Comet技术又可以分为长轮询和流技术。长轮询改进了上述的轮询技术,减小了无用的请求。它会为某些数据设定过期时间,当数据过期后才会向服务端发送请求;这种机制适合数据的改动不是特别频繁的情况。流技术通常是指客户端使用一个隐藏的窗口与服务端建立一个HTTP长连接,服务端会不断更新连接状态以保持HTTP长连接存活;这样的话,服务端就可以通过这条长连接主动将数据发送给客户端;流技术在大并发环境下,可能会考验到服务端的性能。
      这两种技术都是基于请求-应答模式,都不算是真正意义上的实时技术;它们的每一次请求、应答,都浪费了一定流量在相同的头部信息上,并且开发复杂度也较大。
      伴随着HTML5推出的WebSocket,真正实现了Web的实时通信,使B/S模式具备了C/S模式的实时通信能力。WebSocket的工作流程是这 样的:浏览器通过JavaScript向服务端发出建立WebSocket连接的请求,在WebSocket连接建立成功后,客户端和服务端就可以通过 TCP连接传输数据。因为WebSocket连接本质上是TCP连接,不需要每次传输都带上重复的头部数据,所以它的数据传输量比轮询和Comet技术小了很多.

       关于websocket的优点可以看:http://www.tuicool.com/articles/7zyMvy6

     

    二,Python中的WebSocket

     Django的WebSocket:

      1.dwebsocket:  https://www.cnblogs.com/huguodong/p/6611602.html

      2.channels:  http://www.cnblogs.com/evilliu/articles/6529087.html

     参考链接:https://www.cnblogs.com/jingmoxukong/p/7755643.html

     

    这里使用flask作为服务器,python版本为3.6.5

    安装模块

    pip install gevent-websocket

     群聊

    from flask import Flask,request,render_template
    from geventwebsocket.handler import WebSocketHandler
    from gevent.pywsgi import WSGIServer
    from geventwebsocket.websocket import WebSocket
    import json
    
    
    app = Flask(__name__)
    
    user_dict = {}  # 空字典,用来存放用户名和发送消息
    
    @app.route("/<username>")  # 参数为用户名
    def index(username):
        # 获取请求的WebSocket对象
        user_socket = request.environ.get("wsgi.websocket") # type:WebSocket
        if user_socket:
            # 设置键值对
            # {'xiao': <geventwebsocket.websocket.WebSocket object at 0x0000020F6F6B8DB0>}
            user_dict[username] = user_socket
            print(user_dict)
    
        # 循环,接收消息
        while True:
            # 接收消息
            msg = user_socket.receive()
            print(msg)
            # 反序列化数据,因为前端发送的是json
            recv_msg = json.loads(msg)
            print(recv_msg)
            # 构造数据结构
            send_msg = {
                # 获取用户名
                "username":recv_msg.get("username"),
                # 获取消息
                "msg":recv_msg.get("msg")
            }
            # 遍历字典
            for i in user_dict.values():
                # 这里的i就是websocket对象
                # 判断websocket对象等于请求的websocket对象
                if i == user_socket:
                    # 跳过循环
                    continue
    
                # 发送数据,对数据做序列化
                i.send(json.dumps(send_msg))
    
    @app.route("/ws")
    def ws():
        return render_template("many_people.html")
    
    if __name__ == '__main__':
        # 创建一个WebSocket服务器
        http_serv = WSGIServer(("0.0.0.0",5000),app,handler_class=WebSocketHandler)
        # 开始监听HTTP请求
        http_serv.serve_forever()
        # app.run("0.0.0.0", 5000, debug=True)

    前端

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    你的昵称:<input type="text" id="nickname">
    <button onclick="connws()">连接服务器</button>
    <br><br>
    发送消息:<input type="text" id="talk">
    <button onclick="send_msg()">发送信息</button><br><br>
    <div style=" 500px;height: 100%;border: 1px red solid;" id="text">
    
    </div>
    </body>
    <script type="application/javascript">
        var user_name = null;  //用户名
        var ws = null;  //WebSocket 对象,默认设置为空
    
        //连接ws
        function connws() {
            //获取输入框中的用户名
            user_name = document.getElementById("nickname").value;
            //创建 WebSocket 对象
            ws = new WebSocket("ws://127.0.0.1:5000/" + user_name);
            //客户端接收服务端数据时触发
            ws.onmessage = function (data) {
                // 反序列化接收数据
                var recv_msg = JSON.parse(data.data);
                console.log(recv_msg);
                // 执行自定义函数createDiv,传入2个参数
                createDiv(recv_msg.username, recv_msg.msg);
            };
        }
    
        //发送消息
        function send_msg() {
            // 获取输入框的发送消息
            var talk = document.getElementById("talk").value;
            // 执行自定义函数createDiv
            createDiv("w", talk);
            // 组件发送数据对象
            send_str = {
                username:user_name,  //用户名
                msg:talk  //消息
            };
            //使用连接发送数据,序列化对象
            ws.send(JSON.stringify(send_str));
        };
    
        //显示聊天信息
        function createDiv(self, content) {
            // 创建div标签
            var divtag = document.createElement("div");
            //定义格式
            var who = self + " : ";
            // 判断参数为w时
            if (self == "w") {
                // 替换字符串
                who = "我 : "
            }
            // 修改显示框的text属性
            divtag.innerText = who + content;
            // 获取显示框
            var text = document.getElementById("text");
            // appendChild() 方法向节点添加最后一个子节点
            // 添加一个div标签
            text.appendChild(divtag);
        }
    
    </script>
    </html>

     单聊

    from flask import Flask,request,render_template
    from geventwebsocket.handler import WebSocketHandler
    from gevent.pywsgi import WSGIServer
    from geventwebsocket.websocket import WebSocket
    import json
    
    
    app = Flask(__name__)
    
    user_dict = {}  # 空字典,用来存放用户名和发送消息
    
    @app.route("/<username>")  # 参数为用户名
    def index(username):
        # 获取请求的WebSocket对象
        user_socket = request.environ.get("wsgi.websocket") # type:WebSocket
        if user_socket:
            # 设置键值对
            # {'xiao': <geventwebsocket.websocket.WebSocket object at 0x0000020F6F6B8DB0>}
            user_dict[username] = user_socket
            print(user_dict)
    
        # 循环,接收消息
        while True:
            # 接收消息
            msg = user_socket.receive()
            # print(msg)
            # 反序列化数据,因为前端发送的是json
            recv_msg = json.loads(msg)
            print(recv_msg)
            # 构造数据结构
            send_msg = {
                # 消息
                "msg": recv_msg.get("msg"),
                # 来自于哪个用户
                "from_user": username,
            }
            # 获取聊天对象的名字
            to_user = user_dict.get(recv_msg.get("to_user"))
            # 发送数据
            to_user.send(json.dumps(send_msg))
    
    @app.route("/ws")
    def ws():
        return render_template("single_chat.html")
    
    if __name__ == '__main__':
        # 创建一个WebSocket服务器
        http_serv = WSGIServer(("0.0.0.0",5000),app,handler_class=WebSocketHandler)
        # 开始监听HTTP请求
        http_serv.serve_forever()
        # app.run("0.0.0.0", 5000, debug=True)
    后端
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    你的昵称:<input type="text" id="nickname">
    <button onclick="connws()">连接服务器</button>
    <br>
    与谁说话:<input type="text" id="sender">
    <br>
    发送消息:<input type="text" id="talk">
    <button onclick="send_msg()">发送信息</button><br/><br/>
    <div style=" 500px;height: 100%;border: 1px red solid;" id="text">
    
    </div>
    </body>
    <script type="application/javascript">
        var user_name = null;  //用户名
        var ws = null;  //WebSocket 对象,默认设置为空
    
        //连接ws
        function connws() {
            //获取输入框中的用户名
            user_name = document.getElementById("nickname").value;
            //创建 WebSocket 对象
            ws = new WebSocket("ws://127.0.0.1:5000/" + user_name);
            //客户端接收服务端数据时触发
            ws.onmessage = function (data) {
                // 反序列化接收数据
                var recv_msg = JSON.parse(data.data);
                console.log(recv_msg);
                // 执行自定义函数createDiv,传入2个参数
                createDiv(recv_msg.from_user, recv_msg.msg);
            };
        }
    
        function send_msg() {
            // 获取输入框的发送消息
            var talk = document.getElementById("talk").value;
            // 获取输入框的聊天对象
            var sender = document.getElementById("sender").value;
            // 执行自定义函数createDiv
            createDiv("w", talk);
            // 构造发送数据对象
            send_str = {
                msg:talk,  //消息
                to_user:sender, //对方
            };
            //使用连接发送数据,序列化对象
            ws.send(JSON.stringify(send_str));
        };
    
        //显示聊天信息
        function createDiv(self, content) {
            // 创建div标签
            var divtag = document.createElement("div");
            //定义格式
            var who = self + " : ";
            // 判断参数为w时
            if (self == "w") {
                // 替换字符串
                who = "我 : "
            }
            // 修改显示框的text属性
            divtag.innerText = who + content;
            // 获取显示框
            var text = document.getElementById("text");
            // appendChild() 方法向节点添加最后一个子节点
            // 添加一个div标签
            text.appendChild(divtag);
        }
    
    </script>
    </html>
    前端

     

    相关拓展:https://segmentfault.com/a/1190000010140660

  • 相关阅读:
    1.14验证码 彩票
    String代码示例
    1.13作业
    控制台输入人数和分数 自动判断最高分最低分
    对矩阵进行转置运算
    16、输入三角形的三个边,求其面积
    02、
    15、判断字符串是否回文——字符串
    14、求出最大元素的下标及地址值——数组
    13、字符串在指定的地方以及元素个数实行逆置——字符串
  • 原文地址:https://www.cnblogs.com/Mr-Murray/p/9846682.html
Copyright © 2020-2023  润新知