80887


websocketsモジュールを使用して、クライアント側のwebsocketをKeep-aliveで接続したいです。  
しかし、時間が経つとサーバーからソケットを切断されてしまいます。  
これは、パケットキャプチャした結果、Keep-aliveが送信されていないためにサーバーからFINが返ってきていました。

どのように記述したらKeep-aliveとして実装できるのでしょうか？  
クライアント側は完全に待ち受けで、送信はしません。

なお、websocket-clientモジュールを回避しようと思い、websocketsモジュールを使用しております。

[code]

    import asyncio
    
    import websockets
    
    async def hello():
        uri = "ws://localhost:8080"
        async with websockets.connect(uri) as websocket:
    
            while(True):
                ret = await websocket.recv()
                print(ret)
    
    asyncio.get_event_loop().run_until_complete(hello())
    
    
    
[/code]

websocket-clientモジュールを使用した場合に、HTTP:Keep-aliveができ、websocketsモジュールではKeep-
aliveができないため、比較として、パケットキャプチャしました。  
pyload部分しか違いがありません。画像の左側がwebsocket-clientモジュール使用時のKeep-
aliveパケット、右側がwebsocketsモジュールでのping()コマンドでのKeep-aliveを期待したパケットです。

[![Keep-
aliveのパケットキャプチャ](https://i.stack.imgur.com/Mismw.png)](https://i.stack.imgur.com/Mismw.png)

websocket-clientモジュールのソースコードを見てみると、payloadは空のままping実行されています。(run_forever())  
websocketsでは低レベルのpayloadがセットできない。文字列あるいはバイト列で「空」は受け付けてくれません。  
根本的にwebsocketsでは無理なのかもしれません。  
なお、websocketsのFAQに記載があるpingを20秒間隔で送信するという部分ですが、パケットキャプチャしても、そのような形跡は見当たりませんでした。


CC BY-SA 4.0
2021-08-05T08:12:40.043
80617
2021-08-18T08:03:45.300
2021-08-06T07:33:45.790
32891
32891
question
python websocket
Python websocketsを使用したクライアントでのKeep-alive接続方法


私の目的は、WebSocket接続において、相手が送出してくるデータを待ち受けるだけであるため、ある程度、汎用性を無視した受信専用モジュールで十分です。  
そのため、標準ライブラリで、何となく動くものを作成しました。

websokectsモジュールやasyncioで接続したセッションに対して、socketインスタンスに移譲してsetsockoptメソッドが使用できれば良いのですが、、、どなたか方法をご存知の方は、別途回答をいただきたいと思います。

[code]

    from base64 import encodebytes as base64encode
    import os
    import selectors
    import socket
    import sys
    import threading
    import time
    import traceback
    import urllib.parse
    
    class ClientWebSocket(object):
        def __init__(self, url:str,
            on_open=None, on_recv_msg=None, on_err_msg=None, on_close=None
            ):
    
            self.url=url
    
            # callback method
            self.on_open=on_open
            self.on_recv_msg=on_recv_msg
            self.on_err_msg=on_err_msg
            self.on_close=on_close
    
            return
    
        def _callback_method_(self,method,*args):
            if method:
                try:
                    method(self,*args)
                except Exception as err:
                    if self.on_err_msg:
                        self.on_err_msg(self,err)
            return
    
        def run(self):
            try:
                url = urllib.parse.urlsplit(self.url)
            except Exception as err:
                if self.on_err_msg:
                    self.on_err_msg(self,err)
                return err
    
            try:
                self.sock = socket.socket(
                    family=socket.AF_INET,type=socket.SOCK_STREAM)
                self.sock.connect((url.hostname,url.port))
                self._keep_alive_(self.sock)
    
                self._callback_method_(self.on_open)
    
                send_msg = self._create_web_socket_msg_(url.hostname,url.port,url.path)
    
                self.sock.sendall(send_msg.encode())
    
                self._read_()
    
            except Exception as err:
                if self.on_err_msg:
                    self.on_err_msg(self,err)
                return err
    
        def _keep_alive_(self,sock):
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE,1)
            sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE,30)
            sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL,30)
            sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT,5)
    
        def _read_(self):
            while True:
                select = selectors.DefaultSelector()
                select.register(self.sock,selectors.EVENT_READ)
    
                read = select.select()
                if read:
                    msg = self.sock.recv(1024)
                    if self.on_recv_msg:
                        self.on_recv_msg(msg)
    
                select.close()
    
        def _create_web_socket_msg_(self,hostname,port,path):
    
            sec_key = self._create_sec_websocket_key()
    
            msg = f'GET {path} HTTP/1.1\r\n'
            msg += f'Upgrade: websocket\r\n'
            msg += f'HOST: {hostname}:{port}\r\n'
            msg += f'Origin: http://{hostname}:{port}\r\n'
            msg += f'Sec-WebSocket-Key: {sec_key}\r\n'
            msg += f'Sec-WebSocket-Version: 13\r\n'
            msg += f'Connection: Upgrade\r\n\r\n'
    
            return msg
    
    
        def _create_sec_websocket_key(self):
            return base64encode(os.urandom(16)).decode('utf-8').strip()
    
    
    if __name__ == '__main__':
    
        def on_open(*args):
            return
    
        def on_msg(*args):
            print(args)
            return
    
        def on_error(*args):
            return
    
        def on_close(*args):
            return
    
        instSock = ClientWebSocket(
            url = 'ws://127.0.0.1:8080/path',
            on_open=on_open,
            on_recv_msg=on_msg,
            on_err_msg=on_error,
            on_close=on_close
            )
        instSock.run()
    
    
    
[/code]


CC BY-SA 4.0
2021-08-18T08:03:45.300
80887
2021-08-18T08:03:45.300
32891
80617
answer