【问题标题】:Continous receiving & sending on a socket在套接字上连续接收和发送
【发布时间】:2018-03-21 13:40:31
【问题描述】:

我正在处理我需要连接到服务器的任务(没有透露服务器的详细信息),捕获回复,修改它并将其发回以进行验证。

我创建了以下代码来满足我的需要,但问题是在第一次正确回复后,服务器发送另一个。

代码:

# -*- encoding: utf-8 -*-

import socket
from decryptmsg import decryptmsg
from cleanmsg import cleanmsg

#connection 
ip="<IP>"
port=4000
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip,port))

def recvall(sock):
    BUFFER = 8192
    data = b''
    while True:
        part = sock.recv(BUFFER)
        data += part
        if len(part) < BUFFER:
            break
    return data


while True:

    print "[+] start communication"
    data = recvall(sock)
    print data
    data = cleanmsg(data)
    if data != None:
        valmis = decryptmsg(str(data))
        if valmis == None:
            print "[-] no results"
            break
        else:
            print "[+] sending message... "
            sock.send(valmis) 
            continue

当我遇到第二个问题时,我使用此代码很好地捕获了输入并按预期进行处理,但是当我尝试发送第二个回复时,我收到错误:

Traceback (most recent call last):
File "challenge.py", line 28, in <module>
     sock.send(valmis)
socket.error: [Errno 32] Broken pipe

如果我不关闭或关闭套接字,则不会向服务器发送任何回复。

如何告诉我的客户发送消息并等待没有 socket.shutdown 的回复?或者如果我需要为每个循环打开新的套接字,应该如何构造循环?服务器的回复每次都会改变,所以如果我完全打开新连接并请求数据,我会得到新的回复,并且该过程会重新开始。

更新: 问题似乎是在尝试从服务器接收第二个回复时,客户端只接收到第一行消息。

【问题讨论】:

  • 我也用 sendall(data, MSG_DONTWAIT) 进行了测试,结果相同。
  • 你的方法从根本上被打破了;绝对没有理由相信在 TCP 流上对 .recv() 的单个调用将获得完整的消息(接收的块与另一端发送的块之间没有关联)。任何允许通过单个连接发送多条消息的协议都必须有某种方式来界定它们;通常是不能出现在消息本身中的特定字符或字符串,或者是在每条消息之前传输的长度值。
  • 感谢杰森的评论。我会考虑添加某种缓冲区,但我通过这个 recv 获得了正确的数据 - 在这里发送回复是我需要帮助的。
  • 无论服务器的编码方式如何,我都会删除第二个data = sock.recv(8192),因为它发生在while 循环的顶部,这意味着数据为无。我的快速测试客户端也不需要shutdown。

标签: python sockets


【解决方案1】:

你怎么知道它没有发送任何东西?我稍微修改了您的代码(else: 子句中有一些奇怪的东西,我稍后会回来)。

import socket

#connection 
ip="localhost"
port=4000
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip,port))

while True:

    data = sock.recv(8192)
    if not data: break;
    print data
    if data != None:
        valmis = data
        if valmis == None:
            print "[-] no results"
            break
        else:
            print "[+] sending message... "
            sock.send(valmis) #this never gets sent without
            continue

基本上,这是您的代码的剥离版本 - 没有解密或外部功能。它只是发回它从服务器接收到的任何内容。

然后我用 ncat 运行了一个“服务器”:

ncat -l 4000

启动您的程序并开始输入行(1、2、3、4 等),这发生在“服务器”。客户及时回复我的信息:

test@xyzzy:/tmp$ ncat -l 4000
1
1
2
2
3
3

这发生在客户端:

test@xyzzy:/tmp$ python so.py 
1

[+] sending message... 
2

[+] sending message... 
3

[+] sending message... 

在我看来,这段代码运行良好。如果服务器没有收到你的回复,可能是服务器端有问题。例如,它可能期望响应中有一个终止符。您的cleanmsg 是否过多地清理消息,例如删除尾随换行符并且服务器希望收到一个?

当您在那里执行另一个 sock.recv() 时,您原来的 else 子句中存在问题。这意味着在收到回复后,您将在那里阻塞以等待来自服务器的下一条消息,当您收到一条消息时,您将continue 您的循环并再次点击sock.recv()。第二条消息已在您的 else 子句中使用。

如果您的服务器以某种方式确认您的解密,这可能是故意的。如果你的协议是这样的:

server -> client  (request)
client -> server  (decrypted message)
server -> client  (some kind of acknowledgement - unclear from your code)

server -> client  (request 2)
etc. 

那么您可能在 Jason 的评论中遇到了问题。 TCP 套接字与消息的概念完全无关。他们只是传输数据。当您的代码点击 sock.recv() 时,可能会发生以下五件事之一:

  1. 套接字和调用块中没有任何内容
  2. 有一个完整的“消息”,只有在套接字中,您会收到它
  3. 有部分消息,您将收到该消息。可能是因为消息超过 8192 字节,或者您的代码只是在服务器仅传输了部分消息数据时才决定读取。
  4. 有两个或更多完整的“消息”在等待,您将全部收到。
  5. 四个,但最后一条消息是部分的

始终在使用 TCP 套接字时,必须满足场景 2-5。您必须解析数据,确保一切都在那里,如果没有,请等待更多。如果超出您的预期,请相应地处理它们。如果它们是完整的消息,则处理它们。如果最后一条消息是部分消息,则处理其他所有消息并等待更多消息。

如果消息似乎在使用 TCP 套接字的自制通信协议中“消失”,则 99% 的问题是由于假设套接字知道或关心您的“消息”的结构而引起的。一个非常常见的错误是在您的第一条消息之后将您的套接字读取为空并忽略您收到的所有内容。

希望这会有所帮助。从套接字通信的角度来看,没有额外 recv 的代码似乎可以正常工作 - 无需关闭套接字。可能是服务器端问题、协议问题或消息解析问题。

因此,您的套接字始终只有一个 recv 调用。即使您期待某种确认而不是新消息,也只有一个地方可以处理套接字数据。然后在那里做一些条件来检测你收到什么样的消息,然后决定如何处理它。

【讨论】:

  • 谢谢汉努!我实际上没有想到这一点 - 我按照您的建议修改了代码,并将打印语句添加到 while 循环的开头,并且循环似乎重新启动正常,但没有从服务器端接收到数据。这里的机制如下: 1. 当连接到服务器时,它会提示您一些加密格式的文本 2. cleanmsg-function 清除即将到来的输入,只留下消息的加密部分 3. decryptmsg 解密该输出 4. 那么你应该将其发送回服务器,如果加密被正确破解,服务器会发送另一条消息作为代表
  • 这就是我遇到问题的地方 - 我似乎无法告诉服务器我已完成发送,因此永远没有回复。
  • 我捕获了流量,你是对的 - 通信确实有效;当跟踪 TCP 流时,我可以看到服务器正在回复,但似乎回复只是部分的 - 在回复中我只能看到回复的第一行,并且实际的加密数据永远不会回到客户端,因此解密永远不会地方。我已经用新的接收代码更新了原始问题,但这似乎没有奏效。
  • OK - 有时答案离您太近而无法看到 - 服务器在答案中除了换行符“\n”之外,还有正确的消息以产生响应 - 现在通信正常了。将您的答案添加为正确答案 - 谢谢!
  • 这将是“协议错误”或“剥离换行符”。等待消息中的换行符构成了此特定协议的基础。如果我个人负责客户端和服务器,我几乎总是使用 zeromq。它与套接字一样简单易用,并将抽象级别从字节提升到消息。它将负责完整地发送和接收消息。对于套接字,我最终只需要编写很多驴代码来提供相同的功能。
猜你喜欢
  • 1970-01-01
  • 2011-07-11
  • 2014-07-31
  • 1970-01-01
  • 2010-11-14
  • 1970-01-01
  • 2016-02-21
  • 2013-04-01
  • 1970-01-01
相关资源
最近更新 更多