首页 > 解决方案 > 套接字服务器未在 recv() 上暂停。立即读取 0 字节并关闭

问题描述

我有一个服务器客户端设置,其工作方式如下:

  1. 客户端连接到服务器。
  2. 客户端向服务器发送一条 64 字节的消息,告诉服务器要读取多少数据
  3. 服务器读取那么多字节的数据,做出响应,然后重复该过程。
  4. 当客户端完成后,它会向服务器发送一个空消息
  5. 服务器看到消息长度为0并关闭连接。

这似乎适用于第一遍。但是,在服务器响应之后,它不会等待客户端发送更多数据。相反,服务器立即读取 64 个字节。由于客户端没有响应,所以消息长度为0,连接关闭。

我不确定为什么在客户端发送更多数据之前服务器不会暂停。

这是服务器循环:

        self.__stop = False
        while not self.__stop:
            if self.client_sock:
                # Check if the client is still connected and if data is available
                try:
                    rdy_read, rdy_write, sock_err = select.select(
                        [self.client_sock, ], [self.client_sock, ], [], 5)
                except select.error as err:
                    self.stop()
                    return

                if len(rdy_read) > 0:
                    # msg length will be sent as bytes
                    read_data = self.client_sock.recv(64)

                    # Check if the socket has been closed
                    if read_data == 0:
                        self.stop()
                    else:
                        msg_length = int(read_data)
                        msg = self.client_sock.recv(msg_length)

                        response = f"{[12, 15, 66]}\n"

                        msg_size = padded_size_of_msg(response)
                        self.client_sock.send(msg_size)
                        self.client_sock.send(f"{response}".encode('utf-8'))

            else:
                print(
                    f"[THREAD {self.number}] No client connected.")
                self.stop()
        self.close()

函数 padded_size_of_msg() 用于计算消息的长度,将该数字填充为 64 字节,然后将其发送给客户端:

def padded_size_of_msg(msg):
    msg_length = len(msg)
    send_length = str(msg_length).encode('utf-8')
    send_length += b' ' * (64- len(send_length))
    return send_length

完整的类声明如下:

class ServerSocketThread(Thread):
    def __init__(self, client_sock, client_addr, number):
        Thread.__init__(self)
        self.client_sock = client_sock
        self.client_addr = client_addr
        self.number = number

    def run(self):

        self.__stop = False
        while not self.__stop:
            if self.client_sock:
                # Check if the client is still connected and if data is available
                try:
                    rdy_read, rdy_write, sock_err = select.select(
                        [self.client_sock, ], [self.client_sock, ], [], 5)
                except select.error as err:
                    print(
                        f"[THREAD {self.number}] Select() failed on socket with {self.client_addr}")
                    self.stop()
                    return

                if len(rdy_read) > 0:
                    # msg length will be sent as bytes
                    read_data = self.client_sock.recv(64)

                    # Check if the socket has been closed
                    if read_data == 0:
                        print(
                            f"[THREAD {self.number}] {self.client_addr} closed the socket")
                        self.stop()
                    else:
                        msg_length = int(read_data)
                        # Client will send msg as bytes. No need to decode to str
                        msg = self.client_sock.recv(msg_length)

                        response = f"{[12, 15, 66]}\n"

                        # Send outputs to client as bytes
                        msg_size = padded_size_of_msg(response)
                        self.client_sock.send(msg_size)
                        self.client_sock.send(f"{response}".encode('utf-8'))

            else:
                print(
                    f"[THREAD {self.number}] No client connected.")
                self.stop()
        self.close()

    def stop(self):
        self.__stop = True

    def close(self):
        if self.client_sock:
            print(
                f"[THREAD {self.number}] Closing conn with {self.client_addr}")
            self.client_sock.close()

标签: pythonsocketsclient-server

解决方案


这最终成为从命令行使用 nc 的问题。

如果我尝试从客户端发送“测试”,它会将大小注册为 4 字节并告诉服务器读取 4 字节。从终端,nc 将改为发送“test\n”。换行符 '\n' 不会被读取,而是在队列中等待。当调用第二个 recv() 时,它立即读取 '\n' 并将其作为关闭连接的指示。


推荐阅读