TCP通信 python整理

# はじめに

TCPを利用したソケット通信について、整理しておきます。

# 基礎用語
## 1. クライアントソケットの利用手順

1. ソケットの作成 :  ``` socket()```
1. ソケットの設定とサーバーへの接続：``` connect()```
1. データのやり取り: ``` send()```, ``` recv()```
1. コネクションをクローズする：``` close()```

## 2. サーバーソケットの利用手順

1. ソケットの作成 :  ``` socket()```
1. アドレスとポート番号の設定：``` bind()```
1. 接続の待ち受け: ```listen() ```
1. 通信用ソケットの取得:```accept() ```
1. データのやり取り: ``` send()```, ``` recv()```
1. コネクションをクローズする：``` close()```


#Pythonでの利用

## 1. ホスト名からIPアドレス取得プログラム

socketモジュール
socket.gethostbyname()を利用

```Python
import socket

hostname = 'www.qiita.com'
IP_address = socket.gethostbyname(hostname)
print(f'IP address of {hostname} is {IP_address}')
```

```Python:Result
IP address of www.qiita.com is 54.249.74.29

Process finished with exit code 0

```

## 2. クライアント側のソケット実装

基本的なクライアントプログラムを実装します。
1. ネットワーク層：IPv4
1. トランスポート層:TCP

```Python
5000番ポートでサーバーに接続します。
使い方：python client_socket_IP4.py
```

```Python:client_socket_IP4.py

'''

This program shows the way how to use
 socket client in python.

'''

#Process
#01. Preparing Socket : socket()
#02. Configuring Soccket and Connect to the Server : connect()
#03. Data　Yaritori : send(), recv()
#04. Closing the connection : close()

import socket

HOST = '192.168.0.164'
PORT = 5000
BUFSIZE = 4096

#01. Preparing Socket : socket()
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#socket.AF_INET : IPv4
#socket.SOCK_STREAM : TCP

#02. Configuring Soccket and Connect to the Server : connect()
client.connect((HOST,PORT))

#03. Data　Yaritori : send(), recv()
data = client.recv(BUFSIZE)
print(data.decode('UTF-8'))

#04. Closing the connection : close()
client.close()

```


## 3. サーバー側のソケット実装

### 3.1.一回だけクライアントからの接続を受け付けてすぐに終了するサーバープログラム

1. ネットワーク層：IPv4
1. トランスポート層:TCP

実装手順
1. ソケットの作成 :  ``` socket()```
1. アドレスとポート番号の設定：``` bind()```
1. 接続の待ち受け: ```listen() ```
1. 通信用ソケットの取得:```accept() ```
1. データのやり取り: ``` send()```, ``` recv()```
1. コネクションをクローズする：``` close()```


```Python:server_single_connection.py
'''

This program shows the way how to make your pc
 into server with socket in python.

'''

#-----------------------------------------------
#Process
#01. Socket Making : socket()
#02. Address & Port : bind()
#03. Waiting the connection : listen()
#04. Getting the socket : accept()
#05. Data Yaritori : send(), recv()
#06. Closing the connection()
#-----------------------------------------------

import socket

#Port Number
PORT = 5000

#Main
#01. Socket Making : socket()
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#02. Address & Port : bind()
server.bind(("", PORT))
#03. Waiting the connection : listen()
server.listen()
#04. Getting the socket : accept()
client, addr = server.accept()
#05. Data Yaritori : send(), recv()
client.sendall(b"Hi, nice to meet you!\n") #messeage
#06. Closing the connection()
client.close()
server.close()
```
### 3.2.繰り返して接続を受け付けてサーバープログラム

1. ネットワーク層：IPv4
1. トランスポート層:TCP

実装手順
1. ソケットの作成 :  ``` socket()```
1. アドレスとポート番号の設定：``` bind()```
1. 接続の待ち受け: ```listen() ```
1. while True
    1. 通信用ソケットの取得:```accept() ```
    1. データのやり取り: ``` send()```, ``` recv()```
    1. コネクションをクローズする：``` close()```

```Python:server_continous_connection.py
'''

This program shows the way how to make your pc
 into server with socket in python.

 Ctrl + Break : quit

'''

#-----------------------------------------------
#Process
#01. Socket Making : socket()
#02. Address & Port : bind()
#03. Waiting the connection : listen()
#04. Getting the socket : accept()
#05. Data Yaritori : send(), recv()
#06. Closing the connection()
#-----------------------------------------------
import datetime
import socket


PORT = 5000
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'

#01. Socket Making : socket()
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#02. Address & Port : bind()
# server.bind(("", PORT))
server.bind(ADDR)
#03. Waiting the connection : listen()
server.listen()


try:
    while True :
        print(f'[NEW Connection] {ADDR} connected.')
        # 04. Getting the socket : accept()
        client, addr = server.accept()
        # 05. Data Yaritori : send(), recv()
        msg = str(datetime.datetime.now())
        client.sendall(msg.encode("UTF-8"))
        print(f'{msg} : 接続要求あり')
        print(client)
        client.close()

except KeyboardInterrupt:
    print('Finished!')

```

## 4. クライアントとサーバーが双方向の通信を行う。

### 4.1. 双方向の通信を行うクライアントプログラム

```Python:client_souhoukou.py
'''

This program shows the way how to use
 socket client in python.

'''

#Process
#01. Preparing Socket : socket()
#02. Configuring Soccket and Connect to the Server : connect()
#03. Data　Yaritori : send(), recv()
#04. Closing the connection : close()

import socket

HOST = '192.168.0.195'
PORT = 5000
BUFSIZE = 4096
FORMAT = 'utf-8'

#01. Preparing Socket : socket()
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#02. Configuring Soccket and Connect to the Server : connect()
client.connect((HOST,PORT))

#03. Data　Yaritori : send(), recv()
# サーバーへのメッセージ
msg = input('Input the msg to server:')
client.sendall(msg.encode(FORMAT))

# サーバーからのメッセージを受信
data = client.recv(BUFSIZE)
print('サーバーからのメッセージ')
print(data.decode(FORMAT))


#04. Closing the connection : close()
client.close()


```

### 4.2. 双方向の通信を行うクライアントプログラム

```Python:server_continous_souhoukou.py
'''

This program shows the way how to make your pc
 into server with socket in python.

 Ctrl + Break : quit

'''

#-----------------------------------------------
#Process
#01. Socket Making : socket()
#02. Address & Port : bind()
#03. Waiting the connection : listen()
#04. Getting the socket : accept()
#05. Data Yaritori : send(), recv()
#06. Closing the connection()
#-----------------------------------------------
import datetime
import socket


PORT = 5000
BUFSIZE = 4096
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'

#01. Socket Making : socket()
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#02. Address & Port : bind()
server.bind(ADDR)
#03. Waiting the connection : listen()
server.listen()

#


try:
    while True :
        print(f'[NEW Connection] {ADDR} connected.')

        # 04. Getting the socket : accept()
        client, addr = server.accept()

        # 05. Data Yaritori : send(), recv()
        # 確認
        msg = str(datetime.datetime.now())
        print(f'{msg} : 接続要求あり')
        print(client)

        #クライアントより受信
        data = client.recv(BUFSIZE)
        print(data.decode(FORMAT))

        #サーバーから送信
        client.sendall(msg.encode(FORMAT))

        # 06. Closing the connection()
        client.close()


except KeyboardInterrupt:
    print('Finished!')



```

## 4. サーバーの処理をマルチスレッド化する。

### 4.1. クライアント側プログラム

```Python:client_code.py

import socket

HEADER = 64
PORT = 5050
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
SERVER = "192.168.0.195"
ADDR = (SERVER, PORT)

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)

def send(msg):
    message = msg.encode(FORMAT)
    msg_length = len(message)
    send_length = str(msg_length).encode(FORMAT)
    send_length += b' ' * (HEADER - len(send_length))
    client.send(send_length)
    client.send(message)
    print(client.recv(2048).decode(FORMAT))

send("Hello World!")
input()
send("Hello Everyone!")
input()
send("Hello Tim!")

send(DISCONNECT_MESSAGE)

```
### 4.2. マルチスレッド化されたサーバー側プログラム

```Python:server_code.py
import socket
import threading

HEADER = 64
PORT = 5050
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)


def handle_client(conn, addr):
    print(f"[NEW CONNECTION] {addr} connected.")

    connected = True
    while connected:
        msg_length = conn.recv(HEADER).decode(FORMAT)
        if msg_length:
            msg_length = int(msg_length)
            msg = conn.recv(msg_length).decode(FORMAT)
            if msg == DISCONNECT_MESSAGE:
                connected = False

            print(f"[{addr}] {msg}")
            conn.send("Msg received".encode(FORMAT))

    conn.close()


def start():
    server.listen()
    print(f"[LISTENING] Server is listening on {SERVER}")
    while True:
        conn, addr = server.accept()
        thread = threading.Thread(target=handle_client, args=(conn, addr))
        thread.start()
        print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")


print("[STARTING] server is starting...")
start()
```
# 追加勉強資料
1. threadingより非同期処理の場合、こちらが参考になります。
https://qiita.com/sand/items/4f27e62c7491b8e85d12