1

我正在尝试在 VB.NET 中实现 Telnet 客户端。我以以下代码为例:

我正在实施的程序如下:

  1. 我单击“打开 Telnet”按钮打开 Telnet 会话。
  2. 我在左侧的文本框中写了一条指令(字符串),然后单击Button1以将消息发送到 Telnet 服务器(带有嵌入式以太网端口的电子板)。
  3. Telnet 服务器发送的应答显示在左侧的文本框中。

Telnet 客户端表格

我在示例和正在执行的实现中遇到的问题是消息显示延迟。例如,如果我发送字符串1FFFFFF + vbCrLf,我应该会收到来自服务器的消息,说Unknown HMI code!. 我已经与 Wireshark 确认了该消息是在我使用 VB.NET 程序发送指令之后由 Telnet 服务器发送的,但只有当我Button1再次单击时它才会显示在右侧的文本框中(无论写什么在左侧的文本框中)。

您能否告诉我代码中是否缺少某些内容?

下面是我的代码:

Imports System
Imports System.IO
Imports System.Net.Sockets
Imports System.Security.Cryptography.X509Certificates
Imports System.Text
Imports System.Threading
Imports System.Net.Http
Imports System.Net.Security
Imports System.Net.IPAddress
Imports System.Net

Public Class Form1
    ' Create a TcpClient.
    Dim client As New TcpClient
    Dim stream As NetworkStream

    ' Function to write/read a TCP stream. 
    Shared Sub Connect(server As [String], message As [String])
        Try
            ' Translate the passed message into ASCII and store it as a Byte array.
            Dim data As [Byte]() = System.Text.Encoding.ASCII.GetBytes(message)

            ' Send the message to the connected TcpServer. 
            Form1.stream.Write(data, 0, data.Length)

            Console.WriteLine("Sent: {0}", message)

            ' Buffer to store the response bytes.
            data = New [Byte](256) {}

            ' String to store the response ASCII representation.
            Dim responseData As [String] = [String].Empty

            ' Read the first batch of the TcpServer response bytes.
            Dim bytes As Int32 = Form1.stream.Read(data, 0, data.Length)
            responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes)
            Console.WriteLine("Received: {0}", responseData)
            Form1.TelnetRx.Text += responseData + vbCrLf
            Form1.TelnetRx.Refresh()

        Catch e As ArgumentNullException
            Console.WriteLine("ArgumentNullException: {0}", e)
        Catch e As SocketException
            Console.WriteLine("SocketException: {0}", e)
        End Try

        Console.WriteLine(ControlChars.Cr + " Press Enter to continue...")
        Console.Read()
    End Sub

    ' Function to open a Telnet session.
    Public Function OpenTelnetSession(server As String, Port As Int32) As Boolean
        Dim ipAddress As IPAddress = Parse(server)
        client.Connect(ipAddress, Port)
        stream = Me.client.GetStream()
        Return True
    End Function

    ' Button to send a message.
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Connect("192.168.8.110", TCP_Order.Text + vbCrLf)
    End Sub

    ' Button to open the Telnet session.
    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles OpenTelnet.Click
        OpenTelnetSession("192.168.8.110", 10001)
    End Sub

    ' Button to close the Telnet session.
    Private Sub CloseTelnet_Click(sender As Object, e As EventArgs) Handles CloseTelnet.Click

    End Sub
End Class
4

1 回答 1

1

那是因为您没有读取响应的整个缓冲区,所以您只是从中获取 256 个字节:

data = New [Byte](256) {} ' <-

此外,您必须在收到响应后通过关闭流和TcpClient来释放资源。始终通过Using语句创建一次性对象以保证这一点。

同步示例


下面的示例以同步阻塞模式连接到端点,调用者线程被阻塞,直到端点返回响应或抛出异常(例如连接超时)。

Private Function Connect(server As String, port As Integer, Msg As String) As String
    Using client As New TcpClient(server, port),
        netStream = client.GetStream,
        sr = New StreamReader(netStream, Encoding.UTF8)

        Dim msgBytes = Encoding.UTF8.GetBytes(Msg)

        netStream.Write(msgBytes, 0, msgBytes.Length)

        Return sr.ReadToEnd
    End Using
End Function

和来电者:

Private Sub TheCaller()
    Dim resp As String = Nothing

    Try
        Dim server = "192.168.8.110"
        Dim port = 10001
        Dim msg = $"1FFFFFF{ControlChars.CrLf}{ControlChars.CrLf}"
        
        resp = Connect(server, port, msg)
    Catch ex As ArgumentNullException
        resp = ex.Message
    Catch ex As SocketException
        resp = ex.SocketErrorCode.ToString
    Catch ex As Exception
        resp = ex.Message
    Finally
        If resp IsNot Nothing Then
            UpdateStatus(resp)
        End If
    End Try
End Sub

异步示例


由于您正在开发 WinForms 应用程序,因此您可能希望使用异步操作,而且我认为您不想阻塞 UI 线程。这里需要调用TcpClient和读/写流的Async方法:

Private Async Function ConnectAsync(server As String,
                                    port As Integer, msg As String) As Task(Of String)
    Using client As New TcpClient
        Await client.ConnectAsync(server, port)

        Using netStream = client.GetStream,
            sw = New StreamWriter(netStream, Encoding.UTF8) With {.AutoFlush = True },
            sr = New StreamReader(netStream, Encoding.UTF8)

            Await sw.WriteLineAsync(msg)

            Return Await sr.ReadToEndAsync()
        End Using
    End Using
End Function

Async来电者:

Private Async Sub TheCaller()
    Dim resp As String = Nothing

    Try
        Dim server = "192.168.8.110"
        Dim port = 10001
        Dim msg = $"1FFFFFF{ControlChars.CrLf}{ControlChars.CrLf}"
        
        resp = Await ConnectAsync(server, port, msg)
    Catch ex As ArgumentNullException
        resp = ex.Message
    Catch ex As SocketException
        resp = ex.SocketErrorCode.ToString
    Catch ex As Exception
        resp = ex.Message
    Finally
        If resp IsNot Nothing Then
            UpdateStatus(resp)
        End If
    End Try
End Sub

代码片段中的UpdateStatus只是将响应附加到 TextBox 的一种方法。

Private Sub UpdateStatus(txt As String)
    StatusTextBox.AppendText(txt)
    StatusTextBox.AppendText(ControlChars.NewLine)
End Sub
于 2020-06-20T21:40:52.900 回答