为什么主机中断连接?

17 投票
1 回答
41279 浏览
提问于 2025-04-15 14:34

我正在自学Python网络编程,想起之前学习线程的时候,看到过这个页面,于是我把上面的代码复制下来,更新到Python 3.1.1版本并运行,结果一切都很顺利。

然后我做了一些修改。我的目标是实现一个简单的功能:

  1. 客户端将一个整数进行“腌制”(也就是把它转换成一种可以传输的格式),然后发送给服务器。
  2. 服务器接收到这个“腌制”的整数,解腌制(还原成原来的整数),将它乘以2,然后再腌制后发送回客户端。
  3. 客户端接收到这个“腌制”(并且已经乘以2的)整数,解腌制,最后输出结果。

这是服务器的代码:

import pickle
import socket
import threading

class ClientThread(threading.Thread):
    def __init__(self, channel, details):
        self.channel = channel
        self.details = details
        threading.Thread.__init__ ( self )

    def run(self):
        print('Received connection:', self.details[0])
        request = self.channel.recv(1024)
        response = pickle.dumps(pickle.loads(request) * 2)
        self.channel.send(response)
        self.channel.close()
        print('Closed connection:', self.details [ 0 ])

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('', 2727))
server.listen(5)

while True:
    channel, details = server.accept()
    ClientThread(channel, details).start()

这是客户端的代码:

import pickle
import socket
import threading

class ConnectionThread(threading.Thread):
    def run(self):
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.connect(('localhost', 2727))

        for x in range(10):
            client.send(pickle.dumps(x))
            print('Sent:',str(x))
            print('Received:',repr(pickle.loads(client.recv(1024))))

        client.close()

for x in range(5):
    ConnectionThread().start()

服务器运行得很好,当我启动客户端时,它成功连接并开始发送整数,按预期接收到了加倍后的结果。然而,很快就出现了异常:

Exception in thread Thread-2:
Traceback (most recent call last):
  File "C:\Python30\lib\threading.py", line 507, in _bootstrap_inner
    self.run()
  File "C:\Users\Imagist\Desktop\server\client.py", line 13, in run
    print('Received:',repr(pickle.loads(client.recv(1024))))
socket.error: [Errno 10053] An established connection was aborted by the softwar
e in your host machine

服务器继续正常运行,能够接收连接;只有客户端崩溃了。这是什么原因呢?

补充:我用以下代码让客户端正常工作:

import pickle
import socket
import threading

class ConnectionThread(threading.Thread):
    def run(self):
        for x in range(10):
            client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            client.connect(('localhost', 2727))
            client.send(pickle.dumps(x))
            print('Sent:',str(x))
            print('Received:',repr(pickle.loads(client.recv(1024))))
            client.close()

for x in range(5):
    ConnectionThread().start()

不过,我还是不太明白发生了什么。这不就是反复打开和关闭连接吗?难道这没有时间限制吗(关闭连接后不应该马上再打开吗)?

1 个回答

11

你的客户端现在是正确的——你想要打开一个连接,发送数据,接收回复,然后再关闭这个连接。

最开始的错误是因为服务器在发送第一个回复后就关闭了连接,这导致客户端在尝试通过同一个连接发送第二条消息时收到了连接已关闭的提示。

不过,我还是不太明白 这是怎么回事。难道这不就是 一直开关连接吗?

没错。这样做是可以的,虽然不是效率最高的方式。

难道不应该对这个有时间限制吗 (你不应该在关闭连接后 这么快就能重新打开一个连接)?

你可以随便多快打开一个客户端连接,因为每次打开连接时,系统会给你一个新的本地端口号,这样连接之间就不会互相干扰。在上面的服务器代码中,它会为每个新的连接启动一个新的线程。

每个IP连接有四个部分(源地址、源端口、目标地址、目标端口),这个四元组(也就是所谓的quad)在每个连接中必须不同。对于客户端连接,除了源端口是固定的,其他的都是不变的,所以操作系统会为你处理这个。

打开服务器连接就麻烦一些——如果你想快速打开一个新的服务器连接,你需要了解一下 SO_REUSEADDR。

server.bind(('', 2727))

上面提到的内容需要你去了解一下 SO_REUSEADDR。

撰写回答