通过TCP实现TLV协议

1 投票
1 回答
1955 浏览
提问于 2025-04-17 05:15

我现在正在尝试实现一个(T)LV协议,用于在TCP之上进行通信。这个协议的早期版本是通过每次发送一条消息来实现的,也就是说,发送一条消息后就接收一次(例如:send("要发送的消息" -- recv(... ))。这样做在带宽上非常糟糕,因为我发送的包太小了。

所以现在我想切换到LV协议,一次发送几条消息,只用它们各自的长度来分隔(我现在使用Protocol Buffers来序列化我的数据)。

我现在有两个问题:

  • 在Python中,我是这样发送的:

    sock.send(struct.pack("<H", len(gtMessage.SerializeToString())))

    sock.send(gtMessage.SerializeToString())

如果我把这个放进一个循环里,发送几条这样的消息,我觉得又会回到我之前的问题。有没有办法把要发送的字符串连接在一起呢?

  • 在C++中,我首先接收消息的长度,然后读取长度字段所指示的字节数。

从性能上来说,先从TCP读取所有内容再解析,还是先读取一条消息,解析完再读取下一部分更好呢?

编辑:经过进一步研究,我想把第一个问题重新表述为:

这个

    sock.send("somestring")
    sock.send("somestring")

和这个

sock.send("somestring"+"somestring")

是一样的吗?

1 个回答

1

连续发送两次数据可能会导致实际发送出两个数据包,这样不好。为了避免这个问题,你可以自己把这两部分数据合并在一起,或者使用writev(也叫“聚合写入”),或者在第一次发送时使用TCP_CORK,这样可以防止它单独变成一个数据包。

在接收数据时,你应该尽量接收一个大块的数据(尽量多接收,直到合理的限制,比如几兆字节),然后再进行解析。不要试着先接收一两个字节来判断大小,然后再进行下一次接收——这样效率很低,如果发送的消息被拆分了,你可能还会遇到“短读取”的问题。

撰写回答