jjzjj

Python:SocketServer 意外关闭 TCP 连接

coder 2023-09-18 原文

我想实现一个向 Python 发送请求的 TCP/IP 网络客户端应用程序 SocketServer并期望得到回应。我已经开始使用官方 Python SocketServer sample code :

server.py:

#!/usr/bin/env python
# encoding: utf-8

import SocketServer

class MyTCPHandler(SocketServer.StreamRequestHandler):

    def handle(self):
        request  = self.rfile.readline().strip()
        print "RX [%s]: %s" % (self.client_address[0], request)

        response = self.processRequest(request)

        print "TX [%s]: %s" % (self.client_address[0], response)
        self.wfile.write(response)

    def processRequest(self, message):
        if   message == 'request type 01':
            return 'response type 01'
        elif message == 'request type 02':
            return 'response type 02'

if __name__ == "__main__":
    server = SocketServer.TCPServer(('localhost', 12345), MyTCPHandler)
    server.serve_forever()

client.py:

#!/usr/bin/env python
# encoding: utf-8

import socket

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

try:
    sock.connect(('127.0.0.1', 12345))

    data = 'request type 01'
    sent = sock.sendall(data + '\n')
    if sent == 0:
        raise RuntimeError("socket connection broken")

    received = sock.recv(1024)
    print "Sent:     {}".format(data)
    print "Received: {}".format(received)

    data = 'request type 02'
    sent = sock.sendall(data + '\n')
    if sent == 0:
        raise RuntimeError("socket connection broken")

    received = sock.recv(1024)
    print "Sent:     {}".format(data)
    print "Received: {}".format(received)

except Exception as e:
    print e

finally:
    sock.close()

server.py 输出:

RX [127.0.0.1]: request type 01
TX [127.0.0.1]: response type 01

client.py 输出:

Sent:     request type 01
Received: response type 01
[Errno 54] Connection reset by peer

哪里做错了?似乎服务器正在关闭连接。我怎样才能让它保持打开状态?

注意:这是 C++/Qt: QTcpSocket won't write after reading 的后续问题

更新(在 abarnert's answer 之后):

我从中得到的是 SocketServer.StreamRequestHandler 不是最新的设计,虽然它允许我通过网络连接,但它并不真正支持我使用所有 TCP/我需要注意 IP 相关方面以实现稳健的通信。

这已在 Python 3 中通过 asyncio 得到解决。 ,但由于该项目存在于 Python 2 中,所以这不是一个选项。因此,我在 Twisted 中实现了上面描述的服务器和客户端。 :

server.py:

#!/usr/bin/env python
# encoding: utf-8

from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor

class SimpleProtocol(LineReceiver):

    def connectionMade(self):
        print 'connectionMade'

    # NOTE: lineReceived(...) doesn't seem to get called

    def dataReceived(self, data):
        print 'dataReceived'
        print 'RX: %s' % data

        if   data == 'request type 01':
            response = 'response type 01'
        elif data == 'request type 02':
            response = 'response type 02'
        else:
            response = 'unsupported request'

        print 'TX: %s' % response
        self.sendLine(response)

class SimpleProtocolFactory(Factory):

    def buildProtocol(self, addr):
        return SimpleProtocol()

reactor.listenTCP(12345, SimpleProtocolFactory(), interface='127.0.0.1')
reactor.run()

client.py:

#!/usr/bin/env python
# encoding: utf-8

from twisted.internet import reactor
from twisted.internet.protocol import Protocol
from twisted.internet.endpoints import TCP4ClientEndpoint, connectProtocol

class SimpleClientProtocol(Protocol):
    def sendMessage(self, msg):
        print "[TX]: %s" % msg
        self.transport.write(msg)

def gotProtocol(p):
    p.sendMessage('request type 01')
    reactor.callLater(1, p.sendMessage, 'request type 02')
    reactor.callLater(2, p.transport.loseConnection)

point = TCP4ClientEndpoint(reactor, '127.0.0.1', 12345)
d = connectProtocol(point, SimpleClientProtocol())
d.addCallback(gotProtocol)
reactor.run()

客户端不会关闭,而是空闲直到CTRL+C。 Twisted 可能需要一段时间才能让我理解,但对于手头的工作来说,使用经过测试和试用的框架显然比自己完成所有这些基础工作更合理。

注意:这在 Twisted XmlStream: How to connect to events? 处继续

最佳答案

这里的问题是,在 TCPHandler 中,“请求”实际上是一个完整的连接,从开始到结束。* 您的处理程序在 accept 时被调用,并且当您从它返回时,套接字将关闭。

如果你想在此基础上构建一个请求-响应-协议(protocol)处理程序,它在单个套接字级请求上处理多个协议(protocol)级请求,你必须自己做(或使用更高级别的框架) . (像 BaseHTTPServer 这样的子类演示了如何做到这一点。)

例如,您可以在handle 函数中使用一个循环。当然你可能想在这里添加一个异常处理程序和/或处理来自 rfile 的 EOF(注意 self.rfile.readline() 将返回 '' 用于 EOF,但 '\n' 用于空行,因此您必须在调用 strip 之前检查它,除非您希望空行表示“退出”在你的协议(protocol)中)。例如:

def handle(self):
    try:
        while True:
            request  = self.rfile.readline()
            if not request:
                break
            request = request.rstrip()
            print "RX [%s]: %s" % (self.client_address[0], request)

            response = self.processRequest(request)

            print "TX [%s]: %s" % (self.client_address[0], response)
            self.wfile.write(response + '\n')
    except Exception as e:
        print "ER [%s]: %r" % (self.client_address[0], e)
    print "DC [%s]: disconnected" % (self.client_address[0])

这将经常与您现有的客户端一起工作,至少在卸载机器上的本地主机上,但它实际上并不正确,而且“经常工作”很少足够好。参见 TCP sockets are byte streams, not message streams对于更长时间的讨论,但简而言之,您需要做 David Schwarz's answer 中提到的事情:将换行符附加到您从服务器写入的内容(我已经在上面做了),并让客户端逐行读取,而不是一次只尝试读取 1024 个字节(您可以通过编写自己的缓冲区和-分割线代码,或仅通过使用 makefile 方法,因此它可以像服务器端一样使用 rfile.readline()。)

不修复客户端不会导致回答 claim 的问题,但它导致这样的问题:

Sent:     request type 01
Received: resp
Sent:     request type 02
Received: onse type 01
response typ

并且您可以看到,在实际尝试以编程方式处理响应的真实程序中,像 response type 01\nresponse typ 这样的响应不会发生非常有用……


* 请注意,SocketServer 是一种没有人真正喜爱的古老设计。 Python 3 添加 asyncio 是有原因的,Python 2 中的人们通常使用第三方框架,如 Twistedgevent .它们对于简单任务来说更简单,对于复杂任务来说更灵活/强大(并且效率更高)。

关于Python:SocketServer 意外关闭 TCP 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29571770/

有关Python:SocketServer 意外关闭 TCP 连接的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  3. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  4. ruby - 无法在 60 秒内获得稳定的 Firefox 连接 (127.0.0.1 :7055) - 2

    我使用的是Firefox版本36.0.1和Selenium-Webdrivergem版本2.45.0。我能够创建Firefox实例,但无法使用脚本继续进行进一步的操作无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055)错误。有人能帮帮我吗? 最佳答案 我遇到了同样的问题。降级到firefoxv33后一切正常。您可以找到旧版本here 关于ruby-无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055),我们在StackOverflow上找到一个类

  5. ruby - 如何关闭 ruby​​ gem "Spreadsheet?"中的文件 - 2

    下面的代码在我第一次运行它时就可以正常工作:require'rubygems'require'spreadsheet'book=Spreadsheet.open'/Users/me/myruby/Mywks.xls'sheet=book.worksheet0row=sheet.row(1)putsrow[1]book.write'/Users/me/myruby/Mywks.xls'当我再次运行它时,我会收到更多消息,例如:/Library/Ruby/Gems/1.8/gems/spreadsheet-0.6.5.9/lib/spreadsheet/excel/reader.rb:11

  6. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  7. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  8. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  9. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  10. 「Python|Selenium|场景案例」如何定位iframe中的元素? - 2

    本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决

随机推荐