VB.NET重定向Tshark捕获命令输出时StreamReader.ReadLine阻塞问题
解决VB.NET中Tshark实时抓包输出重定向阻塞问题
我来帮你分析下这个问题的根源,再给你一套可行的解决方案:
问题诊断
你的第二条命令之所以会阻塞,核心原因在于Tshark的输出缓冲机制:
- 第一条命令输出的字段多(帧号、IP地址等),每个数据包的输出内容足够长,很快就会填满Tshark的输出缓冲区,触发内容刷新,所以
ReadLine()能正常读到换行符。 - 第二条命令只输出
dns.qry.name,内容极短,Tshark默认使用块缓冲(攒够缓冲区才会输出),不会立刻把单条DNS记录刷到输出流里,导致ReadLine()一直等待换行符,最终阻塞。
另外,你原代码还有一个隐患:先读完标准输出再读标准错误,一旦Tshark往错误输出写内容并填满缓冲区,整个进程会卡住,反过来也会影响标准输出的读取。
解决方案
1. 修改Tshark命令,强制行缓冲
给第二条命令加上-l参数,这个参数会强制Tshark使用行缓冲,每捕获到一个数据包就立刻输出一行,不会攒缓冲区:
-i 10 -l -T fields -e dns.qry.name src port 53
2. 改用异步事件读取输出,避免阻塞
放弃同步的StreamReader.ReadLine()方式,改用Process的OutputDataReceived和ErrorDataReceived异步事件,同时处理标准输出和错误输出,彻底解决阻塞问题。
修改后的完整代码:
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 ' 加入-l参数强制行缓冲 Dim oStartInfo As New ProcessStartInfo("C:\Program Files\Wireshark\tshark.exe", "-i 10 -l -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 ' 绑定输出/错误事件处理程序 AddHandler oProcess.OutputDataReceived, AddressOf Process_OutputDataReceived AddHandler oProcess.ErrorDataReceived, AddressOf Process_ErrorDataReceived oProcess.StartInfo = oStartInfo Catch ex As Exception MsgBox(ex.Message) 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 oProcess.Start() ' 启动异步读取 oProcess.BeginOutputReadLine() oProcess.BeginErrorReadLine() ' 等待进程结束(直到手动停止) oProcess.WaitForExit() Catch ex As Exception MsgBox(ex.Message) End Try End Sub ' 处理标准输出 Private Sub Process_OutputDataReceived(sender As Object, e As DataReceivedEventArgs) If Not String.IsNullOrEmpty(e.Data) Then ' 跨线程更新UI需要Invoke确保线程安全 Me.Invoke(Sub() output &= e.Data & vbNewLine TextBox1.Text = output TextBox1.Select(TextBox1.TextLength, 0) End Sub) End If End Sub ' 处理标准错误 Private Sub Process_ErrorDataReceived(sender As Object, e As DataReceivedEventArgs) If Not String.IsNullOrEmpty(e.Data) Then Me.Invoke(Sub() output &= "[ERROR] " & e.Data & vbNewLine TextBox1.Text = output TextBox1.Select(TextBox1.TextLength, 0) End Sub) End If End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click ' 停止并清理进程 If oProcess IsNot Nothing AndAlso Not oProcess.HasExited Then oProcess.Kill() oProcess.Dispose() oProcess = New Process() End If Button1.Enabled = True Button2.Enabled = False output = "" TextBox1.Text = "" End Sub Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load End Sub End Class
代码改进点说明
- 移除了原代码中不必要的
Thread.Sleep(2000),避免无意义的延迟。 - 用异步事件替代同步读取,彻底解决输出/错误缓冲区阻塞的问题。
- 跨线程更新UI时使用
Invoke,避免WinForm的线程安全报错。 - 停止按钮中主动Kill并释放进程资源,避免Tshark后台残留。
内容的提问来源于stack exchange,提问作者Mohammad Fneish
相关产品推荐
相关产品推荐

