• Python3+WebSockets实现WebSocket通信


    一、说明

    1.1 背景说明

    前段时间同事说云平台通信使用了个websocket的东西,今天抽空来看一下具体是怎么个通信过程。

    从形式上看,websocket是一个应用层协议,socket是数据链路层、网络层、传输层的抽像;从应用场合上看,websocket可以使用javascript实现,而socket不能用javascript实现(真不能吗?我不太确定);从实际效果上看,和一般的socket连接用起来没什么区别。

    我们知道http是短连接的,反复建立和销毁连接比较耗费资源,另外http协议经常头部内容比主体内容还长也比较浪费资源;websocket可以认为就是一个内容使用载荷固定格式的socket长连接。

    websocket基本协议格式如下,更多说明见RFC 6455

    1.2 环境说明

    当前环境我使用Python3+WebSockets库,WebSockets直接使用pip安装即可:

    pip install websockets

    二、代码实现

    长连接是有状态的,所以一般在且只在最开始进行一次身份认证,而后通信过程不需要认证信息。我们这里实现一个简单的用户名密码认证过程。长连接更多内容可参考“连接与短连接的安全差异讨论 ”。

    另外,注意把代码中的ip改成自己的。

    2.1 python服务端代码

    import asyncio
    import websockets
    
    # 检测客户端权限,用户名密码通过才能退出循环
    async def check_permit(websocket):
        while True:
            recv_str = await websocket.recv()
            cred_dict = recv_str.split(":")
            if cred_dict[0] == "admin" and cred_dict[1] == "123456":
                response_str = "congratulation, you have connect with server
    now, you can do something else"
                await websocket.send(response_str)
                return True
            else:
                response_str = "sorry, the username or password is wrong, please submit again"
                await websocket.send(response_str)
    
    # 接收客户端消息并处理,这里只是简单把客户端发来的返回回去
    async def recv_msg(websocket):
        while True:
            recv_text = await websocket.recv()
            response_text = f"your submit context: {recv_text}"
            await websocket.send(response_text)
    
    # 服务器端主逻辑
    async def main_logic(websocket, path):
        await check_permit(websocket)
    
        await recv_msg(websocket)
    
    start_server = websockets.serve(main_logic, '10.10.6.91', 5678)
    asyncio.get_event_loop().run_until_complete(start_server)
    asyncio.get_event_loop().run_forever()

    2.2 python版客户端代码

    import asyncio
    import websockets
    
    # 向服务器端认证,用户名密码通过才能退出循环
    async def auth_system(websocket):
        while True:
            cred_text = input("please enter your username and password: ")
            await websocket.send(cred_text)
            response_str = await websocket.recv()
            if "congratulation" in response_str:
                return True
    
    # 向服务器端发送认证后的消息
    async def send_msg(websocket):
        while True:
            _text = input("please enter your context: ")
            if _text == "exit":
                print(f'you have enter "exit", goodbye')
                await websocket.close(reason="user exit")
                return False
            await websocket.send(_text)
            recv_text = await websocket.recv()
            print(f"{recv_text}")
    
    # 客户端主逻辑
    async def main_logic():
        async with websockets.connect('ws://10.10.6.91:5678') as websocket:
            await auth_system(websocket)
    
            await send_msg(websocket)
    
    asyncio.get_event_loop().run_until_complete(main_logic())

    2.3 html版客户端代码

    html版客户端代码,只能通过回调函数接收服务端返回的数据,不能主动接收,感觉怪怪的。

    <!DOCTYPE HTML>
    <html>
       <head>
       <meta charset="utf-8">
       <title>websocket通信客户端</title>
           <script type="text/javascript">
             function WebSocketTest()
             {
                if ("WebSocket" in window)
                {
                   // 打开一个 web socket
                   var ws = new WebSocket("ws://10.10.6.91:5678");
                   
                   // 连接建立后的回调函数
                   ws.onopen = function()
                   {
                   // Web Socket 已连接上,使用 send() 方法发送数据
                      ws.send("admin:123456");
                      alert("正在发送:admin:123456");
                   };
                   
                   // 接收到服务器消息后的回调函数
                   ws.onmessage = function (evt) 
                   { 
                      var received_msg = evt.data;
                      if (received_msg.indexOf("sorry") == -1) {
                        alert("收到消息:"+received_msg);
                      }
                      
                   };
                   
                   // 连接关闭后的回调函数
                   ws.onclose = function()
                   { 
                      // 关闭 websocket
                      alert("连接已关闭..."); 
                   };
                }
                else
                {
                   // 浏览器不支持 WebSocket
                   alert("您的浏览器不支持 WebSocket!");
                }
             }
             
          </script>
       </head>
    
       <body onload="WebSocketTest()">
    
       </body>
    </html>
    View Code

    三、通信数据包截获及通信过程分析

    以下数据包其于上边的python服务端和html版客户端,再次强调注意把代码中的ip改成自己电脑当前的ip。

    3.1 wireshark通信数据包截获及通信过程分析

    wireshark拦截数据包后可使用过滤器表达式“websocket”进行过滤:

    我们追踪数据流可以更清晰地看清websocket的通信过程,可以看到先是用http完成了建立连接,然后切换到websocket协议。

     再看具体数据流也确实如此,先用两个http包建立连接,而后是websocket通信(问题是不清楚websocket内容是怎么编码的,有些就显示不了原始内容)

    3.2 burpsuite通信数据包截获及通信过程分析

    和正常配置代理即可,burpsuite能识别和拦截websocket数据包,如下图:

    不过和一般http请求有区别的是,websocket请求会被单独汇总到“WebSockets history”选项卡,而不是和http请求混在“HTTP history”选项卡。

    3.3 开发者工具通信数据包截获及通信过程分析

    Firefox开发者工具只能看到建立websocket连接的两个http数据包,没看到怎么查看具体传输内容,Chrome开发者工具Frames选项卡可以,如下:

    参考:

    https://websockets.readthedocs.io/en/stable/intro.html

    https://www.runoob.com/html/html5-websocket.html

  • 相关阅读:
    关于研发核心团队建设的一些思考
    无法打开物理文件xxx.mdf操作系统错误 5:“5(拒绝访问。)” (Microsoft SQL Server,错误: 5120)的解决方法
    自适应网页设计(Responsive Web Design)
    css浮动与绝对定位小记
    WEB进度条控件
    GitHub上整理的一些工具
    雾里看花般的迷茫--货运APP
    揭秘史上最完美一步到位的搭建Andoriod开发环境
    我是如何在SQLServer中处理每天四亿三千万记录的
    货运APP产品魔力=卓越功能×情感诉求
  • 原文地址:https://www.cnblogs.com/lsdb/p/10949766.html
Copyright © 2020-2023  润新知