为什么 telnet 被认为是一种协议?这不就是一个简单的TCP发送/回显程序吗?

为什么 telnet 被认为是一种协议?这不就是一个简单的TCP发送/回显程序吗?

这更像是一个概念性问题。我需要一些澄清。


今天学习了一些socket编程的东西,写了一个简单的聊天服务器和聊天客户端Beej 的网络编程指南。 (聊天服务器接收客户端消息并将消息发送给所有其他客户端)

我复制了 聊天服务器我自己写的聊天客户端

聊天客户端只是一个将stdin输入发送到服务器并从服务器打印套接字数据的程序。

后来我注意到指南说我只能使用它telnet来连接到服务器。我尝试过并且成功了。


我对telnet很陌生,很长一段时间都不知道它到底是什么。

所以现在我的经历让我很困惑:

telnet 不就是一个简单的 TCP 发送/回显程序吗?是什么让它如此特别协议事物?我的愚蠢聊天客户端程序不会创建[应用程序]协议。

来自维基百科通讯协议:

在电信中,通信协议是一个系统规则 允许通信系统的两个或多个实体通过物理量的任何类型的变化来传输信息。

Telnet 创建什么规则?telnet host port,打开 TCP 流套接字以进行原始输入/输出?这不是规则。

答案1

Telnet 定义于RFC 854。使它(以及其他任何东西)成为协议的是一组规则/约束。其中一条规则是 Telnet 通过 TCP 完成,并分配端口 23 - 这件事可能看起来微不足道,但需要在某处指定。

你不能随心所欲地发送任何东西,有些东西是有限制和特殊意义的。例如,它定义了一个“网络虚拟终端”——这是因为当telnet建立时,可能有许多不同的终端:打印机、黑白显示器、支持ANSI代码的彩色显示器等。

另外,还有这样的东西:

总之,任何一方发送 WILL XXX 来表明该方希望(提议)开始执行选项 XXX,DO XXX 和 DON'T XXX 是其肯定和否定确认;类似地,发送DO XXX以指示另一方(即DO的接收者)开始执行选项XXX的愿望(请求),WILL XXX和WON'T XXX是肯定和否定确认。由于 NVT 是未启用任何选项时留下的内容,因此保证 DON'T 和 WON'T 响应使连接处于两端都可以处理的状态。因此,所有主机可以将其TELNET过程实现为完全不知道不支持的选项,仅返回对任何无法理解的选项请求的拒绝(即,拒绝)。

在现代,大多数东西已经不再那么重要了(话又说回来,telnet 作为一种协议不再被广泛使用,不仅仅是因为它缺乏安全性)所以在实践中它归结为发送/回显,除非你必须与终端实际交互。

答案2

连接两个终端时“正常工作”的事实telnet本身就是一个协议决定:设计者telnet决定在 TCP 连接的任一端使用具有“网络虚拟终端”的模型,并决定在基本终端上对这些虚拟终端进行建模功能(实际上是电传打字机)。

除此之外,telnet它不仅仅是一个简单的基于 TCP 的回显服务;它支持带内发送的控制代码,用于各种目的。它被编入RFC 854

答案3

A子系统被称为“远程登录”建议这是一个围绕网络系统原语的 shell 程序,允许远程主机上的电传打字机或类似终端充当服务主机上的电传打字机。

这是来自 RFC 15,维基百科“telnet”中提到了它。本文也首先将 telnet 称为“应用协议”。

RFC 15 是 1969 年 9 月发布的 - 就在几天前,我听到了一次有趣的采访,采访的对象是第一个“互联网”连接者,距今正好 50 年前。他的故事是这样的:

“与 N. Armstrong 不同,我们没有想到任何特殊消息。但是在输入“lo”(“登录”)后,系统崩溃了。所以“lo”(如“lo and behold”)是前两个回想起来,我们想不出更好的办法了。”

(以下是部分采访内容:维基阿帕网克莱因洛克


Telnet 早于 TCP/IP。 RFC 15 谈到“网络原语”。由于 TCP/IP,telnet 只负责处理“其余部分”,即应用层(RFC854 中的“协商选项”)。

是的,即使没有 TCP,telnet 也非常简单。 RFC 15 结尾为:

TELNET子系统构成了“0级”网络程序,很快就会被超越。然而,它非常简单,很快就能发挥作用。


“协议”与 telnet 一起使用,因为本质上两个(不同的)系统需要使用一种“语言”进行通信。

RFC 854 也解释了这个概念:

TELNET 协议建立在三个主要思想之上:第一,“网络虚拟终端“……


答案4

现有的答案已经解释了,telnet可以做的不仅仅是通过 TCP 流输入数据不变;我想把重点放在“我的愚蠢的聊天客户端程序不会创建[应用程序]协议”。 -问题的一部分。 (顺便说一句,对于实际的“TCP 上的原始数据”,您可以使用netcat代替telnet

仅仅因为一个协议简单或不好——或者甚至没有在任何地方明确指定——并不意味着它不是一个协议。如果没有达成一致的协议,两个系统就无法通信,即使该协议只是“TCP 上的原始数据”。如果你尝试在一端使用 FTP,在另一端使用 HTTP,你就会遇到问题,不管怎样,如果你幸运的话,它不会工作;如果你幸运的话,它不会工作。如果你不这样做,你就会得到错误的行为。 (至少过去的情况是,如果您在与 HTTP 服务器相同的主机上的非标准端口上运行 FTP 守护程序,则在某些浏览器上,您可以使用它来执行 XSS 并通过指向受害者的浏览器来绕过 CSRF 保护POST 到 FTP 服务器,这会在错误消息中回显 POST 内容,浏览器会将其解释为 HTTP/0.9 响应 - 并愉快地运行任何 javascript。)

my dumb chat client program“你所说的那个东西使用什么协议?我想通过我的程序与它交谈”是一个完全有效的问题。一个可能的答案是“TCP 上的原始数据”。然而,这并不是一个详尽的答案。

  • 如果您有一些默认端口,这可能应该是答案的一部分。
  • 如果你想非常清楚,你可以指定你的意思是“正常的 TCP 数据,而不是 TCP 紧急/带外数据”(这有点夸张,因为它没有任何意义,并且判断根据我见过的认为第三组 fdselect()是错误的人的数量,大多数人甚至似乎不知道带外数据的存在 - 但即使这是一个不好的例子,我希望你明白我的意思)
  • 即使您可能没有想到的事情也可能隐式地成为协议的一部分:如何关闭连接?您只是打电话close()还是使用它shutdown()来确保首先传递所有待处理的数据?

您说“为原始输入/输出打开 TCP 流套接字?这不是规则。”,但真的是这样吗? “你必须使用 TCP”、“你必须按原样发送数据”和“你应该使用端口 XXX”对我来说听起来像是规则。

(PS,我在写这篇文章时有点着迷;这个答案的其余部分有点离题,并试图回答自然的后续问题“好吧,所以一切都是协议 - 这有什么影响吗?”)

因此,请记住,每次让两个系统相互通信时,您要么使用现有协议,要么创建新协议:在为时已晚之前记录您的协议! (即,最晚,当协议变得不再简单,或者开始拥有比开发人员更多的用户时)许多早期的P2P协议都说“官方客户端的来源是文档”,这在复杂的项目中非常烦人,并且只会导致客户端使用微妙不同的协议并且到处不兼容。

有句古老的格言“对你所生产的东西要严格,对你所接受的东西要宽松”——核心观点很好,但坦率地说,至少它的应用方式,我认为这是废话,只会导致更多可维护性和兼容性方面的问题。我说:详细指定(尝试考虑每个极端情况),并对您生成和接受的内容极其严格,您可能会防止/解决真正的错误。

一个关于严格解决错误的例子:几年前,某个 BitTorrent 跟踪器开始遇到问题:他们的一些 torrent 在某些客户端上无法运行;客户没有抱怨,他们只是得到了不同的信息哈希,因此找不到种子。他们无法弄清楚发生了什么,并宣称受影响的客户端一定有问题 - 停止使用它们。 Bittorrent 使用 Bencoding,这是非常严格和明确的 - 并且有一个很好的理由:相同的输入总是会产生完全相同的编码输出字符串,因此对于相同的输入数据,[info]哈希总是相同的。当我第一次遇到其中一个种子时,我尝试将其输入到我自己的(非常严格的)解析器中,它立即说:Error: Bencoded dictionary keys not sorted (last='sha1', key='private').

所以发生了什么事?跟踪器开始自动将“私有”密钥添加到每个上传的 torrent 中,但只是将其添加到末尾,而不是像 bencoding 指定的那样对字典密钥进行排序 - 它生成的内容并不严格。一些客户端仅部分解码 torrent,并计算未解码、未更改的字符串的哈希值。一些客户端解码了整个 torrent,并重新编码了他们需要散列的部分,在此过程中正确地对密钥进行排序(创建有效的 Bencoding),并获得了不同的散列。据我所知/回忆,没有客户抱怨数据无效——客户对他们接受的数据并不严格。那么,哪些哈希值是正确的?两者都不!两种计算哈希值的方法都是正确的,但种子文件已损坏!即使其中一个客户对此进行了投诉,该错误也可能会在同一天得到修复,而不是花费几个月的时间。 (IIRC 几年后,我还在另一个完全不相关的跟踪器中报告了相同的错误;第一个跟踪器的软件是内部开发的,因此它也不是相同的代码库)

另一个例子,这次是关于极端情况的:eDonkey2000 P2P 协议使用了一个名为 ed2k 的自定义哈希;该规范没有指定输入数据长度可以被块大小整除的极端情况下的行为,因此诞生了两个不兼容的变体,它们为某些文件产生不同的哈希值,并且是不兼容的。 (我说的是“规范”,但由于这是一个早期的 P2P 协议,我很确定没有规范;在对协议进行逆向工程时可能会错过特殊情况)

关于详细规范的需求:很多时候,当尝试编写协议的客户端或文件格式的解析器时,我遇到过这样的情况,即使是最初的程序员也不知道某些数据实际上是如何编码的,因为它们我使用过一些库,但从未详细检查过它的实际用途。两个例子:

某个 API 使用自定义 UDP 协议,带有加密的[可变长度]数据包 - 文档指定了 AES128,但没有提及所使用的填充。当开发商询问此事时,他们的答复是这样的:

呃,我们实际上不知道,我们只是在这个Java库中使用了这个函数,并且文档似乎没有提及它使用哪个填充

某个设备的网站对与其保存文件的接口有这样的说法:

无法创建或修改已保存的 *.logicdata 文件。这是因为这些文件是通过 boost 二进制序列化生成的,并且包含非常复杂的对象结构。事实上,我们甚至不明白它是如何工作的。 Boost 序列化极其复杂。

(注意:但实际上,对足够的文件格式进行逆向工程并不需要那么长时间就能导出数据)

其中一些示例是关于编码、文件格式或自定义算法(尽管仍然在协议内部使用,除了最后一个),但我想说所有这些都适用于所有这些。无论您谈论的是协议、文件格式、编码还是自定义算法:

  • 写一个好的规范/很好地记录它
    • 尝试考虑所有极端情况
    • 尝试包含所有需要的详细信息
  • 严格遵循规范/严格

相关内容