找回密码
 注册
搜索
系统gho:最纯净好用系统下载站投放广告、加入VIP会员,请联系 微信:wuyouceo
查看: 210|回复: 11

[分享] iperf3 简单GUI

[复制链接]
发表于 昨天 19:44 | 显示全部楼层 |阅读模式
本帖最后由 kedion 于 2026-5-21 19:49 编辑

iperf3一款主动式网络性能测量工具,主要用途是测量IP网络上可达的最大带宽。它能模拟并测量TCP、UDP和SCTP等主流协议的吞吐量、丢包率、抖动和重传等关键网络性能指标,由于原版iperf3是个命令行程序,并且是全英文状态,不适合普通人测试使用,所以通过deepseek使用powershell写了个简单GUI界面,方便普通人测试自己主机网络情况,由于代码是全有deepseek编写,欢迎大家反馈问题
  1. <font size="3">Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force

  2. $scriptDirectory = $PSScriptRoot
  3. if (-not $scriptDirectory) { $scriptDirectory = Get-Location }

  4. # 自动查找 iperf3.exe
  5. $script:iperfPath = $null
  6. try {
  7.     $searchPaths = @(
  8.         (Join-Path $scriptDirectory "iperf3.exe"),
  9.         (Join-Path ([System.IO.Path]::GetTempPath()) "iperf3.exe")
  10.     )
  11.     foreach ($path in $searchPaths) {
  12.         if (Test-Path $path) { $script:iperfPath = $path; break }
  13.     }
  14.     if (-not $script:iperfPath -and (Test-Path "iperf3.exe")) {
  15.         $script:iperfPath = (Resolve-Path "iperf3.exe").Path
  16.     }
  17. } catch {
  18.     $script:iperfPath = $null
  19. }

  20. Add-Type -AssemblyName System.Windows.Forms, System.Drawing

  21. # 全局变量
  22. $script:iperfProcess       = $null
  23. $script:running            = $false
  24. $script:modifiedAdapters   = @{}
  25. $script:logfilePath        = ""
  26. $script:isAdministrator    = $false
  27. $script:tempOutputFile     = ""
  28. $script:lastReadPosition   = 0
  29. $script:updateTimer        = $null
  30. $script:monitorTimer       = $null

  31. # ================= 主窗体 =================
  32. $form = New-Object System.Windows.Forms.Form
  33. $form.Text = "iperf3 图形界面 + 网络配置工具 (管理员模式)"
  34. $form.Size = New-Object System.Drawing.Size(1150, 950)   # 增加高度
  35. $form.StartPosition = "CenterScreen"
  36. $form.Add_FormClosing({
  37.     param($obj, $e)
  38.     if ($script:running) { Stop-IperfTest }
  39.     Restore-ModifiedNetworkConfigurations
  40. })

  41. $mainTable = New-Object System.Windows.Forms.TableLayoutPanel
  42. $mainTable.Dock = "Fill"
  43. $mainTable.ColumnCount = 2
  44. $mainTable.RowCount = 1
  45. [void]$mainTable.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Absolute, 400)))  # 缩减左侧宽度
  46. [void]$mainTable.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Percent, 100)))
  47. $form.Controls.Add($mainTable)

  48. # ----------------- 左侧面板 -----------------
  49. $leftPanel = New-Object System.Windows.Forms.Panel
  50. $leftPanel.Dock = "Fill"
  51. $leftPanel.AutoScroll = $true
  52. $mainTable.Controls.Add($leftPanel, 0, 0)

  53. $flowPanel = New-Object System.Windows.Forms.FlowLayoutPanel
  54. $flowPanel.Dock = "Fill"
  55. $flowPanel.FlowDirection = "TopDown"
  56. $flowPanel.AutoScroll = $true
  57. $flowPanel.Padding = New-Object System.Windows.Forms.Padding(10)
  58. $flowPanel.WrapContents = $false
  59. $leftPanel.Controls.Add($flowPanel)

  60. # ----- 1. 运行模式 -----
  61. $groupMode = New-Object System.Windows.Forms.GroupBox
  62. $groupMode.Text = "运行模式"
  63. $groupMode.Width = 370
  64. $groupMode.Height = 65
  65. $flowPanel.Controls.Add($groupMode)

  66. $radioClient = New-Object System.Windows.Forms.RadioButton
  67. $radioClient.Text = "客户端 (Client)"
  68. $radioClient.Location = New-Object System.Drawing.Point(15, 25)
  69. $radioClient.Size = New-Object System.Drawing.Size(130, 25)
  70. $radioClient.Checked = $true
  71. $groupMode.Controls.Add($radioClient)

  72. $radioServer = New-Object System.Windows.Forms.RadioButton
  73. $radioServer.Text = "服务器 (Server)"
  74. $radioServer.Location = New-Object System.Drawing.Point(160, 25)
  75. $radioServer.Size = New-Object System.Drawing.Size(130, 25)
  76. $groupMode.Controls.Add($radioServer)

  77. # ----- 2. 网络 IP 配置 -----
  78. $groupNet = New-Object System.Windows.Forms.GroupBox
  79. $groupNet.Text = "网络 IP 配置 (退出时恢复原始配置)"
  80. $groupNet.Width = 370
  81. $groupNet.Height = 230   # 增加高度
  82. $flowPanel.Controls.Add($groupNet)

  83. $yNet = 20
  84. $labelWidth = 80
  85. $ctrlStartX = 90
  86. $ctrlWidth = 170

  87. $lblAdapter = New-Object System.Windows.Forms.Label
  88. $lblAdapter.Text = "网卡适配器:"
  89. $lblAdapter.Location = New-Object System.Drawing.Point(10, $yNet)
  90. $lblAdapter.Size = New-Object System.Drawing.Size($labelWidth, 25)
  91. $groupNet.Controls.Add($lblAdapter)

  92. $comboAdapter = New-Object System.Windows.Forms.ComboBox
  93. $comboAdapter.DropDownStyle = "DropDownList"
  94. $comboAdapter.Location = New-Object System.Drawing.Point($ctrlStartX, $yNet)
  95. $comboAdapter.Size = New-Object System.Drawing.Size(260, 25)
  96. $groupNet.Controls.Add($comboAdapter)
  97. $yNet += 35

  98. $lblCurrentIP = New-Object System.Windows.Forms.Label
  99. $lblCurrentIP.Text = "当前IP地址:"
  100. $lblCurrentIP.Location = New-Object System.Drawing.Point(10, $yNet)
  101. $lblCurrentIP.Size = New-Object System.Drawing.Size($labelWidth, 25)
  102. $groupNet.Controls.Add($lblCurrentIP)

  103. $txtCurrentIP = New-Object System.Windows.Forms.TextBox
  104. $txtCurrentIP.ReadOnly = $true
  105. $txtCurrentIP.BackColor = "LightGray"
  106. $txtCurrentIP.Location = New-Object System.Drawing.Point($ctrlStartX, $yNet)
  107. $txtCurrentIP.Size = New-Object System.Drawing.Size(260, 23)
  108. $groupNet.Controls.Add($txtCurrentIP)
  109. $yNet += 35

  110. $lblNewIP = New-Object System.Windows.Forms.Label
  111. $lblNewIP.Text = "新IP地址:"
  112. $lblNewIP.Location = New-Object System.Drawing.Point(10, $yNet)
  113. $lblNewIP.Size = New-Object System.Drawing.Size($labelWidth, 25)
  114. $groupNet.Controls.Add($lblNewIP)

  115. $txtNewIP = New-Object System.Windows.Forms.TextBox
  116. $txtNewIP.Location = New-Object System.Drawing.Point($ctrlStartX, $yNet)
  117. $txtNewIP.Size = New-Object System.Drawing.Size($ctrlWidth, 23)
  118. $groupNet.Controls.Add($txtNewIP)
  119. $yNet += 35

  120. $lblSubnet = New-Object System.Windows.Forms.Label
  121. $lblSubnet.Text = "子网掩码:"
  122. $lblSubnet.Location = New-Object System.Drawing.Point(10, $yNet)
  123. $lblSubnet.Size = New-Object System.Drawing.Size($labelWidth, 25)
  124. $groupNet.Controls.Add($lblSubnet)

  125. $txtSubnet = New-Object System.Windows.Forms.TextBox
  126. $txtSubnet.Text = "255.255.255.0"
  127. $txtSubnet.Location = New-Object System.Drawing.Point($ctrlStartX, $yNet)
  128. $txtSubnet.Size = New-Object System.Drawing.Size($ctrlWidth, 23)
  129. $groupNet.Controls.Add($txtSubnet)
  130. $yNet += 35

  131. $btnApplyIP = New-Object System.Windows.Forms.Button
  132. $btnApplyIP.Text = "应用 IP 设置"
  133. $btnApplyIP.Location = New-Object System.Drawing.Point($ctrlStartX, $yNet)
  134. $btnApplyIP.Size = New-Object System.Drawing.Size(140, 35)
  135. $groupNet.Controls.Add($btnApplyIP)
  136. $yNet += 45

  137. $lblIPStatus = New-Object System.Windows.Forms.Label
  138. $lblIPStatus.Text = "就绪"
  139. $lblIPStatus.Location = New-Object System.Drawing.Point(10, $yNet)
  140. $lblIPStatus.Size = New-Object System.Drawing.Size(340, 25)
  141. $groupNet.Controls.Add($lblIPStatus)

  142. # ----- 3. iperf3 参数设置 -----
  143. $groupAll = New-Object System.Windows.Forms.GroupBox
  144. $groupAll.Text = "iperf3 测试参数设置"
  145. $groupAll.Width = 370
  146. $groupAll.Height = 420   # 增加高度
  147. $flowPanel.Controls.Add($groupAll)

  148. $y = 20

  149. $lblServer = New-Object System.Windows.Forms.Label
  150. $lblServer.Text = "服务器地址:"
  151. $lblServer.Location = New-Object System.Drawing.Point(10, $y)
  152. $lblServer.Size = New-Object System.Drawing.Size(75, 25)
  153. $groupAll.Controls.Add($lblServer)

  154. $txtServer = New-Object System.Windows.Forms.TextBox
  155. $txtServer.Text = "127.0.0.1"
  156. $txtServer.Location = New-Object System.Drawing.Point(90, $y)
  157. $txtServer.Size = New-Object System.Drawing.Size(260, 23)
  158. $groupAll.Controls.Add($txtServer)
  159. $y += 35

  160. $lblPort = New-Object System.Windows.Forms.Label
  161. $lblPort.Text = "端口:"
  162. $lblPort.Location = New-Object System.Drawing.Point(10, $y)
  163. $lblPort.Size = New-Object System.Drawing.Size(45, 25)
  164. $groupAll.Controls.Add($lblPort)

  165. $txtPort = New-Object System.Windows.Forms.TextBox
  166. $txtPort.Text = "5201"
  167. $txtPort.Location = New-Object System.Drawing.Point(55, $y)
  168. $txtPort.Size = New-Object System.Drawing.Size(70, 23)
  169. $groupAll.Controls.Add($txtPort)

  170. $lblProto = New-Object System.Windows.Forms.Label
  171. $lblProto.Text = "协议:"
  172. $lblProto.Location = New-Object System.Drawing.Point(135, $y)
  173. $lblProto.Size = New-Object System.Drawing.Size(45, 25)
  174. $groupAll.Controls.Add($lblProto)

  175. $comboProto = New-Object System.Windows.Forms.ComboBox
  176. $comboProto.DropDownStyle = "DropDownList"
  177. [void]$comboProto.Items.AddRange(@("tcp", "udp", "sctp"))
  178. $comboProto.SelectedIndex = 0
  179. $comboProto.Location = New-Object System.Drawing.Point(180, $y)
  180. $comboProto.Size = New-Object System.Drawing.Size(170, 25)
  181. $groupAll.Controls.Add($comboProto)
  182. $y += 35

  183. $lblDuration = New-Object System.Windows.Forms.Label
  184. $lblDuration.Text = "测试时长:"
  185. $lblDuration.Location = New-Object System.Drawing.Point(10, $y)
  186. $lblDuration.Size = New-Object System.Drawing.Size(75, 25)
  187. $groupAll.Controls.Add($lblDuration)

  188. $numHour = New-Object System.Windows.Forms.NumericUpDown
  189. $numHour.Location = New-Object System.Drawing.Point(85, $y)
  190. $numHour.Size = New-Object System.Drawing.Size(45, 23)
  191. $numHour.Minimum = 0
  192. $numHour.Maximum = 23
  193. $numHour.Value = 0
  194. $groupAll.Controls.Add($numHour)

  195. $lblHour = New-Object System.Windows.Forms.Label
  196. $lblHour.Text = "时"
  197. $lblHour.Location = New-Object System.Drawing.Point(135, $y)
  198. $lblHour.Size = New-Object System.Drawing.Size(25, 25)
  199. $groupAll.Controls.Add($lblHour)

  200. $numMinute = New-Object System.Windows.Forms.NumericUpDown
  201. $numMinute.Location = New-Object System.Drawing.Point(160, $y)
  202. $numMinute.Size = New-Object System.Drawing.Size(45, 23)
  203. $numMinute.Minimum = 0
  204. $numMinute.Maximum = 59
  205. $numMinute.Value = 0
  206. $groupAll.Controls.Add($numMinute)

  207. $lblMinute = New-Object System.Windows.Forms.Label
  208. $lblMinute.Text = "分"
  209. $lblMinute.Location = New-Object System.Drawing.Point(210, $y)
  210. $lblMinute.Size = New-Object System.Drawing.Size(25, 25)
  211. $groupAll.Controls.Add($lblMinute)

  212. $numSecond = New-Object System.Windows.Forms.NumericUpDown
  213. $numSecond.Location = New-Object System.Drawing.Point(235, $y)
  214. $numSecond.Size = New-Object System.Drawing.Size(45, 23)
  215. $numSecond.Minimum = 0
  216. $numSecond.Maximum = 59
  217. $numSecond.Value = 10
  218. $groupAll.Controls.Add($numSecond)

  219. $lblSecond = New-Object System.Windows.Forms.Label
  220. $lblSecond.Text = "秒"
  221. $lblSecond.Location = New-Object System.Drawing.Point(285, $y)
  222. $lblSecond.Size = New-Object System.Drawing.Size(25, 25)
  223. $groupAll.Controls.Add($lblSecond)
  224. $y += 35

  225. $lblParallel = New-Object System.Windows.Forms.Label
  226. $lblParallel.Text = "并行流数:"
  227. $lblParallel.Location = New-Object System.Drawing.Point(10, $y)
  228. $lblParallel.Size = New-Object System.Drawing.Size(75, 25)
  229. $groupAll.Controls.Add($lblParallel)

  230. $txtParallel = New-Object System.Windows.Forms.TextBox
  231. $txtParallel.Text = "1"
  232. $txtParallel.Location = New-Object System.Drawing.Point(85, $y)
  233. $txtParallel.Size = New-Object System.Drawing.Size(55, 23)
  234. $groupAll.Controls.Add($txtParallel)
  235. $y += 35

  236. $lblUdpBw = New-Object System.Windows.Forms.Label
  237. $lblUdpBw.Text = "UDP带宽(Mbps):"
  238. $lblUdpBw.Location = New-Object System.Drawing.Point(10, $y)
  239. $lblUdpBw.Size = New-Object System.Drawing.Size(110, 25)
  240. $groupAll.Controls.Add($lblUdpBw)

  241. $txtUdpBw = New-Object System.Windows.Forms.TextBox
  242. $txtUdpBw.Text = "100"
  243. $txtUdpBw.Location = New-Object System.Drawing.Point(120, $y)
  244. $txtUdpBw.Size = New-Object System.Drawing.Size(70, 23)
  245. $groupAll.Controls.Add($txtUdpBw)
  246. $y += 35

  247. # ----- 测试模式选项 -----
  248. $groupTestMode = New-Object System.Windows.Forms.GroupBox
  249. $groupTestMode.Text = "测试模式 (仅客户端)"
  250. $groupTestMode.Location = New-Object System.Drawing.Point(10, $y)
  251. $groupTestMode.Size = New-Object System.Drawing.Size(350, 125)
  252. $groupAll.Controls.Add($groupTestMode)

  253. $radioDefault = New-Object System.Windows.Forms.RadioButton
  254. $radioDefault.Text = "默认模式(客户端发送)"
  255. $radioDefault.Location = New-Object System.Drawing.Point(10, 25)
  256. $radioDefault.Size = New-Object System.Drawing.Size(200, 25)
  257. $radioDefault.Checked = $true
  258. $groupTestMode.Controls.Add($radioDefault)

  259. $radioReverse = New-Object System.Windows.Forms.RadioButton
  260. $radioReverse.Text = "反向测试 (服务器发送)"
  261. $radioReverse.Location = New-Object System.Drawing.Point(10, 50)
  262. $radioReverse.Size = New-Object System.Drawing.Size(200, 25)
  263. $groupTestMode.Controls.Add($radioReverse)

  264. $radioBidir = New-Object System.Windows.Forms.RadioButton
  265. $radioBidir.Text = "独立双向测试 (先客户端后服务器)"
  266. $radioBidir.Location = New-Object System.Drawing.Point(10, 75)
  267. $radioBidir.Size = New-Object System.Drawing.Size(300, 25)
  268. $groupTestMode.Controls.Add($radioBidir)

  269. $radioDualtest = New-Object System.Windows.Forms.RadioButton
  270. $radioDualtest.Text = "同时双向测试 (客户端与服务器同时)"
  271. $radioDualtest.Location = New-Object System.Drawing.Point(10, 100)
  272. $radioDualtest.Size = New-Object System.Drawing.Size(300, 25)
  273. $groupTestMode.Controls.Add($radioDualtest)

  274. $y += 135

  275. # ----- 日志文件选项 -----
  276. $chkLogfile = New-Object System.Windows.Forms.CheckBox
  277. $chkLogfile.Text = "保存日志到文件"
  278. $chkLogfile.Location = New-Object System.Drawing.Point(10, $y)
  279. $chkLogfile.Size = New-Object System.Drawing.Size(110, 25)
  280. $groupAll.Controls.Add($chkLogfile)

  281. $txtLogfilePath = New-Object System.Windows.Forms.TextBox
  282. $txtLogfilePath.ReadOnly = $true
  283. $txtLogfilePath.BackColor = "LightGray"
  284. $txtLogfilePath.Location = New-Object System.Drawing.Point(120, $y)
  285. $txtLogfilePath.Size = New-Object System.Drawing.Size(230, 23)
  286. $txtLogfilePath.Text = "未选择"
  287. $groupAll.Controls.Add($txtLogfilePath)
  288. $y += 35

  289. $btnStart = New-Object System.Windows.Forms.Button
  290. $btnStart.Text = "启动测试"
  291. $btnStart.Location = New-Object System.Drawing.Point(15, $y)
  292. $btnStart.Size = New-Object System.Drawing.Size(85, 35)
  293. $groupAll.Controls.Add($btnStart)

  294. $btnStop = New-Object System.Windows.Forms.Button
  295. $btnStop.Text = "停止"
  296. $btnStop.Enabled = $false
  297. $btnStop.Location = New-Object System.Drawing.Point(115, $y)
  298. $btnStop.Size = New-Object System.Drawing.Size(85, 35)
  299. $groupAll.Controls.Add($btnStop)

  300. $btnClear = New-Object System.Windows.Forms.Button
  301. $btnClear.Text = "清空输出"
  302. $btnClear.Location = New-Object System.Drawing.Point(215, $y)
  303. $btnClear.Size = New-Object System.Drawing.Size(85, 35)
  304. $groupAll.Controls.Add($btnClear)

  305. # ================= 右侧输出面板 =================
  306. $rightPanel = New-Object System.Windows.Forms.Panel
  307. $rightPanel.Dock = "Fill"
  308. $mainTable.Controls.Add($rightPanel, 1, 0)

  309. $txtOutput = New-Object System.Windows.Forms.RichTextBox
  310. $txtOutput.Dock = "Fill"
  311. $txtOutput.ReadOnly = $true
  312. $txtOutput.BackColor = "Black"
  313. $txtOutput.ForeColor = "LightGreen"
  314. $txtOutput.Font = New-Object System.Drawing.Font("Consolas", 10)
  315. $rightPanel.Controls.Add($txtOutput)

  316. # ================= 功能函数 =================

  317. function Write-OutputLog {
  318.     param([string]$text)
  319.     if ([string]::IsNullOrEmpty($text)) { return }
  320.     $txtOutput.AppendText($text + "`r`n")
  321.     $txtOutput.ScrollToCaret()
  322. }

  323. # ---------- 网络配置函数 ----------
  324. function Backup-SingleNetworkConfig {
  325.     param([int]$ifIndex, [string]$ifAlias)
  326.     if ($script:modifiedAdapters.ContainsKey($ifIndex)) { return }
  327.     $ipInterface = Get-NetIPInterface -InterfaceIndex $ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue
  328.     $dhcpEnabled = $ipInterface.Dhcp -eq "Enabled"
  329.     $ipAddresses = @()
  330.     if (-not $dhcpEnabled) {
  331.         $ipAddresses = Get-NetIPAddress -InterfaceIndex $ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue
  332.     }
  333.     $config = @{
  334.         InterfaceAlias = $ifAlias
  335.         DHCP = $dhcpEnabled
  336.         IPAddresses = @()
  337.     }
  338.     foreach ($ip in $ipAddresses) {
  339.         $config.IPAddresses += @{
  340.             IPAddress = $ip.IPAddress
  341.             PrefixLength = $ip.PrefixLength
  342.         }
  343.     }
  344.     $script:modifiedAdapters[$ifIndex] = $config
  345.     if ($dhcpEnabled) {
  346.         Write-OutputLog "已备份 $ifAlias 的原始配置 (DHCP 自动获取)"
  347.     } else {
  348.         $ipStr = if ($config.IPAddresses.Count -gt 0) { $config.IPAddresses[0].IPAddress } else { "无" }
  349.         Write-OutputLog "已备份 $ifAlias 的原始配置 (静态 IP: $ipStr)"
  350.     }
  351. }

  352. function Restore-ModifiedNetworkConfigurations {
  353.     if ($script:modifiedAdapters.Count -eq 0) { return }
  354.     Write-OutputLog "正在恢复被修改的网卡到原始配置..."
  355.     foreach ($ifIndex in $script:modifiedAdapters.Keys) {
  356.         $config = $script:modifiedAdapters[$ifIndex]
  357.         $adapter = Get-NetAdapter -InterfaceIndex $ifIndex -ErrorAction SilentlyContinue
  358.         if (-not $adapter) { continue }
  359.         try {
  360.             Get-NetIPAddress -InterfaceIndex $ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue | ForEach-Object {
  361.                 Remove-NetIPAddress -InterfaceIndex $ifIndex -IPAddress $_.IPAddress -Confirm:$false -ErrorAction SilentlyContinue
  362.             }
  363.             if ($config.DHCP) {
  364.                 Set-NetIPInterface -InterfaceIndex $ifIndex -Dhcp Enabled -ErrorAction SilentlyContinue
  365.                 ipconfig /renew $adapter.Name | Out-Null
  366.             } else {
  367.                 Set-NetIPInterface -InterfaceIndex $ifIndex -Dhcp Disabled -ErrorAction SilentlyContinue
  368.                 foreach ($ipObj in $config.IPAddresses) {
  369.                     New-NetIPAddress -InterfaceIndex $ifIndex -IPAddress $ipObj.IPAddress -PrefixLength $ipObj.PrefixLength -ErrorAction SilentlyContinue | Out-Null
  370.                 }
  371.             }
  372.         } catch {
  373.             Write-OutputLog "恢复 $($config.InterfaceAlias) 失败: $($_.Exception.Message)"
  374.         }
  375.     }
  376.     $script:modifiedAdapters.Clear()
  377.     Update-CurrentIPDisplay
  378.     Write-OutputLog "网络配置恢复完成。"
  379. }

  380. function Initialize-NetworkAdapters {
  381.     try {
  382.         $adapters = Get-NetAdapter | Where-Object {$_.Status -eq 'Up'}
  383.         $comboAdapter.Items.Clear()
  384.         foreach ($adapter in $adapters) {
  385.             [void]$comboAdapter.Items.Add($adapter.InterfaceAlias)
  386.         }
  387.         if ($comboAdapter.Items.Count -gt 0) {
  388.             [void]($comboAdapter.SelectedIndex = 0)
  389.             Update-CurrentIPDisplay
  390.         }
  391.     } catch {
  392.         Write-OutputLog "加载网卡列表失败: $($_.Exception.Message)"
  393.     }
  394. }

  395. function Update-CurrentIPDisplay {
  396.     $selectedAdapter = $comboAdapter.SelectedItem
  397.     if (-not $selectedAdapter) { return }
  398.     $adapter = Get-NetAdapter -InterfaceAlias $selectedAdapter -ErrorAction SilentlyContinue
  399.     if ($adapter) {
  400.         $ipAddress = Get-NetIPAddress -InterfaceIndex $adapter.InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue | Select-Object -First 1
  401.         $ipInterface = Get-NetIPInterface -InterfaceIndex $adapter.InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue
  402.         $dhcpEnabled = $ipInterface.Dhcp -eq "Enabled"
  403.         if ($ipAddress) {
  404.             $txtCurrentIP.Text = if ($dhcpEnabled) { "$($ipAddress.IPAddress) (DHCP)" } else { "$($ipAddress.IPAddress) (静态)" }
  405.         } else {
  406.             $txtCurrentIP.Text = if ($dhcpEnabled) { "未配置IPv4 (DHCP模式)" } else { "未配置IPv4 (静态模式)" }
  407.         }
  408.     } else {
  409.         $txtCurrentIP.Text = "未知"
  410.     }
  411. }

  412. function Get-GatewayFromIP {
  413.     param([string]$ipAddress)
  414.     if (-not $ipAddress) { return $null }
  415.     $parts = $ipAddress -split '\.'
  416.     if ($parts.Count -ne 4) { return $null }
  417.     $parts[3] = "1"
  418.     return $parts -join '.'
  419. }

  420. function Set-NetworkIP {
  421.     $selectedAdapter = $comboAdapter.SelectedItem
  422.     $newIP = $txtNewIP.Text.Trim()
  423.     $subnetMask = $txtSubnet.Text.Trim()
  424.    
  425.     if (-not $selectedAdapter) { $lblIPStatus.Text = "请选择网卡"; $lblIPStatus.ForeColor = "Red"; return }
  426.     if (-not $newIP) { $lblIPStatus.Text = "请填写新IP地址"; $lblIPStatus.ForeColor = "Red"; return }
  427.     if ($newIP -notmatch '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$') { $lblIPStatus.Text = "IP地址格式不正确"; $lblIPStatus.ForeColor = "Red"; return }
  428.     if ($subnetMask -notmatch '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$') { $lblIPStatus.Text = "子网掩码格式不正确"; $lblIPStatus.ForeColor = "Red"; return }
  429.    
  430.     $adapter = Get-NetAdapter -InterfaceAlias $selectedAdapter -ErrorAction SilentlyContinue
  431.     if (-not $adapter) { $lblIPStatus.Text = "网卡不存在"; $lblIPStatus.ForeColor = "Red"; return }
  432.     $ifIndex = $adapter.InterfaceIndex
  433.     $ifAlias = $adapter.InterfaceAlias
  434.     try {
  435.         Backup-SingleNetworkConfig -ifIndex $ifIndex -ifAlias $ifAlias
  436.         $lblIPStatus.Text = "正在修改IP,请稍候..."
  437.         $lblIPStatus.ForeColor = "Blue"
  438.         Set-NetIPInterface -InterfaceIndex $ifIndex -Dhcp Disabled -ErrorAction SilentlyContinue
  439.         Get-NetIPAddress -InterfaceIndex $ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue | ForEach-Object {
  440.             Remove-NetIPAddress -InterfaceIndex $ifIndex -IPAddress $_.IPAddress -Confirm:$false -ErrorAction SilentlyContinue
  441.         }
  442.         $prefixLength = ConvertTo-PrefixLength $subnetMask
  443.         New-NetIPAddress -InterfaceIndex $ifIndex -IPAddress $newIP -PrefixLength $prefixLength -ErrorAction Stop | Out-Null
  444.         $lblIPStatus.Text = "成功:IP已改为 $newIP,退出时将恢复原始配置"
  445.         $lblIPStatus.ForeColor = "Green"
  446.         Write-OutputLog "已修改网卡 $selectedAdapter 的IP为 $newIP,掩码 $subnetMask"
  447.         Update-CurrentIPDisplay
  448.         if ($radioClient.Checked) {
  449.             $gatewayIP = Get-GatewayFromIP $newIP
  450.             if ($gatewayIP) {
  451.                 $txtServer.Text = $gatewayIP
  452.                 Write-OutputLog "客户端模式:已自动将服务器地址设为 $gatewayIP"
  453.             }
  454.         }
  455.     } catch {
  456.         $lblIPStatus.Text = "修改失败:$($_.Exception.Message)"
  457.         $lblIPStatus.ForeColor = "Red"
  458.     }
  459. }

  460. function ConvertTo-PrefixLength {
  461.     param([string]$mask)
  462.     if (-not $mask) { return 24 }
  463.     $bits = $mask.Split('.') | ForEach-Object { [convert]::ToString($_, 2).PadLeft(8,'0') }
  464.     $binary = ($bits -join '')
  465.     return ($binary -replace '0', '').Length
  466. }

  467. # ---------- iperf3 参数处理 ----------
  468. function Update-UIState {
  469.     $isClient = $radioClient.Checked
  470.     $txtServer.Enabled = $isClient
  471.     $numHour.Enabled = $isClient
  472.     $numMinute.Enabled = $isClient
  473.     $numSecond.Enabled = $isClient
  474.     $txtParallel.Enabled = $isClient
  475.     $groupTestMode.Enabled = $isClient
  476.     $chkLogfile.Enabled = $isClient
  477.     $txtLogfilePath.Enabled = $isClient
  478.     $txtUdpBw.Enabled = $isClient -and ($comboProto.SelectedItem -eq "udp") -and ($comboProto.SelectedItem -ne "sctp")
  479. }

  480. function Get-TotalSeconds {
  481.     $hours = [int]$numHour.Value
  482.     $minutes = [int]$numMinute.Value
  483.     $seconds = [int]$numSecond.Value
  484.     $total = $hours * 3600 + $minutes * 60 + $seconds
  485.     if ($total -eq 0) { $total = 1 }
  486.     return $total
  487. }

  488. function Get-CommandLineArguments {
  489.     $isClient = $radioClient.Checked
  490.     $argsList = @()
  491.     if ($isClient) {
  492.         $serverAddr = $txtServer.Text.Trim()
  493.         if (-not $serverAddr) { Write-OutputLog "错误:客户端模式需要指定服务器地址。"; return @() }
  494.         $argsList += "-c"
  495.         $argsList += $serverAddr
  496.         $duration = Get-TotalSeconds
  497.         $argsList += "-t"; $argsList += $duration
  498.         if ($txtParallel.Text -and $txtParallel.Text -ne "1") { $argsList += "-P"; $argsList += $txtParallel.Text }
  499.         if ($radioReverse.Checked) { $argsList += "-R" }
  500.         elseif ($radioBidir.Checked) { $argsList += "--bidir" }
  501.         elseif ($radioDualtest.Checked) { $argsList += "-d" }
  502.         $argsList += "--forceflush"
  503.     } else {
  504.         $argsList += "-s"
  505.     }
  506.     if ($txtPort.Text) { $argsList += "-p"; $argsList += $txtPort.Text }
  507.     $protocol = $comboProto.SelectedItem
  508.     if ($protocol -eq "udp") { $argsList += "-u" }
  509.     elseif ($protocol -eq "sctp") { $argsList += "--sctp" }
  510.     if ($isClient -and $protocol -eq "udp" -and $txtUdpBw.Text -match '^\d+$') {
  511.         $argsList += "-b"; $argsList += "$($txtUdpBw.Text)M"
  512.     }
  513.     if ($isClient -and $chkLogfile.Checked -and $script:logfilePath -ne "") {
  514.         $argsList += "--logfile"; $argsList += $script:logfilePath
  515.     }
  516.     return $argsList
  517. }

  518. # ================= 文件重定向 + 定时器方案 =================
  519. function Start-IperfTest {
  520.     if (-not $script:iperfPath) {
  521.         Write-OutputLog "错误:找不到 iperf3.exe,无法启动测试。"
  522.         return
  523.     }
  524.     $argsList = Get-CommandLineArguments
  525.     if ($argsList.Count -eq 0) { return }

  526.     Write-OutputLog ">>> 执行命令: $script:iperfPath $argsList"

  527.     $script:tempOutputFile = [System.IO.Path]::GetTempFileName()
  528.     $script:lastReadPosition = 0

  529.     $cmdArgs = "/c `"`"$script:iperfPath`" $($argsList -join ' ') > `"$script:tempOutputFile`" 2>&1`""
  530.     $psi = New-Object System.Diagnostics.ProcessStartInfo
  531.     $psi.FileName = "cmd.exe"
  532.     $psi.Arguments = $cmdArgs
  533.     $psi.UseShellExecute = $false
  534.     $psi.CreateNoWindow = $true

  535.     $script:iperfProcess = New-Object System.Diagnostics.Process
  536.     $script:iperfProcess.StartInfo = $psi

  537.     try {
  538.         [void]$script:iperfProcess.Start()
  539.         Write-OutputLog "iperf3 进程已启动,正在接收数据..."
  540.     } catch {
  541.         Write-OutputLog "启动 iperf3 失败: $($_.Exception.Message)"
  542.         return
  543.     }

  544.     $script:running = $true
  545.     $btnStart.Enabled = $false
  546.     $btnStop.Enabled  = $true

  547.     $script:updateTimer = New-Object System.Windows.Forms.Timer
  548.     $script:updateTimer.Interval = 100
  549.     $script:updateTimer.Add_Tick({
  550.         if (-not $script:tempOutputFile -or -not (Test-Path $script:tempOutputFile)) { return }
  551.         try {
  552.             $fs = [System.IO.File]::Open($script:tempOutputFile, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
  553.             $fs.Seek($script:lastReadPosition, [System.IO.SeekOrigin]::Begin) | Out-Null
  554.             $reader = New-Object System.IO.StreamReader($fs, [System.Text.Encoding]::UTF8)
  555.             while ($true) {
  556.                 $line = $reader.ReadLine()
  557.                 if ($null -eq $line) { break }
  558.                 Write-OutputLog $line
  559.             }
  560.             $script:lastReadPosition = $fs.Position
  561.             $reader.Close()
  562.             $fs.Close()
  563.         } catch {}
  564.     })
  565.     $script:updateTimer.Start()

  566.     $script:monitorTimer = New-Object System.Windows.Forms.Timer
  567.     $script:monitorTimer.Interval = 500
  568.     $script:monitorTimer.Add_Tick({
  569.         if ($script:iperfProcess -and $script:iperfProcess.HasExited) {
  570.             if ($script:updateTimer) { $script:updateTimer.Stop(); $script:updateTimer.Dispose(); $script:updateTimer = $null }
  571.             if ($script:monitorTimer) { $script:monitorTimer.Stop(); $script:monitorTimer.Dispose(); $script:monitorTimer = $null }
  572.             if ($script:tempOutputFile -and (Test-Path $script:tempOutputFile)) {
  573.                 try {
  574.                     $fs = [System.IO.File]::Open($script:tempOutputFile, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
  575.                     $fs.Seek($script:lastReadPosition, [System.IO.SeekOrigin]::Begin) | Out-Null
  576.                     $reader = New-Object System.IO.StreamReader($fs, [System.Text.Encoding]::UTF8)
  577.                     while ($true) {
  578.                         $line = $reader.ReadLine()
  579.                         if ($null -eq $line) { break }
  580.                         Write-OutputLog $line
  581.                     }
  582.                     $reader.Close()
  583.                     $fs.Close()
  584.                 } catch {}
  585.             }
  586.             $exitCode = $script:iperfProcess.ExitCode
  587.             Write-OutputLog "--- 测试结束,退出代码 $exitCode ---"
  588.             Reset-TestState
  589.         }
  590.     })
  591.     $script:monitorTimer.Start()
  592. }

  593. function Stop-IperfTest {
  594.     if ($script:iperfProcess -and -not $script:iperfProcess.HasExited) {
  595.         Write-OutputLog "正在终止 iperf 进程..."
  596.         try {
  597.             $script:iperfProcess.Kill()
  598.             $script:iperfProcess.WaitForExit(2000)
  599.         } catch {}
  600.     }
  601.     Reset-TestState
  602.     Write-OutputLog "已终止。"
  603. }

  604. function Reset-TestState {
  605.     $script:running = $false
  606.     $btnStart.Enabled = $true
  607.     $btnStop.Enabled  = $false

  608.     if ($script:updateTimer) { $script:updateTimer.Stop(); $script:updateTimer.Dispose(); $script:updateTimer = $null }
  609.     if ($script:monitorTimer) { $script:monitorTimer.Stop(); $script:monitorTimer.Dispose(); $script:monitorTimer = $null }
  610.     if ($script:iperfProcess) { $script:iperfProcess.Dispose(); $script:iperfProcess = $null }
  611.     if ($script:tempOutputFile -and (Test-Path $script:tempOutputFile)) {
  612.         Remove-Item $script:tempOutputFile -Force -ErrorAction SilentlyContinue
  613.     }
  614.     $script:tempOutputFile = ""
  615.     $script:lastReadPosition = 0
  616. }

  617. function Clear-OutputLog {
  618.     $txtOutput.Clear()
  619. }

  620. # ---------- 日志文件选择 ----------
  621. $script:logfileSelectionInProgress = $false
  622. function Invoke-LogfileSelection {
  623.     if ($script:logfileSelectionInProgress) { return }
  624.     $script:logfileSelectionInProgress = $true
  625.     try {
  626.         if ($chkLogfile.Checked) {
  627.             $saveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
  628.             $saveFileDialog.Title = "选择日志文件保存位置"
  629.             $saveFileDialog.Filter = "文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*"
  630.             $saveFileDialog.DefaultExt = "txt"
  631.             if ($script:logfilePath) {
  632.                 $saveFileDialog.FileName = [System.IO.Path]::GetFileName($script:logfilePath)
  633.                 $saveFileDialog.InitialDirectory = [System.IO.Path]::GetDirectoryName($script:logfilePath)
  634.             } else {
  635.                 $saveFileDialog.InitialDirectory = $scriptDirectory
  636.                 $saveFileDialog.FileName = "iperf3_log.txt"
  637.             }
  638.             if ($saveFileDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
  639.                 $script:logfilePath = $saveFileDialog.FileName
  640.                 $txtLogfilePath.Text = $script:logfilePath
  641.                 Write-OutputLog "日志将保存到: $script:logfilePath"
  642.             } else {
  643.                 [void]($chkLogfile.Checked = $false)
  644.                 $txtLogfilePath.Text = "未选择"
  645.             }
  646.         } else {
  647.             $script:logfilePath = ""
  648.             $txtLogfilePath.Text = "未选择"
  649.         }
  650.     } finally {
  651.         $script:logfileSelectionInProgress = $false
  652.     }
  653. }

  654. # ================= 事件绑定 =================
  655. $btnStart.Add_Click({ Start-IperfTest })
  656. $btnStop.Add_Click({ Stop-IperfTest })
  657. $btnClear.Add_Click({ Clear-OutputLog })
  658. $radioClient.Add_CheckedChanged({ Update-UIState })
  659. $radioServer.Add_CheckedChanged({ Update-UIState })
  660. $comboProto.Add_SelectedIndexChanged({ Update-UIState })
  661. $comboAdapter.Add_SelectedIndexChanged({ Update-CurrentIPDisplay })
  662. $btnApplyIP.Add_Click({ Set-NetworkIP })
  663. $chkLogfile.Add_CheckedChanged({ Invoke-LogfileSelection })

  664. # ================= 权限检查 =================
  665. $script:isAdministrator = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
  666. if (-not $script:isAdministrator) {
  667.     Write-OutputLog "警告:未以管理员身份运行,IP 设置功能被禁用。"
  668.     $lblIPStatus.Text = "需要管理员权限"
  669.     $lblIPStatus.ForeColor = "Red"
  670.     $btnApplyIP.Enabled = $false
  671.     $txtNewIP.Enabled = $false
  672.     $txtSubnet.Enabled = $false
  673. } else {
  674.     $lblIPStatus.Text = "就绪"
  675. }

  676. # ================= 初始化 =================
  677. Initialize-NetworkAdapters
  678. Update-UIState
  679. if ($script:iperfPath) {
  680.     Write-OutputLog "欢迎使用 iperf3 图形界面工具。"
  681.     Write-OutputLog "iperf3.exe 路径: $script:iperfPath"
  682. } else {
  683.     Write-OutputLog "警告:未找到 iperf3.exe!请将 iperf3.exe 放置在脚本目录后重启程序。"
  684.     $btnStart.Enabled = $false
  685. }

  686. try {
  687.     $form.ShowDialog() | Out-Null
  688. } catch {
  689.     [System.Windows.Forms.MessageBox]::Show("运行时异常: $($_.Exception.Message)", "错误", "OK", "Error")
  690. } finally {
  691.     if ($script:updateTimer) { $script:updateTimer.Stop(); $script:updateTimer.Dispose() }
  692.     if ($script:monitorTimer) { $script:monitorTimer.Stop(); $script:monitorTimer.Dispose() }
  693.     if ($script:iperfProcess) {
  694.         try { $script:iperfProcess.Kill() } catch {}
  695.         $script:iperfProcess.Dispose()
  696.     }
  697.     if ($script:tempOutputFile -and (Test-Path $script:tempOutputFile)) {
  698.         Remove-Item $script:tempOutputFile -Force -ErrorAction SilentlyContinue
  699.     }
  700.     Restore-ModifiedNetworkConfigurations
  701. }</font>
复制代码
使用方法,保存代码为ps1,右键使用powershell运行,此时无法正常以管理员权限运行,无法更改ip地址,如果需要管理员运行,可以下载附件文件放入iperf3的相同目录下运行
iperf3开源地址:https://github.com/esnet/iperf


游客,如果您要查看本帖隐藏内容请回复








发表于 1 小时前 | 显示全部楼层
感谢分享!
回复

使用道具 举报

发表于 昨天 23:42 | 显示全部楼层

感谢分享好东东~
回复

使用道具 举报

发表于 昨天 22:00 | 显示全部楼层
下载看看
回复

使用道具 举报

发表于 昨天 21:35 | 显示全部楼层
感谢分享!
回复

使用道具 举报

发表于 昨天 21:28 | 显示全部楼层
好!!!!!!!
回复

使用道具 举报

发表于 昨天 21:11 | 显示全部楼层
本帖最后由 haiou327 于 2026-5-21 21:45 编辑


iperf3 GUI.exe (3.08 MB, 下载次数: 21)
iperf3GUI.png

安卓版iperf3
iperf3-android.jpg
iperf_android.rar (541.72 KB, 下载次数: 6)

点评

感谢分享好东东~  详情 回复 发表于 昨天 23:42
回复

使用道具 举报

发表于 昨天 20:59 来自手机 | 显示全部楼层
回复

使用道具 举报

发表于 昨天 20:59 来自手机 | 显示全部楼层
回复

使用道具 举报

发表于 昨天 20:12 | 显示全部楼层
回复见见
回复

使用道具 举报

 楼主| 发表于 昨天 20:10 | 显示全部楼层
目前测试功能是正常的,但是客户端模式下,同时双向测试右侧信息显示有问题,会一直重复刷新
回复

使用道具 举报

发表于 昨天 19:57 | 显示全部楼层
iperf3 简单GUI,感谢分享!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|捐助支持|无忧启动 ( 闽ICP备05002490号-1|闽公网安备35020302032614号 )

GMT+8, 2026-5-22 01:53

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表