Задержка при отображении сообщения, полученного клиентом Te lnet - PullRequest
1 голос
/ 19 июня 2020

Я пытаюсь реализовать клиент Te lnet в VB. NET. Я следую этому коду в качестве примера:

Программа, которую я реализую, работает следующим образом:

  1. Я нажимаю кнопку «Открыть Te lnet» чтобы открыть сеанс Te lnet.
  2. Я пишу инструкцию (строку) в текстовом поле слева, а затем нажимаю Button1, чтобы отправить сообщение на сервер Te lnet ( Электронная плата c со встроенным портом Ethe rnet).
  3. Ответ, отправленный сервером Te lnet, отображается в текстовом поле слева.

Форма для клиента Te lnet

Проблема как с примером, так и с реализацией, которую я делаю, заключается в том, что сообщения отображаются с задержкой. Например, если я отправлю строку 1FFFFFF + vbCrLf, я должен получить сообщение от сервера, в котором говорится Unknown HMI code!. Я проверил с помощью Wireshark, что сообщение отправляется сервером Te lnet сразу после того, как я отправил инструкцию с помощью программы VB. NET, но оно отображается в текстовом поле справа, только если я нажимаю Button1 a второй раз (независимо от того, что написано в текстовом поле слева).

Не могли бы вы сказать мне, что мне не хватает в коде?

Ниже мой код :

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

1 Ответ

1 голос
/ 21 июня 2020

Это потому, что вы не читаете весь буфер ответа, вы просто берете из него 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, и я не думаю, что вы хотите блокировать поток пользовательского интерфейса. Здесь вам нужно вызвать Async методы TcpClient и потоков чтения / записи:

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
...