【问题标题】:How to receive and handle multiple TCP stream sockets?如何接收和处理多个 TCP 流套接字?
【发布时间】:2020-05-25 12:18:26
【问题描述】:

我想通过带有套接字模块的 TCP 将移动点的位置发送到服务器。该点位置在for 循环的每次迭代中更新,并以已使用pickle dumps 方法序列化的元组(x, y) 的形式发送。

问题:

在服务器端,我似乎只能从该循环的第一次迭代中接收位置。好像在此过程中已跳过或丢失了以下所有更新的位置。

我不能确定这种行为背后的原因是什么,但我敢打赌,我没有正确设置服务器端的东西。我怀疑数据将被完全发送,但由于我可能对套接字模块所做的一些错误(我对网络接口的世界完全陌生)而在接收时没有得到充分处理。

代码:

--客户端--

#Python3.7
import socket
import pickle
import math

HOST = "127.0.0.1" 
PORT = 12000

den = 20
rad = 100
theta = math.tau / den

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    sock.connect((HOST, PORT)) #connect to server

    for step in range(1000):
        i = step%den
        x = math.cos(i*theta) * rad
        y = math.sin(i*theta) * rad
        data = pickle.dumps((x, y), protocol=0) 
        sock.sendall(data) 

--服务器端--

#Jython2.7
import pickle
import socket

HOST = "127.0.0.1"   
PORT = 12000

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)

while True:
    connection, address = s.accept()

    if connection:
        data = connection.recv(4096)
        print(pickle.loads(data)) # <-- only print once (first location)

【问题讨论】:

  • 你需要把connection, address = s.accept()放在while循环之外,否则你的服务器每次都会等待一个新的连接
  • 您好,感谢您的建议,但这并不能解决问题。
  • 抱歉,经过深入了解后,您接收数据的方式也存在问题。 connection.recv(4096) 不会在每次收到完整的“数据”消息时都返回 0 到 4096 之间的任意字节数。要处理这个问题,您可以在发送 json 之前发送一个标头,指示应该接收多少数据
  • 有意思,能举个例子吗?
  • 看下面的答案

标签: python sockets tcp pickle


【解决方案1】:

你需要把connection,address = s.accept()放在while循环之外,否则你的服务器每次都会等待一个新的连接。

您接收数据的方式也存在问题。 connection.recv(4096) 不会在每次收到完整的“数据”消息时都返回 0 到 4096 之间的任意字节数。要处理这个问题,您可以在发送 json 之前发送一个标头,指示应该接收多少数据 通过添加标头,您将确保您发送的数据消息将被正确接收。

此示例中的标头是一个四字节 int,表示数据的大小。
服务器

import pickle
import socket
import struct

HEADER_SIZE = 4
HOST = "127.0.0.1"
PORT = 12000


def receive(nb_bytes, conn):
    # Ensure that exactly the desired amount of bytes is received
    received = bytearray()
    while len(received) < nb_bytes:
        received += conn.recv(nb_bytes - len(received))

    return received

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
connection, address = s.accept()

while True:
    # receive header
    header = receive(HEADER_SIZE, connection)
    data_size = struct.unpack(">i", header)[0]
    # receive data
    data = receive(data_size, connection)
    print(pickle.loads(data))

客户

import socket
import pickle
import math

HEADER_SIZE = 4
HOST = "127.0.0.1"
PORT = 12000

den = 20
rad = 100
theta = math.tau / den

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    sock.connect((HOST, PORT)) #connect to server

    for step in range(1000):
        i = step%den
        x = math.cos(i*theta) * rad
        y = math.sin(i*theta) * rad
        data = pickle.dumps((x, y), protocol=0)
        # compute header by taking the byte representation of the int
        header = len(data).to_bytes(HEADER_SIZE, byteorder ='big')
        sock.sendall(header + data)

希望对你有帮助

【讨论】:

  • 抱歉,我很难将您的“服务器”脚本移植到 Python 2.7(实际上是我原始脚本中所述的 Jython 2.7)。 int.from_bytes(header, byteorder ='big') 返回一个错误,我不知道 Python 2 中的等价物。int(str(header), 16)int(str(header).encode('hex'), 16) 似乎在我的机器上都不起作用。
  • 对不起,我用data_size = struct.unpack("&gt;i", header)[0]更新了代码以确保交叉兼容性
  • 非常感谢您的宝贵时间和回答。我从中学到了很多。不幸的是,我无法在 Jython 2.7 中运行它,但它似乎在 Python 2.7 中运行良好。 +1
  • 不客气。您在 Jyhton 2.7 上遇到什么错误?
  • TypeError: StringIO(): 1st arg can’t be coerced to org.python.core.PyArray
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多