【问题标题】:Yet Another 'Connection reset by peer' Error另一个“对等方重置连接”错误
【发布时间】:2014-07-21 21:13:58
【问题描述】:

我正在使用 socket 模块在 python 中创建一个服务器/客户端应用程序,无论出于何种原因,我的服务器一直在结束连接。奇怪的是,这在 Windows 上完美运行,但在 Linux 上却不行。我到处寻找可能的解决方案,但都没有奏效。下面是利用漏洞的代码的净化版本,但是成功率更高。通常它永远不会起作用。希望这仍然是足够的信息。谢谢!

服务器:

import logging
import socket
import threading
import time

def getData():
    HOST = "localhost"
    PORT = 5454

    while True:
        s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
        s.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) #because linux doesn't like reusing addresses by default

        s.bind( ( HOST, PORT ) )
        logging.debug( "Server listens" )
        s.listen( 5 )
        conn, addr = s.accept()
        logging.debug( "Client connects" )
        print "Connected by,", addr

        dataRequest = conn.recv( 1024 )
        logging.debug( "Server received message" )

        time.sleep( .01 ) #usually won't have to sample this fast

        data = """Here is some data that is approximately the length 
of the data that I am sending in my real server. It is a string that
doesn't contain any unordinary characters except for maybe a tab."""

        if not timeThread.isAlive(): #lets client know test is over
            data = "\t".join( [ data, "Terminate" ] )
            conn.send( data )
            s.close()
            print "Finished"
            print "Press Ctrl-C to quit"
            break
        else:
            logging.debug( "Server sends data back to client" )
            conn.send( data )

        logging.debug( "Server closes socket" )
        s.close()

def timer( t ):
    start = time.time()
    while ( time.time() - start ) < t:
        time.sleep( .4 )
        #sets flag for another thread not here

def main():
    global timeThread

    logging.basicConfig( filename="test.log", level=logging.DEBUG )

    #time script runs for
    t = 10 #usually much longer (hours)

    timeThread = threading.Thread( target=timer, args=( t, ) )
    dataThread = threading.Thread( target=getData, args=() )
    timeThread.start()
    dataThread.start()

    #just for testing so I can quit threads when sockets break
    while True:
        time.sleep( .1 )

    timeThread.join()
    dataThread.join()

if __name__ == "__main__":
    main()

客户:

import logging
import socket

def getData():
    dataList = []
    termStr = "Terminate"

    data = sendDataRequest()
    while termStr not in data:
        dataList.append( data )
        data = sendDataRequest()
    dataList.append( data[ :-len( termStr )-1 ] )

def sendDataRequest():
    HOST = "localhost"
    PORT = 5454

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

    while True:
        try:
            s.connect( ( HOST, PORT ) )
            break
        except socket.error:
            print "Connecting to server..."

    logging.debug( "Client sending message" )
    s.send( "Hey buddy, I need some data" ) #approximate length

    try:
        logging.debug( "Client starts reading from socket" )
        data = s.recv( 1024 )
        logging.debug( "Client done reading" )
    except socket.error, e:
        logging.debug( "Client throws error: %s", e )

    print data

    logging.debug( "Client closes socket" )
    s.close()

    return data

def main():
    logging.basicConfig( filename="test.log", level=logging.DEBUG )
    getData()

if __name__ == "__main__":
    main()

编辑:添加回溯

Traceback (most recent call last):
    File "client.py", line 39, in <moduel>
        main()
    File "client.py", line 36, in main
        getData()
    File "client.py", line 10, in getData
        data = sendDataRequest()
    File "client.py", line 28, in sendDataRequest
        data = s.recv( 1024 )
socket.error: [Errno 104] Connection reset by peer

编辑:添加调试

DEBUG:root:Server listens
DEBUG:root:Client sending message
DEBUG:root:Client connects
DEBUG:root:Client starts reading from socket
DEBUG:root:Server received message
DEBUG:root:Server sends data back to client
DEBUG:root:Server closes socket
DEBUG:root:Client done reading
DEBUG:root:Server listens
DEBUG:root:Client sending message
DEBUG:root:Client connects
DEBUG:root:Client starts reading from socket
DEBUG:root:Server received message
DEBUG:root:Server sends data back to client
DEBUG:root:Client done reading
DEBUG:root:Client sending message
DEBUG:root:Client starts reading from socket
DEBUG:root:Server closes socket
DEBUG:root:Client throws error: [Errno 104] Connection reset by peer
DEBUG:root:Server listens

汤姆的理论似乎是正确的。我会尝试弄清楚如何更好地关闭连接。

这没有解决,但接受的答案似乎指出了问题。

编辑:我尝试使用 Tom 的 getData() 函数,但看起来服务器仍然过早关闭连接。应该是可重复的,因为我也无法让它在 Windows 中工作。

服务器输出/回溯:

Connected by, ('127.0.0.1', 51953)
Exception in thread Thread-2:
Traceback (most recent call last):
    File "/usr/lib64/python2.6/threading.py", line 532, in __bootstrap_inner
        self.run()
    File "/usr/lib64/python2.6/threading.py", line 484, in run
        self.__target(*self.__args, **self.__kwargs)
    File "server.py", line 15, in getData
        s.bind( ( HOST, PORT ) )
    File "<string>", line 1, in bind
error: [Errno 22] Invalid argument

客户端输出/回溯:

Here is some data that is approximately the length
of the data that I am sending in my real server. It is a string that
doesn't contain any unordinary characters except for maybe a tab.
Traceback (most recent call last):
    File "client.py", line 49, in <moduel>
        main()
    File "client.py", line 46, in main
        getData()
    File "client.py", line 11, in getData
        data = sendDataRequest()
    File "client.py", line 37, in sendDataRequest
        print data
UnboundLocalError: local variable 'data' referenced before assignment

日志:

DEBUG:root:Server listens
DEBUG:root:Client sending message
DEBUG:root:Client connects
DEBUG:root:Client starts reading from socket
DEBUG:root:Server received message
DEBUG:root:Server sends data back to client
DEBUG:root:Server closes connection
DEBUG:root:Client done reading
DEBUG:root:Client closes socket
DEBUG:root:Client sending message
DEBUG:root:Client starts reading from socket
DEBUG:root:Client throws error: [Errno 104] Connection reset by peer

更新:我使用了 Tom 的 getData() 函数,但将 s.bind() 移动到循环之前并让它工作。老实说,我不知道为什么会这样,所以如果有人能解释为什么服务器关闭它的客户端套接字是安全的,但当它关闭它的服务器套接字时却不是这样,那就太酷了。谢谢!

【问题讨论】:

  • 您是否 100000% 肯定这不是实际的连接问题? tcpdump/wireshark 说什么?你能用nc测试连接吗?
  • 您能否提供更具体的信息,说明在您遇到错误之前服务器和客户端都走了多远 - 以及客户端和服务器的确切错误/堆栈跟踪是什么?
  • @admdrew 我不明白这可能是与 localhost 的连接问题,但我可以在几分钟内得到它
  • @TomDalton 当然,我会在几分钟后发布。至于多远,一般是5秒。有时它会完成(10 秒),但不会连续多次。
  • 您服务器的while True 循环似乎一直在打开一个新套接字,而客户端没有任何信号表明套接字已完成或客户端已完成读取数据 - 这是故意的吗?

标签: python


【解决方案1】:

虽然我无法重现此问题(在 Windows 7 64 位 Python 2.7 上),但我最好的猜测是发生了以下情况:

  • 服务器监听
  • 客户端连接
  • 客户端发送“嘿,伙计,我需要一些数据”
  • 服务器接收到这个
  • 服务器将数据发送回客户端
  • 服务器关闭套接字
  • 客户端尝试读取socket,发现已关闭
  • 客户端抛出“对等连接重置”错误。

您从客户端添加的堆栈跟踪似乎支持这一理论。是否有可能通过一些额外的日志记录或类似来证明情况并非如此?

其他注意事项: 如果您的客户端在它接收到的第一个数据中没有找到终止字符串,它会打开一个到服务器的新套接字。这在我看来是错误的 - 你应该从同一个套接字读取数据,直到你拥有所有数据。

编辑:结合更多的东西:

在您的示例日志输出中,您尚未更新代码,因此我看不到每个日志行的来源。但是,看起来您有 2 个并行运行的客户端(可能在不同的进程或线程中?),这会导致:

我刚刚注意到最后一件事。在https://docs.python.org/2/library/socket.html#example 的示例中,服务器不会关闭套接字,它会关闭通过侦听套接字生成的连接。可能是您有 2 个客户端连接到同一个服务器套接字实例,当您关闭服务器套接字时,您实际上断开了两个连接的客户端,而不仅仅是第一个。如果您正在运行多个客户端,则记录某种身份,例如。 DEBUG:root:Client(6) done reading 可能有助于证明这一点。

对于服务器的数据线程主循环,您能否尝试以下操作,将显示问题是否与关闭侦听套接字而不是连接的套接字有关:


def getData():
    HOST = "localhost"
    PORT = 5454

    s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
    # s.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) #because linux doesn't like reusing addresses by default
    s.bind( ( HOST, PORT ) )
    logging.debug( "Server listens" )
    s.listen( 5 )

    while True:

        conn, addr = s.accept()
        logging.debug( "Client connects" )
        print "Connected by,", addr

        dataRequest = conn.recv( 1024 )
        logging.debug( "Server received message" )

        time.sleep( .01 ) #usually won't have to sample this fast

        data = """Here is some data that is approximately the length 
of the data that I am sending in my real server. It is a string that
doesn't contain any unordinary characters except for maybe a tab."""

        if not timeThread.isAlive(): #lets client know test is over
            data = "\t".join( [ data, "Terminate" ] )
            conn.send( data )
            conn.close()
            print "Finished"
            print "Press Ctrl-C to quit"
            break
        else:
            logging.debug( "Server sends data back to client" )
            conn.send( data )

        logging.debug( "Server closes connection" )
        conn.close()

【讨论】:

  • 我可以试试,你想要什么?我不能做 tcpdump 或 wireshark 但我可以试试 nc 命令。
  • 你看过在 python 中使用logging 包吗?它是开箱即用的线程安全的。
  • 我没有,但我可以看看。
  • 您在寻找什么?您是否只想让我在建立连接并使用时间戳发送/接收数据时记录?
  • 好的,我在OP中添加了日志的结尾。你似乎是对的。
【解决方案2】:

我在这里超出了我的深度,但正在调查一个可能相关的问题(Linux 上的间歇性“对等连接重置”错误,在 Windows 上工作正常),我遇到了http://scie.nti.st/2008/3/14/amazon-s3-and-connection-reset-by-peer/。我们那里有用的调试器 Garry Dolley 总结(在 2008 年!):

“Linux 内核 2.6.17+ 增加了 TCP 窗口/缓冲区的最大大小,如果它不能处理足够大的 TCP 窗口,这会导致其他设备出现故障。设备会重置连接,我们将其视为“对等方重置连接”消息。”

他给出了一个涉及 /etc/sysctl.conf 的解决方案。我还没有尝试过,但可能值得一看?

【讨论】:

    【解决方案3】:

    我遇到了类似的问题,我在发送端被对等方重置连接。原来它发生是因为在接收方的某个地方抛出了异常。因此,当脚本意外结束时,操作系统只会对该套接字上的连接进行 RST。这是一个相当古老的线程,但对于遇到类似线程问题的任何人,我的建议是:在尝试使其复杂之前确保它是单线程的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-07
      • 2017-06-09
      • 1970-01-01
      • 2020-12-22
      • 2017-12-22
      • 1970-01-01
      相关资源
      最近更新 更多