如何保持套接字打开直到客户端关闭?

21 投票
3 回答
43513 浏览
提问于 2025-04-17 08:58

我有一个简单的Python服务器和客户端。

服务器代码:

import SocketServer
import threading


class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print str(self.client_address[0]) + " wrote: "
        print self.data
        self.request.send(self.data.upper())


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

客户端代码:

import socket
import sys
from time import sleep

HOST, PORT = "localhost", 3288
data = "hello"

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    sock.connect((HOST, PORT))
    sock.send(data + "\n")
    received = sock.recv(1024)

    sleep(10)

    sock.send(data + "\n")
    received = sock.recv(1024)

    sleep(10)

    sock.send(data + "\n")
    received = sock.recv(1024)

finally:
    sock.close()

这是我得到的输出:

服务器输出:

>python server.py
127.0.0.1 wrote:
hello

客户端输出:

>python client.py
Traceback (most recent call last):
  File "client.py", line 18, in <module>
    received = sock.recv(1024)
socket.error: [Errno 10053] An established connection was aborted by the software in your host machine

我在一台Linux机器上也试过。服务器只接收到一条消息,然后在接收第二条消息时出现了错误。我刚开始学习Python的网络编程,但我觉得服务器可能因为某种原因关闭了连接。请问我该怎么解决这个问题呢?

3 个回答

0

这里有一个类似的问题,链接是 错误: [Errno 10053]

我也尝试了同样的事情,结果得到了相同的错误。

如果有一个简单的代码可以演示这个错误:

import socket 

host = 'localhost' 
port = 5001 
size = 102400
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect((host,port))
for msg in ['Hello, world','Test','anything goes here']:
    s.send(msg)
    data = s.recv(size)
    print 'Received:', data 
s.close()  

你创建一个套接字对象,然后看看它能发送多少数据,并从服务器回显回来,看看它接收了多少。如果你改变这个数量,比如从1024改到102400(在这段代码中);这意味着套接字不应该关闭,但在我的Windows操作系统上,服务器端一直在监听并打印客户端发送的任何数据,但在客户端你却会收到这个错误。

不过,如果客户端只能连接一次并且只能发送和接收一次,那就是它的设计方式。尝试这个代码就不会有任何错误:

for msg in ['Hello, world','Test','anything goes here']:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    s.connect((host,port))
    s.send(msg)
    data = s.recv(size)
    s.close()
    print 'Received:', data

我不确定一个套接字对象是否只能发送和接收一次数据。

更新 我认为问题出在每个客户端套接字接收数据的能力上,这取决于固定的缓冲区大小; 这就是为什么上面的第二段代码可以工作,从而在服务器上创建新的客户端连接套接字。但这样会消耗很多套接字。

相反,以下代码通过检查使用的大小来解决了这个问题。如果超过了给定的数量,它会在客户端创建一个新的套接字,但确保消息被发送;实际上,问题出在服务器代码上,但我修复了它。

size = 10

这是一个简单的代码尝试。我相信你会理解并优化它,让它更好!

客户端代码:

messag = ['Hello, world', 'Test', 'anything goes here']

def client_to_server(messag,host,port,size):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    countmsg = 0
    restmsg = ''
    for msg in messag:
        strl = tmsg = msg
        if len(restmsg):
            tmsg = restmsg + ' ' + msg
        countmsg = len(tmsg)
        if countmsg <= size:
            pass
        else:
            restmsg = tmsg[size:]
            tmsg = tmsg[:size]
            #s.close()
            countmsg = len(tmsg)
            #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
            #s.connect((host, port))
        print 'Sending to server msg {}'.format(tmsg)
        s.send(tmsg)
        # s.settimeout(1)
        try: 
            data = s.recv(size)
            print 'Received:', data
            if strl == data:
                print strl,data
                countmsg = 0
                restmsg = ''
        except (socket.error), e: 
            print e.args,e.message
            s.close()
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
            s.connect((host, port))
    s.close()
    if restmsg: 
        client_to_server([restmsg],host,port,size)
    return


client_to_server(messag,host,port,size)

服务器代码:

size = 1024  #This has to be bigger than client buf size!
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port)) 
s.listen(backlog)
while True:
        #this is what accepts and creates a P2P dedicated client socket per socket
        client, address = s.accept()
        try: 
            data = client.recv(size)
            while data or 0:
                print "Client sent {} | Server sending data to client address {}".format(data, address)
                client.send(data)
                data = client.recv(size)
            else: client.close()
        except (socket.error), e: 
            client.close()
            print e.args, e.message

试试看。这使用的是同一个套接字。

1

我首先要承认,自从我上次使用过 SocketServer 已经过去很多年了,所以可能有更合适的方法来解决你的问题。

注意,你的客户端打开了一个连接,然后发送了三组数据,也接收了三组数据。(希望 TCP 协议栈在你调用 receive() 方法时,会把缓冲的数据一次性发送出去。)

你的服务器期望能够完整处理一个客户端的连接,从开始到结束,这个过程是通过 SocketServer 的回调机制来实现的。你现在的类只做了一点输入输出操作,然后就 结束 了。你只需要扩展你的服务器回调,让它做更多的事情:

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print str(self.client_address[0]) + " wrote: "
        print self.data
        self.request.send(self.data.upper())
        foo = self.request.recv(1024).strip()
        self.request.send(foo.lower())
        bar = self.request.recv(1024).strip()
        self.request.send("goodbye " + bar)
33

每当有新的连接时,系统会创建一个 MyTcpHandler 对象来处理这个连接,然后会调用 handle 方法来和客户端进行交流。当 handle 方法执行完毕后,连接就会被关闭,所以你需要在 handle 方法里完成和客户端的所有沟通。

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        while 1:
            self.data = self.request.recv(1024)
            if not self.data:
                break
            self.data = self.data.strip()
            print str(self.client_address[0]) + " wrote: "
            print self.data
            self.request.send(self.data.upper())

注意:当客户端关闭连接时,recv 方法会返回 ''(空字符串),所以我把 .strip() 移到了 recv 之后,这样就不会因为客户端只发送了空格而误报了。

撰写回答