Socketモジュールを使ったTCP通信 - Python3


前回の記事と一緒にご覧ください。Python3のソケットモジュールとソケット通信の流れ
python3のsocketモジュールを使った、TCP通信の簡単な例

環境

Python: 3.6
必要とするモジュール: colorama

実行結果とソースコード

サーバー側(ソケット通信二回分)

import socket
from colored_print import print_msg


SERVER_ADDRESS = ('192.168.1.200', 8000)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(SERVER_ADDRESS)

sock.listen(0)

for i in range(2):
    try:
        conn_sock, client_address = sock.accept()
        print_msg('ac', 'The connection accepted.')
        print_msg('i', '{}:{} --------> {}:{}'
                  .format(client_address[0], client_address[1],
                          SERVER_ADDRESS[0], SERVER_ADDRESS[1]))

        # Receiving process
        amount_received = 0
        MSGLEN = int(conn_sock.recv(4))
        print_msg('i', 'MSGLEN: {}'.format(MSGLEN))

        while amount_received < MSGLEN:
            data = conn_sock.recv(min(MSGLEN - amount_received, 32))
            print_msg('i', 'received: {}'.format(data))
            amount_received += len(data)
            if not data:
                raise RuntimeError('The connected socket broken.')

            # Sending process
            conn_sock.send(data)
            print_msg('i', 'send: {}'.format(data))
    finally:
        conn_sock.close()
        print_msg('cl', 'The connection closed.')

クライアント側

import socket
from colored_print import print_msg


SERVER_ADDRESS = ('192.168.1.200', 8000)
MSG = 'Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do.'
MSGLEN = len(MSG)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    sock.connect(SERVER_ADDRESS)
    print_msg('cn', 'The connection accepted')

    print_msg('i', 'MSGLEN: {}'.format(MSGLEN))
    sock.send(bytes(str(MSGLEN), 'utf-8').zfill(4))

    # Sending process
    sock.send(bytes(MSG, 'utf-8'))

    # Receiving process
    chunks = []
    amount_received = 0
    while amount_received < MSGLEN:
        data = sock.recv(min(MSGLEN - amount_received, 32))
        print_msg('i', 'received: {}'.format(data))
        amount_received += len(data)
        if not data:
            raise RuntimeError('The connected socket broken.')
        chunks.append(data)
finally:
    sock.close()
    print_msg('cl', 'The connection closed.')

result = '''
Result: [
    MSGLEN: {},
    Send: {},
    Received: {}
]
'''.format(MSGLEN, MSG, chunks)

print_msg('i', result)

解説

recvのループのブレイクは、

  1. b''が来たらブレイクする。
  2. メッセージの長さがわかるなら受信分の長さがメッセージの長さと等しくなったらブレイクする。

のに種類があるが、1.の問題点は通信の途中で通信が壊れたときにもb''が渡されるということ。そこで今回は新しくはじめに送る4バイトはメッセージの長さであると言うルールを作った。

サーバー側

[ACCEPT]から[CLOSE]までが一つのソケット通信。

クライアントから来たメッセージを受け取ったらすぐに送り返す。

クライアント側

[CONNECT]から[CLOSE]までが一つのソケット通信。

colored_print

下のコードを同じディレクトリに配置してください。

colored_print.py
from colorama import Fore, Style


def print_msg(header, msg):
    '''header are i that is INFO or e that is ERROR'''

    if header == 'i':
        print(Fore.GREEN + '[INFO]',
              Style.RESET_ALL + msg)
    elif header == 'e':
        print(Fore.RED + '[ERROR]',
              Style.RESET_ALL + msg)
    elif header == 'ac':
        print(Fore.BLUE + '[ACCEPT]',
              Style.RESET_ALL + msg)
    elif header == 'cn':
        print(Fore.BLUE + '[CONNECT]',
              Style.RESET_ALL + msg)
    elif header == 'cl':
        print(Fore.BLUE + '[CLOSE]',
              Style.RESET_ALL + msg)
    else:
        print(Fore.RED + 'ERROR: header is an invalid value.'
              + Style.RESET_ALL)