跳转至

Python 处理TCP连接

概要: 使用Python建立TCP服务,或者作为TCP客户端发送数据

创建时间: 2022.12.07 21:38:15

更新时间: 2022.12.07 22:33:05

建立TCP服务端

使用Python建立一个简单的TCP服务,大致分为三步走

  1. 创建一个socket对象作为TCP server,并绑定到指定端口
  2. 开启监听指定端口的连接,处理来自客户端的请求
  3. 关闭服务

实例化如下 server.py

Python
import socket

tcp_addr = ("0.0.0.0", 12345)

# 开启TCP服务,监听连接到本机 12345 端口的数据
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server.bind(tcp_addr)
tcp_server.listen()
print(f"TCP server, listening {tcp_addr}")

# 获取一条来自客户端的请求数据
client_conn, client_addr = tcp_server.accept()
data = client_conn.recv(10240).decode("utf-8")
print(f"Received TCP client data: {data}")

# 关闭TCP服务
tcp_server.close()
print(f"TCP server, has closed.")

解释

  1. "0.0.0.0"表示建立的TCP服务接收来自所有IPV4地址的请求
  2. AF_INET表示 Address Family 中的IPV4协议,同理 AF_INET6 表示IPV6协议
  3. SOCK_STREAM 表示我们建立的是TCP连接,同理 SOCK_DGRAM 是UDP连接

建立TCP客户端

如果需要使用Python通过TCP协议发送数据,与上面类似,也分为三步

  1. 创建一个socket对象作为TCP client,并连接到指定的TCP服务器(服务器IP和端口)
  2. 向TCP服务器请求发送或接收数据
  3. 关闭连接

实例化如下 client.py

Python
import socket

# 连接到本机的12345端口
tcp_addr = ("127.0.0.1", 12345)

# 建立与服务端的TCP连接
tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_client.connect(tcp_addr)

# 注意发送的数据必须是字节流,一般使用UTF-8格式
tcp_client.sendall("hello world".encode("utf-8"))

# 关闭与服务端的TCP连接
tcp_client.close()

同时处理多个客户端请求

如果需要TCP服务端同时处理多个客户端的请求,可以采取多线程的方式,对于一段时间不活动的客户端连接,可以主动关闭连接节省系统资源,代码实现如下

Python
import signal
import socket
import threading
import time
import typing


def handle_sigterm(*args):
    """接收中断信号,安全关闭tcp连接"""
    raise KeyboardInterrupt()


class TcpServer(object):
    def __init__(self, host: str = "0.0.0.0", port: int = 12345, timeout: int = 60):
        self.host = host
        self.port = port
        self.timeout = timeout
        self._addr = (host, port)
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connections = dict()

    def start(self):
        try:
            signal.signal(signal.SIGTERM, handle_sigterm)
            self._start()
        except KeyboardInterrupt:
            self.server.close()
            print(f"TCP server closed, bye.")

    def _start(self):
        self.server.bind(self._addr)
        self.server.listen()
        print(f"TCP server started, listening on {self._addr}")

        while True:
            client_conn, client_addr = self.server.accept()
            print(f"New TCP client connected, address: {client_addr}")
            client_thread = threading.Thread(
                target=self._handle_new_client, args=(client_conn, client_addr)
            )
            client_thread.start()
            self.connections[client_addr] = client_thread

    def _handle_new_client(
            self, client_socket: socket.socket, client_addr: typing.Tuple[str, int]
    ):
        last_communication = time.time()
        while time.time() - last_communication < self.timeout:
            if data := client_socket.recv(10240).decode("utf-8"):
                print(f"New data from client {client_addr}: {data}")
                last_communication = time.time()
            else:
                time.sleep(1)
        client_socket.close()
        print(f"TCP client {client_addr} closed due to timeout")
        self.connections.pop(client_addr)


if __name__ == "__main__":
    tcp_server = TcpServer()
    tcp_server.start()

参考