Socket通信常用方法


使用tcp协议,链接服务器的方法
/// <summary> /// 连接使用tcp协议的服务端 /// </summary> /// <param name="ip">服务端的ip</param> /// <param name="port">服务端的端口号</param> /// <returns></returns> public static Socket ConnectServer(string ip, int port) { Socket s = null; try { IPAddress ipAddress = IPAddress.Parse(ip); IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port); s = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); s.Connect(ipEndPoint); if (s.Connected == false) s = null; } catch (Exception) { } return s; }

发送数据:

        /// <summary>
        /// 向远程主机发送数据
        /// </summary>
        /// <param name="socket">连接到远程主机的socket</param>
        /// <param name="buffer">待发送数据</param>
        /// <param name="outTime">发送超时时长,单位是秒(为-1时,将一直等待直到有数据需要发送)</param>
        /// <returns>0:发送成功;-1:超时;-2:出现错误;-3:出现异常</returns>
        public static int SendData(Socket socket, byte[] buffer, int outTime)
        {
            if (socket == null || socket.Connected == false)
            {
                throw new ArgumentException("参数socket为null,或者未连接到远程计算机");
            }
            if (buffer == null || buffer.Length == 0)
            {
                throw new ArgumentException("参数buffer为null ,或者长度为 0");
            }

            int flag = 0;
            try
            {
                int left = buffer.Length;
                int sndLen = 0;
                int hasSend = 0;

                while (true)
                {
                    if ((socket.Poll(outTime * 1000, SelectMode.SelectWrite) == true))
                    {   
                        // 收集了足够多的传出数据后开始发送
                        sndLen = socket.Send(buffer, hasSend, left, SocketFlags.None);
                        left -= sndLen;
                        hasSend += sndLen;

                        // 数据已经全部发送
                        if (left == 0)
                        {                                        
                            flag = 0;
                            break;
                        }
                        else
                        {
                            // 数据部分已经被发送
                            if (sndLen > 0)
                            {                                    
                                continue;
                            }
                            else // 发送数据发生错误
                            {                                                
                                flag = -2;
                                break;
                            }
                        }
                    }
                    else // 超时退出
                    {                                                        
                        flag = -1;
                        break;
                    }
                }
            }
            catch (SocketException)
            {
                //Log
                flag = -3;
            }
            return flag;
        }

发送string数据,用到方法System.Text.Encoding.Default.GetBytes对上面的方法进行封装

     /// <summary>
        /// 向远程主机发送数据
        /// </summary>
        /// <param name="socket">连接到远程主机的socket</param>
        /// <param name="buffer">待发送的字符串</param>
        /// <param name="outTime">发送数据的超时时间,单位是秒(为-1时,将一直等待直到有数据需要发送)</param>
        /// <returns>0:发送数据成功;-1:超时;-2:错误;-3:异常</returns>
        public static int SendData(Socket socket, string buffer, int outTime)
        {
            if (buffer == null || buffer.Length == 0)
            {
                throw new ArgumentException("buffer为null或则长度为0.");
            }
            return SendData(socket, System.Text.Encoding.Default.GetBytes(buffer), outTime);
        }

接收数据:

        /// <summary>
        /// 接收远程主机发送的数据
        /// </summary>
        /// <param name="socket">要接收数据且已经连接到远程主机的</param>
        /// <param name="buffer">接收数据的缓冲区(需要接收的数据的长度,由 buffer 的长度决定)</param>
        /// <param name="outTime">接收数据的超时时间,单位秒(指定为-1时,将一直等待直到有数据需要接收)</param>
        /// <returns></returns>
        public static int RecvData(Socket socket, byte[] buffer, int outTime)
        {
            if (socket == null || socket.Connected == false)
            {
                throw new ArgumentException("socket为null,或者未连接到远程计算机");
            }
            if (buffer == null || buffer.Length == 0)
            {
                throw new ArgumentException("buffer为null ,或者长度为 0");
            }

            buffer.Initialize();
            int left = buffer.Length;
            int curRcv = 0;
            int hasRecv=0;
            int flag = 0;

            try
            {
                while (true)
                {
                    if (socket.Poll(outTime * 1000, SelectMode.SelectRead) == true)
                    {   
                        // 已经有数据等待接收
                        curRcv = socket.Receive(buffer, hasRecv, left, SocketFlags.None);
                        left -= curRcv;
                        hasRecv += curRcv;

                        // 数据已经全部接收 
                        if (left == 0)
                        {                                    
                            flag = 0;
                            break;
                        }
                        else
                        {
                            // 数据已经部分接收
                            if (curRcv > 0)
                            {                                
                                continue;
                            }
                            else  // 出现错误
                            {                                           
                                flag = -2;
                                break;
                            }
                        }
                    }
                    else // 超时退出
                    {                                                    
                        flag = -1;
                        break;
                    }
                }
            }
            catch (SocketException)
            {
                //Log
                flag = -3;
            }
            return flag;
        }

原文地址:http://blog.youkuaiyun.com/love__coder/article/details/6649688

<think>嗯,用户想了解C#中Socket异步通信常用方法。首先,我需要回忆一下C#中处理Socket异步编程的不同方式,因为随着.NET版本的发展,方法可能有所变化。 用户可能是在开发一个需要处理多个客户端连接的服务端应用,比如聊天服务器或者游戏服务器,所以需要高效地处理并发连接而不会阻塞主线程。异步通信在这里非常关键,因为它能提高应用的响应能力和吞吐量。 首先,我应该想到传统的APM模式(Asynchronous Programming Model),也就是Begin/End方法,比如BeginReceive和EndReceive。这些方法基于IAsyncResult接口,虽然现在有更现代的方式,但了解它们还是有必要的,因为一些遗留代码可能还在使用。 接下来是EAP(Event-based Asynchronous Pattern),使用SocketAsyncEventArgs类,这种方法通过重用SocketAsyncEventArgs对象来减少资源分配,提高性能,特别是在高负载情况下。这个方法可能在高性能应用中被使用,比如网络服务器,用户可能需要了解如何高效地管理这些对象,避免频繁的GC。 然后是.NET Framework 4.5引入的基于Task的异步模式(TAP),也就是async/await关键字,这种方法让异步代码更易读写,比如使用SendAsync和ReceiveAsync方法。这对于现代开发来说非常重要,因为代码结构更清晰,维护起来也更容易。 用户可能想知道这些方法各自的优缺点,以及在不同场景下如何选择。例如,APM虽然老旧,但在旧项目中可能还需要维护;EAP适合高性能需求,但代码结构复杂;TAP则是现代推荐的做法,兼顾可读性和效率。 另外,可能需要提到一些关键点,比如如何避免回调地狱,资源释放的问题,异常处理等。比如在APM中,如果没有正确调用End方法,可能会导致资源泄漏;在EAP中,需要正确复用SocketAsyncEventArgs实例来减少开销。 用户可能还关心实际例子,比如如何实现一个简单的异步服务端,如何处理多个客户端的连接和数据传输。这时候需要给出一些代码片段,展示不同方法的使用方式,但要注意不要过于复杂,保持示例简洁明了。 最后,需要总结各种方法的适用场景,帮助用户根据项目需求选择最合适的方案。比如,如果是新项目,推荐使用async/await;如果需要极致性能,考虑SocketAsyncEventArgs;维护旧代码则需要理解APM模式。 可能还需要提醒一些常见错误,比如在异步方法中阻塞线程,或者没有正确处理异常导致程序崩溃。此外,提及.NET Core和.NET 5+中对异步Socket的支持是否有变化也很重要,确保用户的信息是最新的。 总之,需要从基本概念出发,分层次介绍不同的异步通信方法,结合使用场景和实际示例,帮助用户全面理解并选择适合的方案。</think>在 C# 中实现 Socket 异步通信时,主要通过以下三种模式处理并发连接和高性能网络通信需求: --- ### **1. APM 模式 (Asynchronous Programming Model)** **核心方法**:`BeginXXX`/`EndXXX` 方法对 ```csharp // 服务端接收连接 socket.BeginAccept(asyncResult => { Socket client = socket.EndAccept(asyncResult); // 开始接收数据 client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, client); }, null); // 接收数据回调 void ReceiveCallback(IAsyncResult ar) { int bytesRead = client.EndReceive(ar); // 处理数据... client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, client); } ``` **特点**: - 基于 `IAsyncResult` 接口的回调机制 - 代码嵌套较深,需手动管理异步状态 - 在旧版 .NET 中广泛使用 --- ### **2. EAP 模式 (Event-based Asynchronous Pattern)** **核心类**:`SocketAsyncEventArgs` ```csharp // 创建可重用的异步事件对象 var args = new SocketAsyncEventArgs(); args.Completed += OnAsyncOperationCompleted; args.SetBuffer(new byte[1024], 0, 1024); // 启动异步接收 if (!socket.ReceiveAsync(args)) { OnAsyncOperationCompleted(socket, args); // 同步完成时手动触发 } // 完成事件处理 void OnAsyncOperationCompleted(object sender, SocketAsyncEventArgs e) { if (e.SocketError == SocketError.Success && e.LastOperation == SocketAsyncOperation.Receive) { int bytesRead = e.BytesTransferred; // 处理数据后继续接收 socket.ReceiveAsync(e); } } ``` **优势**: - 通过对象池复用 `SocketAsyncEventArgs`,减少内存分配 - 适合高频、高并发场景(如游戏服务器) --- ### **3. TAP 模式 (Task-based Asynchronous Pattern)** **核心方法**:`SendAsync`/`ReceiveAsync`(.NET 5+) ```csharp // 使用 async/await 简化异步代码 async Task HandleClient(Socket client) { var buffer = new byte[1024]; while (true) { int bytesRead = await client.ReceiveAsync(buffer, SocketFlags.None); if (bytesRead == 0) break; // 处理数据... await client.SendAsync(buffer.AsMemory(0, bytesRead), SocketFlags.None); } } ``` **优势**: - 代码结构线性化,易读易维护 - 底层自动优化为最高效的实现(可能使用 `SocketAsyncEventArgs` 或 IOCP) --- ### **关键实践技巧** 1. **连接管理**: - 设置 `ReceiveTimeout`/`SendTimeout` 防止死连接 - 使用 `KeepAlive` 检测断线 2. **缓冲池优化**: ```csharp // 使用 ArrayPool 减少 GC 压力 var buffer = ArrayPool<byte>.Shared.Rent(1024); ArrayPool<byte>.Shared.Return(buffer); ``` 3. **异常处理**: ```csharp try { await socket.SendAsync(...); } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset) { // 处理客户端强制断开 } ``` --- ### **选择建议** | 场景 | 推荐方案 | |---------|---------| | 旧系统维护 | APM 模式 | | 高频交易系统 | `SocketAsyncEventArgs` + 对象池 | | 常规业务系统 | async/await + `NetworkStream` | .NET 5+ 后推荐优先使用 TAP 模式,其底层通过 `ValueTask` 和 `SocketTaskExtensions` 实现了高性能与代码简洁性的平衡。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值