c tel
有!
在 .NET 4.0(VS2013)里可以用 Socket.Select(BSD 风格)或者 Socket.Poll 实现“不卡 UI 的非阻塞收发”,完全不需要线程/await。
下面把上面的 阻塞版 改成 Select 轮询版:连接、发送、接收都不会假死,代码量仍然很小。
在 .NET 4.0(VS2013)里可以用 Socket.Select(BSD 风格)或者 Socket.Poll 实现“不卡 UI 的非阻塞收发”,完全不需要线程/await。
下面把上面的 阻塞版 改成 Select 轮询版:连接、发送、接收都不会假死,代码量仍然很小。
核心思路
-
用
Socket代替TcpClient; -
发送直接
Send(非阻塞); -
用
Socket.Select(readList, null, null, microSeconds)轮询可读事件; -
轮询间隔 20 ms,在
System.Windows.Forms.Timer里跑,不卡消息循环。
代码(VS2013 .NET 4.0 WinForms)
csharp
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
namespace TelnetSelect2013
{
public partial class Form1 : Form
{
private Socket _socket;
private Timer _selectTimer; // WinForms 计时器,精度 15-20 ms
private List<byte> _recvBuf = new List<byte>();
public Form1()
{
InitializeComponent();
btnDisconnect.Enabled = false;
// 初始化轮询计时器
_selectTimer = new Timer { Interval = 20 };
_selectTimer.Tick += OnSelectTick;
}
/* ---------- 连接(非阻塞) ---------- */
private void btnConnect_Click(object sender, EventArgs e)
{
try
{
Log("正在连接...");
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Blocking = false; // 关键:非阻塞
_socket.BeginConnect(IPAddress.Parse(txtIP.Text), int.Parse(txtPort.Text), ar =>
{
try { _socket.EndConnect(ar); }
catch { Invoke(new Action(() => Log("连接失败"))); return; }
Invoke(new Action(() =>
{
Log("已连接");
btnConnect.Enabled = false;
btnDisconnect.Enabled = true;
_selectTimer.Start(); // 开始轮询
}));
}, null);
}
catch (Exception ex) { Log("连接异常: " + ex.Message); }
}
/* ---------- 断开 ---------- */
private void btnDisconnect_Click(object sender, EventArgs e)
{
_selectTimer.Stop();
if (_socket != null) { _socket.Close