tcp丢失数据包python



我使用tcp套接字在服务器和客户端之间转发数据(作为具有请求类型和数据的类(。当我从客户端发送6个订单对象时,服务器只得到5个订单对象,1个丢失,但tcp是可靠的协议。。。(有时服务器得到6,但通常得到5(我的问题是,为什么一个对象在通过tcp转发时丢失了?

这是客户端代码

PORT = 3000
SIZE = 4096
HOST = '127.0.0.1'
soc = None
ing_map = None

def connect_to_client():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
global soc
soc = s

def send_object(data):
if soc is None:
raise NotImplementedError  # There is no connection
else:
# make data as bytes
msg = pickle.dumps(data)
msg = bytes(f"{len(msg):<{HEADERSIZE}}", 'utf-8') + msg
print(sys.getsizeof(msg))
soc.send(msg)

def get_object(req):
if soc is None:
raise NotImplementedError  # There is no connection
else:
# unpickle the data
send_object(req)
if soc is None:
raise NotImplementedError  # There is no connection
else:
# unpickle the data
data = b''
while True:
part = soc.recv(SIZE)
data += part
if len(part) < SIZE:
break
full_msg = data
try:
data = pickle.loads(full_msg[HEADERSIZE:])
except EOFError:
data = None
return data

def send_order(order):
if order is not None:
send_object(Soc_request(Request.P_ORDER,order))

def main():
global ing_map
connect_to_client()
ing_map = get_object(Soc_request(Request.G_ING_MAP, None))
#send order
burger = Burger(ing_map)
salad = Salad(ing_map)
burger.add_ingredient('ham')
o1 = Order(Priority.NORMAL)
o2 = Order(Priority.NORMAL)
o3 = Order(Priority.VIP)
o4 = Order(Priority.VIP)
o5 = Order(Priority.PLUS)
o6 = Order(Priority.PLUS)
o1.meals_lst.append(burger)
o1.meals_lst.append(burger)
o1.meals_lst.append(burger)
o3.meals_lst.append(burger)
send_order(o1)
send_order(o2)
send_order(o3)
send_order(o4)
send_order(o5)
send_order(o6)
soc.close()

这是服务器代码

HOST = '127.0.0.1'
PORT = 3000
SIZE = 1000
HEADERSIZE = 10
WORKERS = 2
conn = None
addr = None
soc = None
order_m = None

def create_connection():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen()
global conn, addr
global soc
soc = s
conn, addr = s.accept()

def send_object(data):
if soc is None:
raise NotImplementedError  # There is no connection
else:
# make data as bytes
global conn
msg = pickle.dumps(data)
msg = bytes(f"{len(msg):<{HEADERSIZE}}", 'utf-8') + msg
conn.send(msg)

def get_object():
global conn
if conn is None:
raise NotImplementedError  # There is no connection
else:
# unpickle the data
data = b''
while True:
part = conn.recv(SIZE)
data += part
if len(part) < SIZE:
break
full_msg = data
try:
data = pickle.loads(full_msg[HEADERSIZE:])
except EOFError:
data = None
return data

def main():
create_connection()
# Initialize objects
global order_m
ing_map = Ingredient_map()
order_m = OrderManager()
while True:
msg = get_object()
if msg is None:
pass
elif msg.req == Request.G_ING_MAP:  # get ingredient map
send_object(ing_map.instance.map)
elif msg.req == Request.P_ORDER:
order_m.add_order(msg.data)
print(msg.data)
# end while
soc.close()

if __name__ == "__main__":
main()

服务器的输出为订单0订单1订单3订单4订单5但订单2失败了为什么?我该怎么办才能保证所有订单都到账?请求是请求类型的枚举。

让我们可视化从客户端到服务器的6个pickled对象:

server <- 1112222233334445555566 <- client

TCP连接是一个数据流。没有边界。服务器必须将数据拆分为各个部分,并拆下每个部分:

111 22222 3333 444 55555 66

为了能够做到这一点,您必须为数据引入一些高级结构。我不能运行你的代码,但我在那里看不到类似的东西。相反,服务器只是读取一个固定大小的缓冲区。现实有点复杂,但我认为这种情况有时会发生:

1112 2222 3333 4445 5555 66

物体1、3、4和6是一体转移的,但2和5被分成两部分,这些部分不能单独拆开。

我认为您需要将腌制数据的长度放在标头中。然后,服务器应该首先读取标头(固定大小(,提取长度,然后读取并取消拾取尽可能多的后续字节(不少于,不多于(。

还有这个片段:

try:
data = pickle.loads(full_msg[HEADERSIZE:])
except EOFError:
data = None

隐藏错误。当您静默地丢弃数据时,不要责怪TCP丢失数据包。

如@VPfB所述,您发送的是标头大小,但从未解析大小。你真的应该重组你的代码,然而,这里有一个使用你的代码的工作示例。

在您的客户端代码中,将get_object函数更改为此。

def get_object(req):
if soc is None:
raise NotImplementedError  # There is no connection
else:
# unpickle the data
send_object(req)
if soc is None:
raise NotImplementedError  # There is no connection
else:
# unpickle the data
# this initial recv only receives the header length
length = int(soc.recv(HEADERSIZE).decode('utf-8'))
data = b''
while True:
# here we receive a minimum of the standard recv size 
# and the diff between your already received data
# and the full length, whichever is smaller
part = soc.recv(min(SIZE, length - len(data)))
data += part
# if the length that was specifed in the header has been received
# we know we have everything
if length == len(data):
break
full_msg = data
try:
data = pickle.loads(full_msg)
except EOFError:
data = None
return data

现在,我们将对您的服务器代码进行几乎相同的更改。

def get_object():
global conn
if conn is None:
raise NotImplementedError  # There is no connection
else:
# unpickle the data
# the only difference here is
# when we receive nothing, we know it's time to wait for another connect
length = conn.recv(HEADERSIZE).decode('utf-8')
if len(length):
length = int(length)
data = b''
while True:
part = conn.recv(min(SIZE, length - len(data)))
data += part
if length == len(data):
break
full_msg = data
try:
data = pickle.loads(full_msg)
except EOFError:
data = None
return data
else:
# wait for another connection
conn, addr = soc.accept()

实际上,服务器的行为通常是这样的:

# do this forever
while True:
# wait for an incoming connection
conn, addr = soc.accept()
# get the length of the data the connection is sending
length = int(soc.recv(HEADERSIZE).decode('utf-8'))
# get the data
data = b''
while True:
part = soc.recv(min(SIZE, length - len(data)))
data += part
if length == len(data):
break
# do some stuff with the data

不同之处在于,我们在接收到每个对象后等待新的连接。这也为其他尝试连接的客户端提供了转机。

最新更新