为什么我看到“connection reset by peer”错误?

4
我正在使用Python 2.6.1在Mac OS X 10.5上测试cogen。我有一个简单的回显服务器和客户端泵,用于创建10,000个客户端连接进行测试。1000、5000等都非常出色。然而,在大约10,000个连接时,服务器开始随机丢弃客户端 - 客户端看到“对等方重置连接”。
这里是否有一些基本的网络背景知识我不知道?
请注意,我的系统已配置为处理打开的文件(launchctl限制、sysctl(maxfiles等)和ulimit -n都是有效的;已经尝试过)。此外,我已经验证cogen正在选择在底层使用kqueue。
如果我在客户端连接调用中添加了轻微的延迟,一切都很好。因此,我的问题是,当短时间内有高频率的连接时,为什么处于压力下的服务器会丢弃其他客户端?还有其他人遇到过这种情况吗?
为了完整起见,这是我的代码。
这是服务器:
# echoserver.py

from cogen.core import sockets, schedulers, proactors
from cogen.core.coroutines import coroutine
import sys, socket

port = 1200

@coroutine
def server():
    srv = sockets.Socket()
    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    addr = ('0.0.0.0', port)
    srv.bind(addr)
    srv.listen(64)
    print "Listening on", addr
    while 1:
        conn, addr = yield srv.accept()
        m.add(handler, args=(conn, addr))

client_count = 0

@coroutine
def handler(sock, addr):
    global client_count
    client_count += 1
    print "SERVER: [connect] clients=%d" % client_count
    fh = sock.makefile()
    yield fh.write("WELCOME TO (modified) ECHO SERVER !\r\n")
    yield fh.flush()
    try:
        while 1:
            line = yield fh.readline(1024)
            #print `line`
            if line.strip() == 'exit':
                yield fh.write("GOOD BYE")
                yield fh.close()
                raise sockets.ConnectionClosed('goodbye')
            yield fh.write(line)
            yield fh.flush()
    except sockets.ConnectionClosed:
        pass
    fh.close()
    sock.close()
    client_count -= 1
    print "SERVER: [disconnect] clients=%d" % client_count

m = schedulers.Scheduler()
m.add(server)
m.run()

这里是客户端:

# echoc.py

import sys, os, traceback, socket, time
from cogen.common import *
from cogen.core import sockets

port, conn_count = 1200, 10000
clients = 0

@coroutine
def client(num):
    sock = sockets.Socket()
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    reader = None
    try:
        try:
            # remove this sleep and we start to see 
            # 'connection reset by peer' errors
            time.sleep(0.001)
            yield sock.connect(("127.0.0.1", port))
        except Exception:
            print 'Error in client # ', num
            traceback.print_exc()
            return
        global clients
        clients += 1
        print "CLIENT #=%d [connect] clients=%d" % (num,clients)
        reader = sock.makefile('r')
        while 1:
            line = yield reader.readline(1024)
    except sockets.ConnectionClosed:
        pass
    except:
        print "CLIENT #=%d got some other error" % num
    finally:
        if reader: reader.close()
        sock.close()
        clients -= 1
        print "CLIENT #=%d [disconnect] clients=%d" % (num,clients)

m = Scheduler()
for i in range(0, conn_count):
    m.add(client, args=(i,))
m.run()

感谢任何信息!


在服务器端,srv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 让我可以达到16K个客户端连接...我意识到此时我已经用尽了临时端口:
net.inet.ip.portrange.hifirst: 49152 net.inet.ip.portrange.hilast: 65535
- z8000
我想,那么一切都很好了。如果有人能够向我解释为什么不使用TCP_NODELAY会导致连接断开,我将洗耳恭听。 - z8000
1
回复:临时端口...我能够启动第二个IP,并手动将客户端套接字绑定到每个IP上[1024,65535]的每个端口。我能够从运行服务器的同一台机器上连接超过80K个客户端。哇! - z8000
1个回答

8
Python的套接字I/O有时会受到对等方的连接重置影响。这与全局解释器锁和线程调度方式有关。我在此主题上写了一些博客文章作为参考。 time.sleep(0.0001)似乎是推荐的解决方案,因为它可以调整线程调度并允许套接字I/O完成。

那么,当我有随时可以连接的客户端时会发生什么?尽管我并不期望每秒1000个连接尝试,但在没有完全理解这一点之前,我绝不会投入生产。我想我可以使用iptables对连接进行速率限制。 - z8000
1
在大约10,000个连接时,服务器开始随机断开客户端。除此之外,我怀疑您的服务器实际上能够处理10,000个并发连接。您真的能够打开和服务10,000个文件吗?您真的能够执行10,000个数据库查询吗? - S.Lott
@2pence: 由于您的问题没有透露任何信息,我被迫做出假设。既然我被迫做出假设,那么我就没有可能正确地做出假设。请用新的事实更新您的问题。 - S.Lott
1
@2pence:抱歉。你的第一条评论表明你有其他问题,但缺少一些事实。这就是为什么我插话的原因。我很抱歉试图理解你的评论。如果它意味着模糊不清,我可能错过了重点。 - S.Lott
10
无效链接。您能详细说明解决方案吗? - diedthreetimes
1
http://www.itmaybeahack.com/homepage/iblog/architecture/C551260341/E20081031204203/index.html - Fergie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接