设计和实现网络协议的最佳实践是什么?

4
这是我第一次尝试在TCP/IP上实现某些网络协议。我设计了一个,但不确定它是否有效。
设计思路:客户端与服务器打开TCP/IP连接后,每次发送请求时,首先发送请求的大小,后跟一些分隔符(换行或空格),然后是实际请求(HTTP中使用相同的原则,并且在大多数情况下都使用此想法)。
例如,如果客户端想要发送GET ASD,它实际上会发送7 GET ASD(假设空格是分隔符)。
对于服务器端,对于每个客户端,服务器都有一个缓冲区,用于保存传入的请求。每当它从客户端获得一些新的字符块时,服务器将其附加到相应的客户端缓冲区中。之后,服务器将尝试获取请求的内容长度(在此示例中为7),并检查剩余缓冲区的长度是否大于或等于该长度。如果是,则服务器将获取请求的实际内容,进行处理并从缓冲区中删除它。
实现方面,我认为主要问题在于有效地实现和管理缓冲区。我认为具有2 * MAX_SIZE_OF_ONE_REQUEST大小的缓冲区足以为一个客户端提供服务,因为服务器接收到的块可以同时包含第一个请求的结尾和第二个请求的开头。这是我的假设,如果我错了并且我们需要更多或更少的空间,请告诉我原因。
我认为有两种方式可以在缓冲区中存储请求,直到它们被处理:
1.每当服务器接收到新的字符块时,服务器将其附加到缓冲区的右侧。一旦缓冲区包含完整的请求,服务器将处理它,并将其余的所有内容向左移动到缓冲区空间的开头。
2.一些循环缓冲区,在处理请求后不会将缓冲区移动到开头。这是我考虑异步I/O实现缓冲区时的想法(服务器将使用epoll/kqueue/select从客户端接收请求)。我认为,如果服务器不使用异步I/O与客户端通信,则实现缓冲区将更加简单。
此外,我还没有决定服务器在接收到格式错误的请求时应该如何行事。它应该关闭与客户端的连接吗?
也许我写得太多了,但我真的对这个话题很感兴趣,想尽可能多地学习。我认为像我这样的人很多,因此关于此主题的任何实际问题和解决它们的最佳实践都将非常有帮助。

在进行任何设计之前,分析API的要求非常重要。如果您能详细说明这些要求,那么可能会更容易。 - NG.
我受 Redis 的启发,为了学习目的,开始了一个小项目 Kivi (K/V 存储):http://github.com/giolekva/kivi。因此,会有像 Redis 中的 GET、SET、DEL、INCR 等命令。 - giolekva
(添加注释,因为我对你的问题没有完整的答案):我发现预防请求格式不正确的可能性更好。在这种情况下,长度和请求可能不一致,因此我会放弃长度。相反,您可以有一些特殊的终止符('\0'在许多情况下都很有效)。当然,仍然存在客户端发送无休止的字符串以过载服务器的可能性,但是如果长度不正确,至少您不会挂起等待更多字节。 - Jonathan
“同样的原则也被用于HTTP。”并不是这样。HTTP是一种基于行的协议。它可能包含长度标头,也可能不包含。你最好研究一下互联网协议族(FTP、HTTP、SMTP等)的真正工作方式。 - user207421
2个回答

4
如果您不需要一个可读性强的协议,我建议使用二进制协议。开始时,使用x个字节表示命令长度,然后根据您的需要形成其余部分的消息; 当然,如果您喜欢,剩余的消息可以是文本...在我的看来,这种方式更容易在服务器上处理,因为您不需要扫描所有输入字节以确定消息何时结束。
由于您知道需要确定消息长度的(固定)字节数,因此可以忽略所有消息,直到它们达到该长度。您(可能)有一个合理的最大消息大小,因此可以按照可以容纳所有消息的缓冲区来工作。这意味着您可以将读取累积到单个缓冲区中,直到您有一个完整的消息,无需复制或移动。一旦您拥有完整的消息,您可以将此缓冲区传递给处理,并开始读取新的缓冲区。对缓冲区进行引用计数,当您使用它们完成后,它们可以返回到池中。
为了防止拒绝服务攻击,您应该在读取数据时设置超时。如果x时间内没有完整的消息,则断开连接。

消息格式错误,您将会断开连接。在我看来Postel有很多问题要解决;在我看来,当你严谨地要求人们做到正确并且不接受任何差错时,协议会更好...

消息大小超过了您允许的限制,您将会断开连接。

我谈论了关于TCP消息帧问题这里这里,涉及到长度前缀和基于行(序列终止)协议,尽管讨论重点放在我的免费可插拔服务器平台WASP上,所以这可能对您有用也可能没有。

说实话,这是最简单的部分。复杂的部分是设计实际协议,使客户端和服务器可以高效地交流问题空间...然而,如果在更复杂的部分出现问题,这一点出错可能会导致有趣的问题...


3
首先,我建议查阅RFC和ITU.T规范以找到符合您需求的协议。如果没有找到,至少可以了解其他协议的设计方式并了解其背后的原理。
考虑使用XDR或BER(ASN.1)对数据进行二进制编码。有一些库可以处理字节序和对齐问题。例如,将每个数据包包装在XDR opaque中可以更有效地设计服务器(1个TCP前端模块,将未处理的opaque发送到适当的处理程序,而无需知道每个数据包的含义)。
如Len Holgate所提到的,请确保指定特殊情况下应该发生什么(格式不正确的数据包、无响应等),或者是否应该有保持活动状态的数据包?如果是这样,多久发送一次?是否应该进行客户端-服务器协商?
另外,不要忘记在hello数据包中包括协议版本。收到“我的客户端应用程序显示不支持协议版本2”的问题单比“一些客户端运行正常,但当我尝试接收数据集时,只得到随机数!”更好。

2
如果需要查看其他人的规格,则协议版本加1,否则加2... - Len Holgate

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接