PowerShell控制台P2P聊天程序异步输入问题求助
基于Windows Form的PowerShell P2P消息客户端解决方案
核心思路
用Windows Form的异步机制处理消息接收,避免阻塞UI;通过文本框实现输入、按钮触发发送,同时复用原有网络通信逻辑,解决控制台输入暂停程序的问题。
完整实现代码
Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing # 全局变量存储客户端连接 $global:clientConnection = $null # 初始化主窗体 $mainForm = New-Object System.Windows.Forms.Form $mainForm.Text = "P2P消息客户端" $mainForm.Size = New-Object System.Drawing.Size(500, 400) $mainForm.StartPosition = "CenterScreen" $mainForm.FormBorderStyle = "FixedSingle" $mainForm.MaximizeBox = $false # 消息显示区域 $messageBox = New-Object System.Windows.Forms.RichTextBox $messageBox.Location = New-Object System.Drawing.Point(10, 10) $messageBox.Size = New-Object System.Drawing.Size(460, 280) $messageBox.ReadOnly = $true $messageBox.Font = New-Object System.Drawing.Font("Consolas", 10) $mainForm.Controls.Add($messageBox) # 输入文本框 $inputBox = New-Object System.Windows.Forms.TextBox $inputBox.Location = New-Object System.Drawing.Point(10, 300) $inputBox.Size = New-Object System.Drawing.Size(360, 25) $inputBox.MaxLength = 1020 $inputBox.Font = New-Object System.Drawing.Font("Consolas", 10) $mainForm.Controls.Add($inputBox) # 发送按钮 $sendBtn = New-Object System.Windows.Forms.Button $sendBtn.Location = New-Object System.Drawing.Point(380, 300) $sendBtn.Size = New-Object System.Drawing.Size(90, 25) $sendBtn.Text = "发送" $sendBtn.Add_Click({ $textToSend = $inputBox.Text.Trim() if (-not [string]::IsNullOrEmpty($textToSend)) { # 复用原发送逻辑 [System.Byte[]]$bytes = [System.Text.Encoding]::ASCII.GetBytes($textToSend) $null = $global:clientConnection.Client.Send($bytes) # 本地显示自己发送的消息 $messageBox.AppendText("[$env:USERNAME@$env:COMPUTERNAME] $textToSend`n") $inputBox.Clear() } }) $mainForm.Controls.Add($sendBtn) # 绑定回车发送 $inputBox.Add_KeyDown({ if ($_.KeyCode -eq [System.Windows.Forms.Keys]::Enter) { $sendBtn.PerformClick() } }) # 异步接收消息的函数 Function Start-AsyncMessageReceiver { param([Parameter(Mandatory=$true)]$client) $stream = $client.GetStream() $buffer = New-Object System.Byte[] 1024 # 异步读取流,避免阻塞UI $stream.BeginRead($buffer, 0, $buffer.Length, { param($asyncResult) try { $i = $stream.EndRead($asyncResult) if ($i -ne 0) { $encodedText = New-Object System.Text.ASCIIEncoding $data = $encodedText.GetString($buffer, 0, $i) # 跨线程安全更新UI $mainForm.Invoke({ $messageBox.AppendText("[对方] $data`n") }) # 持续监听下一条消息 Start-AsyncMessageReceiver -client $client } else { $mainForm.Invoke({ $messageBox.AppendText("连接已断开`n") $sendBtn.Enabled = $false }) } } catch { $mainForm.Invoke({ $messageBox.AppendText("接收消息出错: $_`n") }) } }, $null) } # 替换为你的实际连接逻辑 # $global:clientConnection = New-Object System.Net.Sockets.TcpClient("目标IP", 端口) # 启动异步接收 # Start-AsyncMessageReceiver -client $global:clientConnection # 启动窗体 $mainForm.ShowDialog()
关键改进点
- 非阻塞消息接收:用
BeginRead/EndRead实现异步监听,不会卡住UI线程 - UI线程安全:更新消息显示时使用
Invoke方法,规避跨线程操作UI的异常 - 输入体验优化:支持回车触发发送,输入框内置长度限制,发送后自动清空
- 状态反馈:连接断开或出错时自动提示,并禁用发送按钮
集成说明
- 将原有网络连接逻辑替换示例中的注释部分,确保
$global:clientConnection正确赋值 - 输入长度限制已通过文本框
MaxLength属性实现,无需额外处理 - 若需服务端监听逻辑,可采用
BeginAcceptTcpClient实现非阻塞连接
内容的提问来源于stack exchange,提问作者Dominiq
相关产品推荐
相关产品推荐

