显示 Telnet 客户端收到的消息时的延迟
Delay when displaying a message received by a Telnet client
我正在尝试在 VB.NET 中实现 Telnet 客户端。我以 this 代码为例:
我正在实施的程序工作如下:
- 我单击“打开 Telnet”按钮以打开 Telnet 会话。
- 我在左边的文本框中写了一条指令(字符串),然后我点击
Button1
将消息发送到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
因为您没有读取响应的整个缓冲区,您只是从中取出 256 个字节:
data = New [Byte](256) {} ' <-
您还必须通过关闭流和 TcpClient once you receive the response. Always create the disposable objects by the Using 语句来保证释放资源。
Synchronous Example
下面的示例以同步阻塞模式连接到端点,调用者线程被阻塞,直到从端点返回响应或抛出异常(例如连接超时)。
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
Asynchronous Example
您可能想使用异步操作,因为您正在开发 WinForms 应用程序,我认为您不想阻塞 UI 线程。这里需要调用TcpClient和read/write流的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
只是一种将响应附加到文本框的方法。
Private Sub UpdateStatus(txt As String)
StatusTextBox.AppendText(txt)
StatusTextBox.AppendText(ControlChars.NewLine)
End Sub
我正在尝试在 VB.NET 中实现 Telnet 客户端。我以 this 代码为例:
我正在实施的程序工作如下:
- 我单击“打开 Telnet”按钮以打开 Telnet 会话。
- 我在左边的文本框中写了一条指令(字符串),然后我点击
Button1
将消息发送到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
因为您没有读取响应的整个缓冲区,您只是从中取出 256 个字节:
data = New [Byte](256) {} ' <-
您还必须通过关闭流和 TcpClient once you receive the response. Always create the disposable objects by the Using 语句来保证释放资源。
Synchronous Example
下面的示例以同步阻塞模式连接到端点,调用者线程被阻塞,直到从端点返回响应或抛出异常(例如连接超时)。
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
Asynchronous Example
您可能想使用异步操作,因为您正在开发 WinForms 应用程序,我认为您不想阻塞 UI 线程。这里需要调用TcpClient和read/write流的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
只是一种将响应附加到文本框的方法。
Private Sub UpdateStatus(txt As String)
StatusTextBox.AppendText(txt)
StatusTextBox.AppendText(ControlChars.NewLine)
End Sub