在不按回车键的情况下通过telnet发送数据

16

我最近开始尝试使用Java sockets和telnet...

我希望用户能够连接到服务器,只需输入一个字母并将其发送到服务器,而无需按回车键发送。我确信服务器没有办法设置这个,但也许telnet有一个参数或其他东西可以实现这个?

也许如果我让用户在运行telnet之前输入stty cbreakstty raw,这样会起作用吗?(仅适用于UNIX系统!)

如果我能让telnet实现这个功能,那么就不必为此编写特殊的客户端程序...

5个回答

9

实际上,服务器确实有一种请求方式:它被称为telnet选项协商。通常情况下,telnet会默认在使用23号端口时将本地tty配置为“原始”模式,在其他端口上则是“熟练”(或“行”)模式。行模式是指您拥有最简单的本地编辑功能,并且在按下回车键时发送数据。

一旦禁用了行模式,您就可以分别配置诸如本地回显之类的内容。

编辑:我认为一个合理的顺序应该是:

255, 253, 34,  /* IAC DO LINEMODE */
255, 250, 34, 1, 0, 255, 240 /* IAC SB LINEMODE MODE 0 IAC SE */
255, 251, 1    /* IAC WILL ECHO */

这使得TELOPT_LINEMODE(34)可用,然后将linemode LM_MODE设置为0(我认为这是告诉客户端不要进行任何本地编辑的正确方式)。最后,它说WILL ECHO表示服务器将回显(因此客户端不会回显)。

客户端(如果支持telnet协商)将回复类似于IAC blah blah或“引用”的序列,如IAC SB ... IAC SE,您可以检测并过滤输入流中的这些序列。


1
我应该在哪里以及如何发送这些选项? - Matt
+1 这看起来是正确的答案。请查看我的更新答案,了解如何实现它。 - eater

3
您可以使用telnet选项协商来完成此操作。协议默认为半双工模式,对于交互式会话,服务器至少应该协商抑制前进选项回显选项

最起码,您可以在会话开始时输出ff fb 01ff fb 03(将回显,将抑制前进),然后回复任何ff fd 01(做回显)用ff fb 01(将回显)进行回复,回复任何ff fd 03(做抑制前进)的请求用ff fb 03(将抑制前进)进行回复。

编辑:添加Ben Jackson提到的行模式协商是更好的答案。对于连接到除23端口以外的大多数客户端而言,抑制前进不足够。

但我认为您遇到的另一个问题是Java正在发送Unicode字符。例如,当您说(char)0xff时,Java假定您指的是UTF-16字符U+00ff,即ÿ。它可能使用UTF-8编码将其通过套接字发送,因此telnet客户端会看到两个字节:c3 bf,它将其传递并显示为ÿ

您可以明确告诉Java使用ISO-8859-1编码。例如,您可能之前一直在执行以下操作:

out = new PrintStream(connection.getOutputStream());
out.print((char)0xff);  // sends 0xc3 0xbf
out.print((char)0xfb);  // sends 0xc3 0xbb
out.print((char)0x01);  // sends 0x01
out.flush();

相反,您可以使用OutputStreamWriter来指定您想要的编码方式:
out = new OutputStreamWriter(connection.getOutputStream(), "ISO-8859-1");
out.write((char)0xff);  // sends 0xff
out.write((char)0xfb);  // sends 0xfb
out.write((char)0x01);  // sends 0x01
out.flush();

我不太明白应该把这些选项代码发送到哪里... 我试着在客户端连接时将这些字符发送给客户端: out.print((char)0xff); out.print((char)0xfb); out.print((char)0x01); out.print((char)0xff); out.print((char)0xfb); out.print((char)0x03); out.flush();但这只会打印出ÿûÿû^。 - Matt
我需要降低层次或做些什么吗? - Matt
使用ISO-8859-1而不是UTF-8阻止了我使用扩展ASCII字符(128到255)。我之前将它们作为Unicode字符串发送(即,\u2588表示219),但显然不再起作用。相反,我尝试将它们作为字符发送,例如只发送219,但它只会打印出�字符。有什么方法可以解决这个问题吗? - Matt
2
我认为你可以同时使用PrintStream和OutputStreamWriter。调用 out = new PrintStream(conn.getOutputStream());rawout = new OutputStreamWriter(conn.getOutputStream(), "ISO-8859-1"); 然后,你可以使用 rawout.write((char)0xff); 发送单个255字节,但对于其他所有内容,可以使用 out.print("whatever \u2588"); - eater

2

要配置telnet在不按回车键的情况下发送数据,您应该禁用行模式选项。这可以通过mode telnet子命令来完成:

将其设置为no line-mode将禁用行模式。

telnet> mode character

这使得逐个字符模式成为可能。最初的回答:

这可以实现逐个输入字符的模式。


2
你需要查看你的telnet客户端文档(例如手册页面)。
例如,我的(Linux上krb5-1.6 telnet实现)有一个"模式字符"命令,它关闭了本地行缓冲,并立即发送大多数字符。
编辑:
禁用其他一些客户端(如PuTTY)中的本地行编辑会产生相同的结果。PuTTY还具有"原始"连接模式,可能会有用。
请注意,如果您不介意在服务器上实现TELNET协议的某些部分,则也可以远程禁用本地行编辑(LINEMODE TELNET选项)。
如果您不关心交互性,还可以使用netcat。
echo -n X | nc host 7777

很不幸,在许多系统(包括我的)中,netcat使用的是行缓冲的I/O。


1

我的建议是使用netcat来实现这个目的。它可以像即时通讯一样运行,而不需要按Enter键。搜索netcat教程可以获得大量视频。


1
我认为这只是世界在变化,现在我们有视频告诉我们如何使用基于控制台的程序。我可以欣赏其中的幽默;-) - Asherah

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