サーチ…


前書き

多くのプログラミング言語は、ソケットを使用してプロセス間またはデバイス間で通信します。このトピックでは、Pythonのソケットモジュールを使用して、一般的なネットワークプロトコルを使ってデータを送受信できるようにする方法について説明します。

パラメーター

パラメータ説明
socket.AF_UNIX UNIXソケット
socket.AF_INET IPv4
socket.AF_INET6 IPv6
socket.SOCK_STREAM TCP
socket.SOCK_DGRAM UDP

UDP経由のデータ送信

UDPはコネクションレスプロトコルです。他のプロセスまたはコンピュータへのメッセージは、何らかの接続を確立することなく送信されます。あなたのメッセージが受信された場合、自動確認はありません。 UDPは、通常、レイテンシに敏感なアプリケーションや、ネットワーク全体のブロードキャストを送信するアプリケーションで使用されます。

次のコードは、UDPを使用してローカルホストのポート6667で待機しているプロセスにメッセージを送信します

UDPはコネクションレスなので、送信後にソケットを「閉じる」必要はないことに注意してください。

from socket import socket, AF_INET, SOCK_DGRAM
s = socket(AF_INET, SOCK_DGRAM)
msg = ("Hello you there!").encode('utf-8')  # socket.sendto() takes bytes as input, hence we must encode the string first.
s.sendto(msg, ('localhost', 6667)) 

UDPによるデータ受信

UDPはコネクションレスプロトコルです。つまり、メッセージを送信するピアは、メッセージを送信する前に接続を確立する必要はありません。 socket.recvfromはタプルを返す( msg [ソケットが受け取ったメッセージ]、 addr [送信者のアドレス])

socketモジュールのみを使用するUDPサーバ:

from socket import socket, AF_INET, SOCK_DGRAM
sock = socket(AF_INET, SOCK_DGRAM)
sock.bind(('localhost', 6667))

while True:
    msg, addr = sock.recvfrom(8192)  # This is the amount of bytes to read at maximum
    print("Got message from %s: %s" % (addr, msg))

以下は、 socketserver.UDPServerを使用した別の実装socketserver.UDPServer

from socketserver import BaseRequestHandler, UDPServer

class MyHandler(BaseRequestHandler):
    def handle(self):
        print("Got connection from: %s" % self.client_address)
        msg, sock = self.request
        print("It said: %s" % msg)
        sock.sendto("Got your message!".encode(), self.client_address) # Send reply

serv = UDPServer(('localhost', 6667), MyHandler)
serv.serve_forever()

デフォルトでは、 socketsブロックされます。つまり、スクリプトの実行は、ソケットがデータを受け取るまで待機します。

TCP経由のデータ送信

インターネットを介してデータを送信することは、複数のモジュールを使用して可能になります。ソケットモジュールは、他のコンピュータやプロセスからのデータの送受信を担う、基礎となるオペレーティングシステムオペレーションへの低レベルのアクセスを提供します。

次のコードは、ホストlocalhost上のポート6667で待機しているTCPサーバーにバイト文字列b'Hello'送信し、終了時に接続を閉じます。

from socket import socket, AF_INET, SOCK_STREAM
s = socket(AF_INET, SOCK_STREAM)
s.connect(('localhost', 6667))  # The address of the TCP server listening
s.send(b'Hello')
s.close()

ソケット出力はデフォルトでブロックされています。つまり、プログラムは接続が完了するまで待機し、アクションが完了するまで呼び出しを待機します。接続の場合、サーバーが実際に接続を受け入れることを意味します。送信の場合、オペレーティングシステムにデータをキューイングして後で送信するのに十分なバッファスペースがあることを意味します。

ソケットは使用後は常に閉じてください。

マルチスレッドTCPソケットサーバー

引数なしで実行すると、このプログラムはポート5000 127.0.0.1への接続をリスンするTCPソケットサーバーを開始します。サーバーは、別のスレッドで各接続を処理します。

-c引き数を指定して実行すると、このプログラムはサーバーに接続し、クライアント・リストを読み取り、それを出力します。クライアントリストはJSON文字列として転送されます。クライアント名は、 -n引数を渡すことで指定できます。異なる名前を渡すことにより、クライアントリストへの影響が観察されることがあります。

client_list.py

import argparse
import json
import socket
import threading

def handle_client(client_list, conn, address):
    name = conn.recv(1024)
    entry = dict(zip(['name', 'address', 'port'], [name, address[0], address[1]]))
    client_list[name] = entry
    conn.sendall(json.dumps(client_list))
    conn.shutdown(socket.SHUT_RDWR)
    conn.close()

def server(client_list):
    print "Starting server..."
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(('127.0.0.1', 5000))
    s.listen(5)
    while True:
        (conn, address) = s.accept()
        t = threading.Thread(target=handle_client, args=(client_list, conn, address))
        t.daemon = True
        t.start()

def client(name):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('127.0.0.1', 5000))
    s.send(name)
    data = s.recv(1024)
    result = json.loads(data)
    print json.dumps(result, indent=4)

def parse_arguments():
    parser = argparse.ArgumentParser()
    parser.add_argument('-c', dest='client', action='store_true')
    parser.add_argument('-n', dest='name', type=str, default='name')
    result = parser.parse_args()
    return result

def main():
    client_list = dict()
    args = parse_arguments()
    if args.client:
        client(args.name)
    else:
        try:
            server(client_list)
        except KeyboardInterrupt:
            print "Keyboard interrupt"

if __name__ == '__main__':
    main()

サーバー出力

$ python client_list.py
Starting server...

クライアント出力

$ python client_list.py -c -n name1
{
    "name1": {
        "address": "127.0.0.1", 
        "port": 62210, 
        "name": "name1"
    }
}

受信バッファは1024バイトに制限されています。クライアントリストのJSON文字列表現がこのサイズを超えると、切り捨てられます。これにより、次の例外が発生します。

ValueError: Unterminated string starting at: line 1 column 1023 (char 1022)

Linux上のRawソケット

まず、ネットワークカードの自動チェックサム機能を無効にします。

sudo ethtool -K eth1 tx off

次に、SOCK_RAWソケットを使用してパケットを送信します。

#!/usr/bin/env python
from socket import socket, AF_PACKET, SOCK_RAW
s = socket(AF_PACKET, SOCK_RAW)
s.bind(("eth1", 0))

# We're putting together an ethernet frame here, 
# but you could have anything you want instead
# Have a look at the 'struct' module for more 
# flexible packing/unpacking of binary data
# and 'binascii' for 32 bit CRC
src_addr = "\x01\x02\x03\x04\x05\x06"
dst_addr = "\x01\x02\x03\x04\x05\x06"
payload = ("["*30)+"PAYLOAD"+("]"*30)
checksum = "\x1a\x2b\x3c\x4d"
ethertype = "\x08\x01"

s.send(dst_addr+src_addr+ethertype+payload+checksum)


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow