自動IP

プレビュー



なぜIPを探しているのですか?



先日、データベースの更新を特定の端末に送信するタスクに直面しました。 しかし、送信する前に、どこに送信するか、どこで受け取るかを把握する必要がありました。 一見したところ、サーバーのIPアドレスを端末に伝えてデータを収集する方が論理的ですが、次のような微妙な点がこのような実装を妨げていました。





したがって、「ピックアップ」のアイデアから「送信」のアイデアに移り、Python 3でIPアドレスの自動検索の実装をいじり始めました。



最初に思いついたのは、データベースの定期的な配布とudp broadcastによるハッシュの合計でしたが、残念ながら、UDPプロトコル配信された情報の整合性を保証しません 。 ただし、ブロードキャストアドレスを使用するという考え方が最終的な実装方法でした。



そのため、最終的に、UDPサーバーからブロードキャストアドレス255.255.255.255にブロードキャストを送信し、ターミナルにUDPサーバーをインストールすることにしました。UDPサーバーは、このブロードキャストのコマンドを受信すると、中央サーバーへのTCP接続を開きます。



おおよそのネットワークトポロジ






最初のステップ:UDPクライアントを作成する



公式のPythonサイトにはソケットの実装がいくつかありますが、すぐに問題に遭遇しました。 ブロードキャストアドレスに送信するとき、インタープリターは以下を発行しました: PermissionError:[Errno 13] Permission denied 。 「stackoverflow」で、問題の解決策を見つけました-ソケットへのそのような配布のために、特別なフラグSO_BROADCASTを設定する必要があります。 この事実を考えると、UDPクライアントを作成する機能は次の形式を取りました。



def create_broadcast_socket(): udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udp_sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) udp_sock.settimeout(2) return udp_sock
      
      





また、次の関数は、このソケットを介してさまざまなメッセージを送信します。



 def ask_addresses(): with create_broadcast_socket() as sock: sock.sendto('show yourself'.encode('utf-8'), ('255.255.255.255', PORT)) def update_many(): with create_broadcast_socket() as sock: sock.sendto('get updates'.encode('utf-8'), ('255.255.255.255', PORT)) def update_one(ip): with create_broadcast_socket() as sock: sock.sendto('get updates'.encode('utf-8'), (ip, PORT))
      
      





名前から、彼らが何をしているのかが明確であり、ここで特別な説明は必要ないと思います。



2番目のステップ:UDPサーバーとTCPクライアントを作成する



さいわい、 socketserverモジュールはすでに標準言語ライブラリにあります。 本格的なUDPサーバーを作成するには、 DatagramRequestHandlerクラスから継承し、 handle()メソッドにロジックを実装するだけで十分です。



 class EchoServer(socketserver.DatagramRequestHandler): def handle(self): data = self.request[0].strip().decode('utf-8') client_ip = self.client_address[0] if data.startswith('show yourself'): print('show myself') with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as tcp_sock: tcp_sock.connect((client_ip, PORT)) tcp_sock.send('show\n'.encode('UTF-8')) elif data.startswith('get updates'): print('get updates FROM ') with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as tcp_sock: tcp_sock.connect((client_ip, PORT)) tcp_sock.send('get\n'.encode('UTF-8')) part = tcp_sock.recv(1024) file = open('internal.db', 'wb') while part: file.write(part) print(part) part = tcp_sock.recv(1024) file.close() print(self.request)
      
      





このサーバーは、特定のポートでUDP接続をリッスンします(ポート番号はグローバル変数PORTに保存されます)。 パケットを受信した後、その内容をチェックし、 「show your」というメッセージがパケット内にある場合、TCP接続を開き、 「show \ n」というメッセージを送信しますその後、更新サーバーのTCPサーバーはこのIPアドレスをアドレスのセットに追加します。 パケットで「get updates」というメッセージ受信した場合、ターミナルはTCP接続を開き、 「get \ n」というメッセージを送信しますその後、SQLiteデータベースファイルのダウンロードが開始されます。 TCPサーバー上のソケットでreadline()メソッドを呼び出すことができるように、便宜上、メッセージの最後に「\ n」文字を使用しました



これらはすべて次のように始まります。



 def run_echo_server(): server = socketserver.UDPServer(('', PORT), EchoServer) server.serve_forever()
      
      





アドレスの代わりに空の行は、利用可能なすべてのネットワークインターフェイスで接続をリッスンするようサーバーに指示します。



3番目のステップ:TCPクライアントの作成



この通信チェーンの最後のリンクは、更新サーバー上のTCPサーバーになります。 同じsocketserverモジュールのStreamRequestHandlerクラスに基づいて実装されます。 最初の場合と同様に、 handle()メソッドを実装するためだけに残りました。



 class NetworkController(socketserver.StreamRequestHandler): def handle(self): request_type = self.rfile.readline() print("{} wrote: {}".format(self.client_address[0], request_type)) if request_type.decode('UTF-8') == 'show\n': scales_catalogue.add(self.client_address[0]) # print(scales_catalogue) elif request_type.decode('UTF-8') == 'get\n': file = open('internal.db', 'rb') part = file.read(1024) while part: self.wfile.write(part) part = file.read(1024) file.close()
      
      





上記のように、 「show \ n」というメッセージを受信すると、サーバーはアドレスの重複を避けるために、メッセージが到着したIPアドレスを内部アレイに追加します。 サーバーが「get \ n」というメッセージを受信すると、1024バイトの部分でデータベースファイルの送信を開始します。



このサーバーは、次の機能で開始します。



 def create_server(): return socketserver.TCPServer(('', PORT), NetworkController) def run_pong_server(): server = create_server() server.serve_forever()
      
      





UDPサーバーと同様に、空の行により、サーバーは使用可能なすべてのネットワークインターフェイスで接続をリッスンします。



まとめ



したがって、システムは、UDPメーリングを介して端末のアドレスを検出し、IPアドレスのリストから選択して端末を強制的に更新されたデータベースファイルを選択するか、UDPメーリングを介してネットワーク上のすべての端末が更新を選択できるようにすることが判明しました。



ニュアンスが理解されない場合、完全なソースコードはGitHubのリポジトリのパブリックドメインにあります



All Articles