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

前回の記事と一緒にご覧ください。[Python3のソケットモジュールとソケット通信の流れ](https://qiita.com/Suzukaze31/items/a181016e8779ac118478)
python3のsocketモジュールを使った、TCP通信の簡単な例

#####環境
Python: `3.6`
必要とするモジュール: `colorama`

## 実行結果とソースコード

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

![server_side_img.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448552/ba361a0b-4095-2220-cc3d-ce34420de373.jpeg)



```Python3
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.')
```



### クライアント側


![client_side_img.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448552/32d46975-41d7-0b31-ef21-169397cb20b9.jpeg)


```Python3
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)
```