c tel

有!
在 .NET 4.0(VS2013)里可以用 Socket.Select(BSD 风格)或者 Socket.Poll 实现“不卡 UI 的非阻塞收发”,完全不需要线程/await。
下面把上面的 阻塞版 改成 Select 轮询版:连接、发送、接收都不会假死,代码量仍然很小。

核心思路

  1. Socket 代替 TcpClient
  2. 发送直接 Send(非阻塞);
  3. Socket.Select(readList, null, null, microSeconds) 轮询可读事件;
  4. 轮询间隔 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(); _socket = null; }
            btnConnect.Enabled = true;
            btnDisconnect.Enabled = false;
            Log("已断开");
        }

        /* ---------- 发送(非阻塞) ---------- */
        private void btnSend_Click(object sender, EventArgs e)
        {
            if (_socket == null) return;
            string cmd = txtCmd.Text.Trim() + "\r\n";
            byte[] data = Encoding.ASCII.GetBytes(cmd);
            _socket.Send(data);          // 非阻塞发送
            Log(">> " + cmd.TrimEnd());
            txtCmd.Clear();
        }

        /* ---------- Select 轮询可读 ---------- */
        private void OnSelectTick(object sender, EventArgs e)
        {
            if (_socket == null) return;

            var readList = new List<Socket> { _socket };
            Socket.Select(readList, null, null, 0); // 0 微秒 = 立即返回
            if (readList.Count == 0) return;        // 无可读

            int avail = _socket.Available;
            if (avail > 0)
            {
                byte[] buf = new byte[avail];
                int read = _socket.Receive(buf);   // 必然立即成功
                string txt = Encoding.ASCII.GetString(buf, 0, read);
                Log(txt);
            }
        }

        /* ---------- 日志 ---------- */
        private void Log(string text)
        {
            if (InvokeRequired) { Invoke(new Action<string>(Log), text); return; }
            txtLog.AppendText(text + Environment.NewLine);
            txtLog.ScrollToCaret();
        }
    }
}
 

运行效果

  • 连接 → 后台 BeginConnect 立即返回,UI 不卡;
  • 发送Send 非阻塞,瞬间完成;
  • 接收Timer 每 20 ms 调用一次 Select,有数据才读,界面全程流畅

发布

项目 → 发布 → 文件夹 → 完成 → 发布
 
拷贝 bin\Release\Publish 下文件即可,自带 .NET 4 运行库,Win7-Win11 直接运行。

🔚 一句话总结

  • .NET 4.0 也有“Select”Socket.Select / Socket.Poll 都能实现 非阻塞轮询
  • WinForms Timer 每 20 ms 调一次,UI 再也不假死
  • 代码量与阻塞版几乎相同,VS2013 直接编译通过
posted @ 2025-09-28 14:05  cnchengv  阅读(8)  评论(0)    收藏  举报