通过 PuTTY 与 TCP 服务器通信

Posted

技术标签:

【中文标题】通过 PuTTY 与 TCP 服务器通信【英文标题】:Communicating with a TCP server via PuTTY 【发布时间】:2021-12-29 04:12:54 【问题描述】:

如何通过从 PuTTY 客户端键入“退出”一词来关闭连接?这是我的代码:

package main

import (
    "bufio"
    "fmt"
    "net"
)

func main() 

    //Ascolta richiesta
    datastream, err := net.Listen("tcp", ":8080")

    if err != nil 
        fmt.Println(err)
        return
    
    defer datastream.Close()

    //Accetta richiesta
    for 
        connessione, err := datastream.Accept()
        if err != nil 
            fmt.Println(err)
            return
        

        go handle(connessione)
    


//Connessione Handle > Thread
func handle(connessione net.Conn) 

    fmt.Println("Scrivere exit per uscire")

    for 
        data, err := bufio.NewReader(connessione).ReadString('\n')

        

        if err != nil 
            fmt.Print(err)
            return
        
        fmt.Println(data)

    


【问题讨论】:

【参考方案1】:

如果我没记错的话,PuTTY 可以使用多种协议来访问服务器,其中SSH 和Telnet 使用最多。

大多数时候人们使用PuTTY访问SSH服务器,在SSH会话中输入“exit”来终止它的语义比较复杂:大多数时候,服务器运行一个command-line shell,SSH协议携带该shell和SSH客户端之间的数据,因此当用户在客户端(例如PuTTY)中键入“exit”时,客户端将其发送到SSH服务器并将其发送到shell; shell 解释该命令,终止它的指令,SSH 服务器检测到它并关闭它的会话。 回顾一下,PuTTY 不会以任何方式自行处理在其中输入“exit”。它只是将该字符串发送到远程端。

您没有告诉我们,但是在您的代码中,看起来您编写了一个普通的 TCP 服务器(我的意思是,不是 SSH 服务器),因此看起来 PuTTY 使用 Telnet 访问您的服务器。 在这种情况下,在 PuTTY 中键入“exit”会使其将该行直接发送到您的 Go 服务器,因此要终止您的服务器,您必须解释发送给它的内容,一旦它收到“exit”行,它必须终止处理循环并关闭连接。

从你的代码来看,应该是这样的

func handle(conn net.Conn) 
    defer conn.Close()

    fmt.Println("Scrivere exit per uscire")

    for 
        data, err := bufio.NewReader(conn).ReadString('\n') 

        if err != nil 
            fmt.Print(err)
            return
        

        fmt.Println(data)
        if data == "exit" 
            return
        
    

我们来了

    添加了关闭连接的deferred 语句,这样每次我们退出连接“处理程序”时都会发生这种情况。 不包括在内是您原始代码中的一个错误,该错误会导致服务器最终资源耗尽。 添加了一个if 语句,该语句分析从客户端发送的文本行,并在该行“退出”时终止处理程序。

更新在评论线程之后。

问题

有一个转折导致我最初提供的修复无法使用:

bufio.Reader.ReadString(delim byte) 记录如下

由于代码要求ReadString 一直读取到\n,当客户端发送“exit”后跟一个换行符(ASCII LF,0x0a)时,ReadString 精确返回“exit”加上该 LF 字符单件。

PuTTY 显然使用的 Telnet 协议会终止客户端发送的每一行CRLF sequence、0x0b、0x0a,这就是发送到服务器的内容,因此我示例中的 if 语句正在接收“exit \r\n"。

实际上,如果 OP 在其调试输出语句中使用 fmt.Printf("%q\n", data) 之类的东西,这两个事实都可以很容易地看到 - 因为 %q 格式化动词使任何不可打印的字符在输出中“可见”。

解决方案

首先,原始代码有一个我在第一次阅读时没有发现的错误:每次循环迭代都会创建一个bufio.Reader 的新实例——如果有的话,会丢弃前一个。但是这种类型被明确记录为有权缓冲来自源读取器的任意数量的数据——也就是说,它能够从读取器中消耗更多的数据,而不是返回给调用任何类型的客户端的数据。阅读方法。 这意味着当 bufio.Reader 被丢弃时,它自上次读取调用以来缓冲的数据也将被丢弃。 这个错误很严重,需要修复。

其次,我们需要决定如何处理换行符终止字符。 有多种选择:

准确指定服务器在其协议中支持的换行符格式,然后实现对该规范的支持。

例如,我们可以假设有线协议中的每一行都必须由一个 LF 终止。 在这种情况下,我们可能(几乎)保持原样,但将客户端提交的字符串与“exit\n”进行比较。

在这种情况下,通过 Telnet 协议与服务器通信将工作,因为 Telnet 客户端会终止用户使用 CRLF 序列提交的行。

对协议松懈,允许 LF 和 CRLF。

为了支持这一点,代码应该以某种方式处理客户端线路以任何一种方式终止的可能性。 例如,这可以通过在比较语句中使用输入行之前修剪输入行来完成。

我想说最简单的方法是使用第二个选项并使用bufio.Scanner 类型:

func handle(conn net.Conn) 
  defer conn.Close()

  scanner = bufio.NewScanner(conn)
  for scanner.Scan() 
    data := scanner.Text()

    fmt.Printf("%q\n", data)

    if data == "exit" 
      return
    
  

  if err := scanner.Err(); err != nil 
    fmt.Println("error:", err)
  

【讨论】:

是的,和你说的一样,但是问题是我试了这段代码还是不行,我试了千百种方法,但是exit命令读不出来,服务器继续去... 你完全明白我需要什么,但是尝试代码,用命令“exit”它不出来。 @FratmCarnal,使用您未修改的代码(Linux,纯 telnet)为我工作:pastebin.com/zQEhtavU> 如您所见,我在 telnet 上输入了“exit”,按 Enter ,并且它已由您的服务器成功传输和处理。可能是您遇到了一些奇怪的网络问题(如 Windows 防火墙干预或诸如此类)或 PuTTY 设置。 (后者可以通过在 Windows 上安装 telnet 来绕过——你可以用谷歌搜索如何做到这一点。) 如果我将 Visual Studio Code 与 Go 语言一起使用,它对我不起作用 我从 PuTTY shell 发送到达 Visual Studio 代码终端的数据,在写入“exit”后连接没有关闭。

以上是关于通过 PuTTY 与 TCP 服务器通信的主要内容,如果未能解决你的问题,请参考以下文章

Qt Serial Communictaion - 通过 Putty 与 stm32f0 板通信

Web浏览器与Web服务器之间的通信过程

HTTP通信机制

tcp通信

二http通信 http与tcp关系

http通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤