What you should know about python sockets

https://mdnice.com/writing/8f86ec17792d43d7bb22190c5e9d3262
https://blog.csdn.net/baiydn/article/details/127253833

1. Short connection long connection

A short connection means that the socket client establishes a connection with the server, and closes the connection with the server immediately after sending and receiving data. If the next request is required, the server needs to be reconnected. Socket short connections are suitable for business scenarios where the interaction between the client and the server is not very frequent.
  A long connection means that after the socket connection is established, the connection is kept, and the connection is not closed, and data packets can be sent and received continuously. Socket persistent connections are suitable for business scenarios with frequent interactions between the client and the server and strong real-time performance.

1.1 server short connection

import socket

# 创建 socket 对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 绑定地址和端口号
server_socket.bind(('localhost', 8000))

# 监听端口号
server_socket.listen()

while True:
    # 等待客户端连接
    client_socket, addr = server_socket.accept()
    
    # 接收客户端请求
    request_data = client_socket.recv(1024)
    
    # 处理客户端请求,返回响应
    response_data = 'Hello, world!'.encode()
    
    # 发送响应给客户端
    client_socket.sendall(response_data)
    
    # 关闭连接
    client_socket.close()

1.2 server long connection (multiple client connections)

import socket

# 设置服务器信息
HOST = '127.0.0.1'
PORT = 8080
BUFFER_SIZE = 1024

# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 绑定地址
server_socket.bind((HOST, PORT))

# 监听端口
server_socket.listen(10)

# 服务器循环
while True:
    # 等待连接
    client_socket, address = server_socket.accept()
    print(f"Connected by {
      
      address}")

    # 设置连接为非阻塞模式
    client_socket.setblocking(False)

    # 定义缓冲区
    data = b''

    # 连接循环
    while True:
        try:
            # 读取数据
            recv_data = client_socket.recv(BUFFER_SIZE)
            if recv_data:
                # 添加数据到缓冲区
                data += recv_data
            else:
                # 连接已断开
                break
        except socket.error:
            # 未读取到数据
            pass

        # 处理数据
        if len(data) >= BUFFER_SIZE:
            # 数据分片,每次处理BUFFER_SIZE大小的数据
            data_list = [data[i:i + BUFFER_SIZE] for i in range(0, len(data), BUFFER_SIZE)]
            for chunk in data_list:
                # 处理数据
                process_data(chunk)

            # 清空缓冲区
            data = b''

        # 发送心跳包
        client_socket.sendall(b'ping')

    # 关闭连接
    client_socket.close()

2. Blocking and non-blocking (for long connections)

In Socket programming, blocking and non-blocking refer to whether the execution of the current thread or process will be blocked when reading or writing Socket data.

When a thread or process invokes a blocking Socket operation (such as reading or writing), the thread or process will wait until the operation completes or times out. During this time, the thread or process cannot perform other tasks.

The non-blocking Socket operation will not block the current thread or process, but immediately returns an error or no data, so that the thread or process can continue to perform other tasks.

In Socket programming, the use of non-blocking Socket operations can improve the concurrency and responsiveness of the program, because it allows a thread or process to handle multiple Socket connections at the same time without waiting for any Socket operation to complete.

3. Multithreading or select

Both using selectand using multithreading are ways to achieve concurrent network programming, and they each have advantages and disadvantages.

selectThe advantages of using are:

  1. Ability to handle multiple connections without creating multiple threads, reducing the overhead of thread creation and context switching;
  2. It can support non-blocking I/O operations, and realize asynchronous I/O operations through the blocking and ready state judgment of selectthe function ;
  3. I/O events of multiple sockets can be processed in one thread to reduce CPU usage.

The advantages of using multithreading are:

  1. For CPU-intensive tasks, multi-threading can effectively utilize multi-core CPUs and improve concurrent processing capabilities;
  2. When processing different requests, each thread is independent of each other and does not affect each other, improving concurrent processing capabilities;
  3. Using multithreading can simplify code implementation and increase readability.

It is necessary to select the appropriate concurrent programming method according to the specific application scenario. If it is an I/O-intensive task, using selectwill be more efficient; if it is a CPU-intensive task, using multithreading can achieve better performance.

4. Even package

The packet connection problem refers to that in network communication, due to the asynchronous speed of data sending and receiving, multiple data packets are merged into one data packet during transmission, resulting in the inability of the data receiver to correctly parse and process the data.

In the above code, due to the use of cyclic data transmission, multiple data packets are sent continuously in a short period of time, which may cause the flow control mechanism of the TCP connection, resulting in the merging of data packets. When multiple data packets are combined into one data packet and sent to the client, the data received by the client is not arranged in the original order, resulting in data confusion. In order to avoid this problem, a message boundary identifier can be used to add a specific identifier to each message so that the message can be correctly divided at the receiver. In the above code, the method of message boundary identifier has been used, but the problem of data packet merging still exists because no appropriate interval is added when continuously sending data.

5. Finally

# encoding=utf-8
import threading
import socket
import struct

class Serv:
    def __init__(self):
        self.client_socket_list=[]

    def tcp_server_start(self, ip, post):
        """
        功能函数,TCP服务端开启的方法
        :return: None
        """
        self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 取消主动断开连接四次握手后的TIME_WAIT状态
        self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        try:
            address = (ip, int(post))
            self.tcp_socket.bind(address)
            self.tcp_socket.listen()
            # 设定套接字为非阻塞式
            self.tcp_socket.setblocking(False)
            self.sever_th = threading.Thread(target=self.tcp_server_concurrency)
            self.sever_th.start()
            print('TCP服务端正在监听端口:%s\n' % str(ip))
        except Exception as ret:
            msg = '请检查IP,端口\n'
            print(msg)

    def tcp_server_concurrency(self):
        """
        功能函数,供创建线程的方法;
        使用子线程用于监听并创建连接,使主线程可以继续运行,以免无响应
        使用非阻塞式并发用于接收客户端消息,减少系统资源浪费,使软件轻量化
        :return:None
        """
        while True:
            try:
                client_socket, client_address = self.tcp_socket.accept()
                client_socket.setblocking(False)
            except Exception:
                pass  # 因为是非堵塞,所以有可能会出现socket异常的情况
            else:
                # 将创建的客户端套接字存入列表,client_address为ip和端口的元组
                self.client_socket_list.append((client_socket, client_address))
                msg = 'TCP服务端已连接IP:%s端口:%s\n' % client_address
                print(msg)
                # 轮询客户端套接字列表,接收数据
            for client, address in self.client_socket_list:
                try:
                    self.recv_msg(client)
                except Exception:
                    pass

    def tcp_send(self, msg):
        try:
            for client, address in self.client_socket_list:
                client.sendall(msg)
                # msg = 'TCP服务端已发送{}\n'.format(msg)
                # print(msg)
        except Exception as e:
            print(e)
            self.client_socket_list.remove((client, address))

    def handle_message(self,msg):
        # 处理接收到的消息
        print(len(msg))
        print(f"Received message: {
      
      msg}")

    def recv_msg(self,sock):
        # 接收数据并解析消息
        buffer_size = 1024  # 缓存区大小
        recv_buffer = b''  # 接收缓存区
        star_sequence = b'star'  # 消息开始标识
        stop_sequence = b'stop'  # 消息结束标识
        msg_start = False  # 标识消息开始
        while True:
            data = sock.recv(buffer_size)
            if not data:
                # 连接已断开
                break
            recv_buffer += data
            while True:
                if not msg_start:
                    star_pos = recv_buffer.find(star_sequence)
                    if star_pos >= 0:
                        # 找到消息开始标识
                        recv_buffer = recv_buffer[star_pos + len(star_sequence):]
                        msg_start = True
                    else:
                        break
                if msg_start:
                    stop_pos = recv_buffer.find(stop_sequence)
                    if stop_pos >= 0:
                        # 找到一个完整的消息
                        msg = recv_buffer[:stop_pos]
                        self.handle_message(msg)
                        recv_buffer = recv_buffer[stop_pos + len(stop_sequence):]
                        msg_start = False
                    else:
                        # 没有找到完整的消息
                        if len(recv_buffer) > buffer_size:
                            # 缓存区已满,报告解析失败
                            recv_buffer=b''
                            raise ValueError("Message too long")
                        break

se=Serv()
se.tcp_server_start('127.0.0.1',2000)

Guess you like

Origin blog.csdn.net/qq_33228039/article/details/130576783
Recommended