VB.NET Перенаправление вывода команды захвата tshark застряло в StreamReader.ReadLine

Я пытаюсь перенаправить выходные данные standard и error моего процесса (написанного на VB.NET), где я выполняю непрерывную команду. Это команда tshark (инструмент командной строки Wireshark), который фиксирует сетевой трафик во время выполнения. Я попробовал обе следующие команды:

  1. -i 5 -B 1 -w /sample.pcap --print -Tfields -e frame.number -e ip.addr -e tcp -e _ws.col.Info -E separator=/t
  2. -i 10 -T fields -e dns.qry.name src port 53

Обе команды отлично работают в командной строке. Однако при попытке перенаправить вывод в коде работает только команда номер 1, а вторая команда зависает при выполнении StreamReader.ReadLine.

Обратите внимание, что я знаю, что ReadLine ожидает новой строки, которая будет прочитана потоком, где обе приведенные выше команды генерируют новую строку вывода для каждого захваченного пакета. Я также пытался использовать Read и ReadBlock (относительно необходимых изменений в коде), но ни одна из них не работала для второй команды.

Вот мой код:

Public Class Form1

    Dim output As String
    Dim oProcess As New Process()



    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Try
                Dim oStartInfo As New ProcessStartInfo("C:\Program Files\Wireshark\tshark.exe", "-i 10 -T fields -e dns.qry.name src port 53")
                oStartInfo.UseShellExecute = False
                oStartInfo.RedirectStandardOutput = True
                oStartInfo.RedirectStandardError = True
                oStartInfo.CreateNoWindow = True
                oStartInfo.WindowStyle = ProcessWindowStyle.Hidden
                oProcess.StartInfo = oStartInfo
            Catch ex As Exception
                MsgBox(ex)
            End Try
            BackgroundWorker1.RunWorkerAsync()
            Button1.Enabled = False
            Button2.Enabled = True
        End Sub

        Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
            Try
                Threading.Thread.Sleep(2000)
                If oProcess.Start() Then
                    Dim sOutput As String
                    Using oStreamReader As System.IO.StreamReader = oProcess.StandardOutput
                        sOutput = oStreamReader.ReadLine
                        While Not sOutput Is Nothing
                            output = sOutput & vbNewLine
                            BackgroundWorker1.ReportProgress(10)
                            sOutput = sOutput + vbNewLine + oStreamReader.ReadLine
                        End While
                    End Using

                    Using oStreamReader As System.IO.StreamReader = oProcess.StandardError
                        sOutput = oStreamReader.ReadLine
                        While Not sOutput Is Nothing
                            output = sOutput & vbNewLine
                            BackgroundWorker1.ReportProgress(10)
                            sOutput = sOutput + vbNewLine + oStreamReader.ReadLine
                        End While
                    End Using
                Else
                    MsgBox("Error starting the process")
                End If
            Catch ex As Exception
                MsgBox(ex)
            End Try

        End Sub

        Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
            TextBox1.Text = output
            TextBox1.Select(0, 0)
        End Sub

        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            BackgroundWorker1.CancelAsync()
            Button1.Enabled = True
            Button2.Enabled = False
        End Sub

        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        End Sub
    End Class

person Mohammad Fneish    schedule 19.05.2019    source источник
comment
Вам не нужен фоновый рабочий. Используйте асинхронные (управляемые событиями) BeginOutputReadLine() и BeginErrorReadLine() в сочетании с событием Exited. Пример кода здесь (вывод идет в RichTextBox. Конечно, вы можете обновить любой другой элемент управления).   -  person Jimi    schedule 19.05.2019
comment
@Jimi спасибо, я попробовал это сейчас, когда он читает первые 3 строки команды (эти строки подсказывают, что использование этого захвата началось) и прекращает чтение чего-либо еще, я проверил, запускается ли событие Exited там, где его не было. Я начал думать, что может быть другой модуль записи вывода, который tshark использует для печати пакетов. что ты думаешь?   -  person Mohammad Fneish    schedule 19.05.2019
comment
Попробуйте это в качестве аргументов: "-i 10 -j ""http tcp ip"" -P -V". Вы должны увидеть массивный поток информации.   -  person Jimi    schedule 19.05.2019
comment
Затем попробуйте: "-i 10 -T fields -e frame.number -e ip.addr -e udp -e _ws.col.Info -j ""http tcp ip"" -P -V". Сузьте его после. Вы должны видеть некоторую информацию время от времени.   -  person Jimi    schedule 19.05.2019
comment
@Jimi, обе команды работают именно так, как вы упомянули, но моя (команда номер 2) все еще не печатается, есть идеи?   -  person Mohammad Fneish    schedule 19.05.2019
comment
Он ничего не фиксирует. Или будет в будущем. Этот процесс не остановится, пока вы его не остановите, так что... проверьте.   -  person Jimi    schedule 19.05.2019
comment
печатает только The NPF driver isn't running. You may have trouble capturing or listing interfaces. Capturing on 'Wi-Fi'   -  person Mohammad Fneish    schedule 19.05.2019
comment
Однако в cmd он фиксирует каждый трафик, который я делаю напрямую, например: The NPF driver isn't running. You may have trouble capturing or listing interfaces. Capturing on 'Wi-Fi' www.youtube.com www.google.com www.google.com.lb i.ytimg.com fonts.googleapis.com yt3.ggpht.com fonts.gstatic.com s.ytimg.com s3.amazonaws.com 1.1.168.198.in-addr.arpa 19.1.168.192.in-addr.arpa 1.1.168.198.in-addr.arpa 19.1.168.192.in-addr.arpa 13 packets captured   -  person Mohammad Fneish    schedule 19.05.2019
comment
Я не знаю, что вам сказать об этом. Возможно, это просто буферизация вывода. Запустите службу (net start npf) и посмотрите, что произойдет. Запустите процесс с повышенными правами.   -  person Jimi    schedule 19.05.2019
comment
Я не знаю, что такое npf и как насчет того, чтобы запустить его с повышенными правами.   -  person Mohammad Fneish    schedule 19.05.2019
comment
если вы имеете в виду NPF в приведенном выше выводе, я не знаю, что это такое, он всегда отображается, но в командной строке все работает нормально   -  person Mohammad Fneish    schedule 19.05.2019
comment
@Jimi, я изменил свою командную строку на -i 10 -w ./sample.pcap -P -V --print -Tfields -e dns.qry.name src port 53, и это после прочтения документов tshark, в которых говорится If you want the packets to be displayed to the standard output and also saved to a file, specify the -P option in addition to the -w option to have the summary line displayed, теперь он печатает 4 пакета, а затем возникает событие process exited   -  person Mohammad Fneish    schedule 19.05.2019
comment
@Jimi, спасибо за вашу большую помощь, я только что решил проблему и разместил ответ ниже. Я очень ценю вашу помощь ‹3.   -  person Mohammad Fneish    schedule 20.05.2019


Ответы (1)


Все это было проблемой tshark и никогда не было проблемой VB.Net. Как упомянул Mr.Kurt Knochner при ответе на вопрос Как передать вывод tshark в реальном времени?:

вывод tshark буферизуется. Пожалуйста, используйте опцию tshark -l, если вы хотите, чтобы tshark сбрасывал STDOUT после каждого пакета.

И, обратившись к документации tshark здесь:

-l Сбрасывать стандартный вывод после печати информации для каждого пакета.

Поэтому, чтобы заставить его работать, я добавил параметр --print вместе с -l в свою командную строку, и теперь он работает как шарм, где теперь он выглядит так:

tshark --print -l -i 10 -w ./sample.pcap -E separator=/t -T fields -e frame.number -e dns.qry.name src port 53

Вот моя окончательная версия кода:

Public Class Form1

    Dim outputQueue As New Queue(Of String)
    Dim captureAdapterID As Integer = 0
    Dim oProcess As Process

    Private Sub Button1_Click(sender1 As Object, e1 As EventArgs) Handles Button1.Click
        Button1.Enabled = False
        Button2.Enabled = True
        captureAdapterID = (ComboBox1.SelectedIndex + 1)
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender1 As Object, e1 As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Try
            oProcess = New Process()
            Dim oStartInfo As New ProcessStartInfo("C:\Program Files\Wireshark\tshark.exe", " --print -l -i " & captureAdapterID & " -w ./sample.pcap -E separator=/t -T fields -e frame.number -e dns.qry.name src port 53")
            oStartInfo.WorkingDirectory = New Uri(System.Windows.Forms.Application.StartupPath).LocalPath
            oStartInfo.UseShellExecute = False
            oStartInfo.RedirectStandardOutput = True

            oStartInfo.RedirectStandardError = True
            oStartInfo.CreateNoWindow = True
            oStartInfo.WindowStyle = ProcessWindowStyle.Hidden
            oProcess.StartInfo = oStartInfo

            If oProcess.Start() Then
                appendOutput("Capturing on device: " & captureAdapterID & " started.")
                Dim sOutput As String
                Using oStreamReader As System.IO.StreamReader = oProcess.StandardOutput
                    sOutput = oStreamReader.ReadLine
                    While Not sOutput Is Nothing
                        appendOutput(sOutput)
                        sOutput = oStreamReader.ReadLine
                    End While
                End Using

                Using oStreamReader As System.IO.StreamReader = oProcess.StandardError
                    sOutput = oStreamReader.ReadLine
                    While Not sOutput Is Nothing
                        appendOutput(sOutput)
                        sOutput = oStreamReader.ReadLine
                    End While
                End Using

                MsgBox("finished")
            Else
                MsgBox("Error starting the process")
            End If
        Catch ex As Exception
            MsgBox(ex.Message)
        Finally
            BackgroundWorker1.ReportProgress(10)
        End Try

    End Sub

    Private Sub appendOutput(sOutput As String)
        outputQueue.Enqueue(sOutput)
        BackgroundWorker1.ReportProgress(10)
    End Sub

    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Dim i As Integer = 0
        For i = 0 To outputQueue.Count - 1 Step 1
            RichTextBox1.AppendText(outputQueue.Dequeue & vbNewLine)
        Next
        RichTextBox1.ScrollToCaret()
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        BackgroundWorker1.CancelAsync()
        oProcess.Kill()
        Button1.Enabled = True
        Button2.Enabled = False
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Try
            Dim process As New Process()
            Dim oStartInfo As New ProcessStartInfo("C:\Program Files\Wireshark\tshark.exe", " -D")
            oStartInfo.WorkingDirectory = New Uri(System.Windows.Forms.Application.StartupPath).LocalPath
            oStartInfo.UseShellExecute = False
            oStartInfo.RedirectStandardOutput = True

            oStartInfo.RedirectStandardError = True
            oStartInfo.CreateNoWindow = True
            oStartInfo.WindowStyle = ProcessWindowStyle.Hidden
            process.StartInfo = oStartInfo

            If process.Start() Then

                Dim sOutput As String
                Using oStreamReader As System.IO.StreamReader = process.StandardOutput
                    sOutput = oStreamReader.ReadToEnd
                    If Not sOutput Is Nothing Then
                        ComboBox1.Items.AddRange(sOutput.Trim.Split(vbNewLine))
                        Try
                            ComboBox1.SelectedIndex = 0
                        Catch ex As Exception

                        End Try
                    End If
                End Using
            Else
                MsgBox("Error starting the get adapter process failed")
            End If
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub
End Class

person Mohammad Fneish    schedule 20.05.2019