为什么这个套接字连接只允许发送和接收一次?

4 投票
1 回答
5350 浏览
提问于 2025-04-17 08:33

背景
我有一个简单的套接字服务器设置,想让它能够同时接收多个连接,并把数据回显给客户端。客户端这边启动了几个线程,每个线程都和服务器建立自己的连接。这在调用socket.send()时没问题,但后续的调用就会出现“连接被对方重置”或者“管道破裂”的错误。值得注意的是,我还没有找到导致重置和破裂的具体原因。我在StackOverflow上查找过解决方案,但我担心自己不知道该搜索什么。

我这样做是不是有问题,还是说我在设置上忽略了什么?

服务器

import SocketServer

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print "{} wrote: {}\n".format(self.client_address[0], self.data)
        self.request.send(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()

客户端

import socket
import sys
import threading
import time

HOST, PORT = "localhost", 9999
def create_client():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        cur_thread = threading.current_thread()
        sock.connect((HOST, PORT))
        for x in range(55):
            msg = "{}: {}\n".format(cur_thread.name, str(x))
            # Connect to server and send data
            print cur_thread.name + ": sending message\n"
            sock.send(msg)
            # Receive data from the server and shut down
            received = sock.recv(2048)
            print "RX:" + received
    finally:
        cur_thread = threading.current_thread()
        response = "{}: Closing!\n".format(cur_thread.name)
        print response
        sock.close()

if __name__ == "__main__":
    print "testing single thread"
    #create_client()
    print "starting threads"
    client_1 = threading.Thread(target=create_client)
    client_1.daemon = True
    client_1.start()
    client_2 = threading.Thread(target=create_client)
    client_2.daemon = True
    client_2.start()

    time.sleep(20)

1 个回答

9

当你从 handle 返回时,套接字就会关闭。你应该使用一个循环,只有在 self.data == '' 的时候才返回。客户端关闭连接时,recv 会返回零字节。此外,在检查返回值之前,不要对结果使用 strip(),否则可能会误判连接已经关闭。最后,使用 ThreadingTCPServer,否则服务器一次只能处理一个连接。

示例:

import SocketServer

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        while True:
            self.data = self.request.recv(1024)
            if self.data == '':
                break
            self.data = self.data.strip()
            print "{} wrote: {}\n".format(self.client_address[0], self.data)
            self.request.send(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()

另外要注意,send() 并不能保证发送所有的消息字节,所以最好使用 sendall() 或者检查返回值。recv() 也可能会有些麻烦。TCP/IP 是一种流式协议,没有消息边界的概念,因此你需要自己实现一个协议来检查是否收到了完整的消息。可能会发送 10000 字节,但接收到的却少于这个,需要多次接收才能获取完整的消息。也有可能发送两次消息,但在一次接收中接收到两条消息,甚至可能接收到一条消息的全部和另一条消息的一部分。对于你的例子,可以简单地将所有接收到的数据缓存起来,直到消息中出现 \n,这样就可以实现一个简单的协议。

撰写回答