c# Socket(客户端)异步通讯

本文详细介绍了一种使用Socket进行高效通讯的方法,包括如何建立TCP连接、发送与接收数据、监测网络状态及异常处理。通过创建自定义TCPClient类,实现了客户端与服务端的稳定通讯,同时提供了连接状态监测和事件通知机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        实现功能,能够随时发送数据,并且能够随时接收服务端发送过来的数据,能够判断服务端或端口网络的连接状态,随时查看与服务器通讯是否通畅。

        可以使用VS创建一个用户控件或创建一个类,来控制Socket通讯,代码成熟,建议做成用户控件,这样可以随时拖拽控件,写事件什么的都方便。

        在写Socket客户端之前,要知道Socket客户端接收数据,使用 的线程循环的方式,有数据接收到的时候,触发消息事件,在断开事件处理时,能够触发断开连接事件,这时要定义几个委托,承载事件的触发。

        public delegate void ReceiveBuffer(byte[] buffer);//收到消息时,传递消息Buffer的事件。

        public delegate void HostDisconnectionEvent();//断开连接时,触发的事件。

        我在使用的时候,习惯将委托单独放在一个.cs文件当,定义为委托事件类:DelegateEvent.cs

        还要考虑到,在Socket接收消息时,需要使用线程,那么在触发收到消息的事件后,如果需要修改界面上的参数或值,需要做控件的异步调用处理,这样处理起来太麻烦,而且处理不好,到时会有一堆的Bug,这是我在使用过程当中遇到的。那么有一没有办法不使用线程,让客户端循环接收消息呢?这时我想到了使用异步,最后又增加了一个异步收消息的委托:

        public delegate void AsyncReceivedClient(object sender);//异步收到消息时的委托,如果整个类当中只有一个客户端连接,可以不带参数。

        这时,委托类基本建完了,那么,这时需要定义代码了:

    创建一个TCPClient类,当然,这个类和System.Net.Sockets下的类不同,为了区分,我定义的命名空间为:System.Net.Securate.TCP,下面是定义的事件和参数:

        /// <summary>
        /// 收到消息事件
        /// </summary>
        public ReceiveBuffer ReceiveComtemplate;
        /// <summary>
        /// 主机断开连接事件
        /// </summary>
        public HostDisconnectionEvent HostDisconnectedComtelplate;
        private string _Address;
        /// <summary>
        /// 获取或设置连接地址
        /// </summary>
        public string Address
        {
            get { return _Address; }
            set { _Address = value; }
        }
        private int _Port;
        /// <summary>
        /// 获取或设置端口号
        /// </summary>
        public int Port
        {
            get { return _Port; }
            set { _Port = value; }
        }
        private Socket tcpClient;
        /// <summary>
        /// 获取连接状态
        /// </summary>
        public bool IsConnected
        {
            get {
                if (tcpClient == null)
                    return false;
                return tcpClient.Connected;
            }
        }

        创建连接的启动事件,我写两种启动方式,一种是不带参的,一种是带参的,如下:

        /// <summary>
        /// 打开连接
        /// </summary>
        /// <returns></returns>
        public bool Start()
        {
            return Start(_Address, _Port);
        }
        /// <summary>
        /// 打开连接
        /// </summary>
        /// <param name="ip">连接地址</param>
        /// <param name="port">连接端口号</param>
        /// <returns></returns>
        public bool Start(string ip, int port)
        {
            try
            {
                _Address = ip;
                _Port = port;
                tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPAddress ipaddress = IPAddress.Parse(_Address);
                tcpClient.Connect(ipaddress, port);
                if (tcpClient.Connected)
                {
                    AsyncReceivedClient rc = new AsyncReceivedClient(Receive);
                    rc.BeginInvoke(tcpClient, null, null);
                }
                return tcpClient.Connected;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        需要注意的是:

        AsyncReceivedClient rc = new AsyncReceivedClient(Receive);
        rc.BeginInvoke(tcpClient, null, null);

        这两行,实现的是异步委托带参的调用,实现随时接收服务端发送过来的消息。

      在客户端收消息时,需要判断服务端连接是否断开,在网上的介绍上,说使用socket.Poll(1, SelectMode.SelectRead)和socket.Available= 0来判断网络是否断开,但在实际上的使用当中,我使用第三方的服务端,使用这种方式,根本不能判断断开,后来我想,能不能开一个SocketCient,来判断服务端口是否正常,相当于telnet命令,在实际使用当中,还没遇到问题:

        /// <summary>
        /// 接收消息
        /// </summary>
        /// <returns></returns>
        public void Receive(object o)
        {
            Socket s = o as Socket;
            bool IsConnected = true;
            while (s.Connected)
            {
                try
                {
                    int res = s.Available;
                    if (res > 0)
                    {
                        byte[] xxxx = new byte[res];
                        int nRead = s.Receive(xxxx);
                        byte[] buffer = new byte[nRead];
                        Array.Copy(xxxx, 0, buffer, 0, nRead);
                        if (ReceiveComtemplate != null)
                            ReceiveComtemplate(buffer);
                    }
                    else
                    {
                        try
                        {
                            Socket testSocket =
                                new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                            testSocket.Connect(s.RemoteEndPoint);
                            if (!testSocket.Connected)
                            {
                                Stop();
                                IsConnected = false;
                                goto EndReceive;
                            }
                            else
                            {
                                testSocket.Shutdown(SocketShutdown.Both);
                                testSocket.Close();
                            }
                        }
                        catch (Exception ex)
                        {
                            Stop();
                            IsConnected = false;
                            goto EndReceive;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Stop();
                    IsConnected = false;
                    goto EndReceive;
                }
                Thread.Sleep(100);
            }
            EndReceive:
            if (!IsConnected)
            {
                if (HostDisconnectedComtelplate != null)
                    HostDisconnectedComtelplate();
            }
        }

        最后就是一个发送,一个停止,这个部分,基本和msdn上介绍的一样,没有什么特殊的处理:

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="buffer">发送的数据</param>
        public void Send(byte[] buffer)
        {
            try
            {
                if (tcpClient.Connected)
                {
                    tcpClient.Send(buffer);
                }
            }
            catch { }
        }
        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            try
            {
                //tcpClient.Client.Shutdown(SocketShutdown.Both);
                //tcpClient.Close();
                tcpClient.Shutdown(SocketShutdown.Both);
                tcpClient.Close();
            }
            catch (Exception ex)
            { }
        }

        到这里基本上已经完成,完整代码如下,如果有不同看法或有更好的意见,欢迎在线留言:

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;

namespace System.Net.Securate.TCP
{
    public sealed class TCPClient
    {
        /// <summary>
        /// 收到消息事件
        /// </summary>
        public ReceiveBuffer ReceiveComtemplate;
        /// <summary>
        /// 主机断开连接事件
        /// </summary>
        public HostDisconnectionEvent HostDisconnectedComtelplate;
        private string _Address;
        /// <summary>
        /// 获取或设置连接地址
        /// </summary>
        public string Address
        {
            get { return _Address; }
            set { _Address = value; }
        }
        private int _Port;
        /// <summary>
        /// 获取或设置端口号
        /// </summary>
        public int Port
        {
            get { return _Port; }
            set { _Port = value; }
        }
        private Socket tcpClient;
        /// <summary>
        /// 获取连接状态
        /// </summary>
        public bool IsConnected
        {
            get {
                if (tcpClient == null)
                    return false;
                return tcpClient.Connected;
            }
        }
        /// <summary>
        /// 打开连接
        /// </summary>
        /// <returns></returns>
        public bool Start()
        {
            return Start(_Address, _Port);
        }
        /// <summary>
        /// 打开连接
        /// </summary>
        /// <param name="ip">连接地址</param>
        /// <param name="port">连接端口号</param>
        /// <returns></returns>
        public bool Start(string ip, int port)
        {
            try
            {
                _Address = ip;
                _Port = port;
                tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPAddress ipaddress = IPAddress.Parse(_Address);
                tcpClient.Connect(ipaddress, port);
                if (tcpClient.Connected)
                {
                    AsyncReceivedClient rc = new AsyncReceivedClient(Receive);
                    rc.BeginInvoke(tcpClient, null, null);
                }
                return tcpClient.Connected;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
        /// <summary>
        /// 接收消息
        /// </summary>
        /// <returns></returns>
        public void Receive(object o)
        {
            Socket s = o as Socket;
            bool IsConnected = true;
            while (s.Connected)
            {
                try
                {
                    int res = s.Available;
                    if (res > 0)
                    {
                        byte[] xxxx = new byte[res];
                        int nRead = s.Receive(xxxx);
                        byte[] buffer = new byte[nRead];
                        Array.Copy(xxxx, 0, buffer, 0, nRead);
                        if (ReceiveComtemplate != null)
                            ReceiveComtemplate(buffer);
                    }
                    else
                    {
                        try
                        {
                            Socket testSocket =
                                new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                            testSocket.Connect(s.RemoteEndPoint);
                            if (!testSocket.Connected)
                            {
                                Stop();
                                IsConnected = false;
                                goto EndReceive;
                            }
                            else
                            {
                                testSocket.Shutdown(SocketShutdown.Both);
                                testSocket.Close();
                            }
                        }
                        catch (Exception ex)
                        {
                            Stop();
                            IsConnected = false;
                            goto EndReceive;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Stop();
                    IsConnected = false;
                    goto EndReceive;
                }
                Thread.Sleep(100);
            }
            EndReceive:
            if (!IsConnected)
            {
                if (HostDisconnectedComtelplate != null)
                    HostDisconnectedComtelplate();
            }
        }
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="buffer">发送的数据</param>
        public void Send(byte[] buffer)
        {
            try
            {
                if (tcpClient.Connected)
                {
                    tcpClient.Send(buffer);
                }
            }
            catch { }
        }
        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            try
            {
                //tcpClient.Client.Shutdown(SocketShutdown.Both);
                //tcpClient.Close();
                tcpClient.Shutdown(SocketShutdown.Both);
                tcpClient.Close();
            }
            catch (Exception ex)
            { }
        }
    }
}
 

<think>好的,用户想了解C#中的Socket客户端异步操作。首先,我需要回忆一下C#中异步编程的相关知识,特别是涉及到Socket的部分。记得在.NET中,异步操作有几种实现方式,比如早期的APM模式(Begin/End方法),基于事件的EAP模式,还有现在主流的TAP模式(async/await)。用户可能更关注现代的async/await方法,所以应该重点介绍这部分。 接下来,需要确定Socket客户端的基本流程。建立连接、发送数据、接收数据、关闭连接,这些都是基本步骤。异步操作的关键在于不阻塞主线程,提高程序的响应性。可能需要提到Socket类中的ConnectAsync、SendAsync、ReceiveAsync等方法,这些都属于TAP模式。 然后,得考虑用户可能的实际应用场景。比如,客户端需要保持长时间连接,或者处理大量并发连接,这时候异步操作的优势就体现出来了。需要举一个简单的例子,说明如何使用async和await来实现异步连接和数据传输。同时,要提醒异常处理的重要性,比如SocketException和ObjectDisposedException等。 另外,线程安全也是一个需要注意的点。在异步操作中,回调可能在不同的线程上执行,需要确保共享资源的正确访问。可能还需要提到取消操作,比如使用CancellationToken来优雅地终止连接。 还要检查是否需要对比同步和异步的区别,帮助用户理解为什么选择异步。比如,同步方法会阻塞线程,而异步方法在等待IO完成时释放线程,提高资源利用率。 最后,确保示例代码符合规范,使用正确的LaTeX格式标注变量和方法名,比如$Socket.ConnectAsync$,并且独立公式用$$...$$。代码部分要结构清晰,注释明确,方便用户理解每一步的作用。可能还需要提到使用NetworkStream进行读写的替代方案,但重点放在Socket类的异步方法上。 总结一下,回答的结构应该包括:异步编程的优势、关键方法、示例代码、注意事项以及总结。确保内容准确,覆盖用户可能的需求,同时避免深入复杂的细节,保持简明易懂。</think>在C#中实现异步Socket客户端需要理解以下核心概念和步骤: $$异步编程模型(TAP)$$ C#推荐使用基于任务的异步模式(Task-based Asynchronous Pattern),通过`async/await`关键字实现非阻塞IO操作 一、关键步骤分解 1. 创建Socket实例: ```csharp var clientSocket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp ); ``` 2. 异步连接: ```csharp await clientSocket.ConnectAsync(IPAddress.Parse("127.0.0.1"), 8080); ``` $ConnectAsync$返回`Task`对象,使用`await`等待连接完成 3. 发送数据: ```csharp byte[] buffer = Encoding.UTF8.GetBytes("Hello Server"); int bytesSent = await clientSocket.SendAsync( new ArraySegment<byte>(buffer), SocketFlags.None ); ``` 4. 接收数据: ```csharp var receiveBuffer = new byte[1024]; var result = await clientSocket.ReceiveAsync( new ArraySegment<byte>(receiveBuffer), SocketFlags.None ); string response = Encoding.UTF8.GetString(receiveBuffer, 0, result.ReceivedBytes); ``` 二、完整示例结构 ```csharp public async Task StartClientAsync() { try { using Socket client = new Socket(/* 参数 */); await ConnectAsync(client); await SendAsync(client, "Hello"); string response = await ReceiveAsync(client); Console.WriteLine($"Received: {response}"); } catch (SocketException ex) { Console.WriteLine($"Socket错误: {ex.SocketErrorCode}"); } } ``` 三、注意事项 1. 异常处理: - 必须捕获$SocketException$和$ObjectDisposedException$ - 检查$SocketError$枚举判断具体错误 2. 缓冲区管理: - 建议使用$ArrayPool<byte>.Shared$租用缓冲区 - 避免在异步操作中修改正在使用的缓冲区 3. 取消操作: ```csharp var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); await clientSocket.ConnectAsync(remoteEP, cts.Token); ``` 四、性能优化建议 1. 设置Socket选项: ```csharp clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); ``` 2. 使用$NetworkStream$封装: ```csharp var stream = new NetworkStream(clientSocket); await stream.WriteAsync(buffer, 0, buffer.Length); ``` 异步Socket客户端的优势: $$吞吐量 = \frac{并发操作数量}{单位时间} \times (1 - 阻塞率)$$ 通过异步IO可显著提升并发处理能力,适用于: - 高频交易系统 - 实时聊天应用 - 物联网设备通信 - 游戏服务器连接 最新改进(C# 10+): - 支持$ValueTask$减少内存分配 - 增强的$Socket.SendFileAsync$方法 - 改进的取消令牌传播机制 建议结合$System.Threading.Channels$实现生产者/消费者模式,构建高效的消息队列处理机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值