Socket编程 (异步通讯) (Tcp,Udp) - Part2

简介: 原文 http://www.cnblogs.com/zengqinglei/archive/2013/04/30/3051629.html Socket编程(异步通讯)(Tcp,Udp)   上一章主要展示了Socket的Tcp\Udp两种协议下的基本通讯方式,属于同步通讯。

原文 http://www.cnblogs.com/zengqinglei/archive/2013/04/30/3051629.html

Socket编程(异步通讯)(Tcp,Udp)

  上一章主要展示了Socket的Tcp\Udp两种协议下的基本通讯方式,属于同步通讯。至于一个服务器对应多个客户端,或者对应多个请求,我们采用的是多线程的方式来解决此问题。然而本章节我们将有更好的方式去实现它:Socket在Tcp\Udp两种协议下的异步通讯方式。

基于Tcp协议异步:

  BeginAccept方法和EndAccept方法

  包含在System.Net.Sockets命名空间下。异步Tcp使用BeginAccept方法开始接受新的客户端连接请求,该方法中系统自动利用线程池创建需要的线程,并在操作完成时利用异步回调机制调用提供给它的方法,同时返回相应的状态参数,然后方可利用EndAccept方法结束该连接请求.

  BeginRecive方法和EndRecive方法

  异步Tcp使用BeginRecive方法和开始接受客户端发送的的消息,该方法如上同理,接受完毕后调用回调函数传递相应的状态参数。利用EndRecive方法接受接受消息。

  至于BeginSend方法和EndSend方法、BeginConnect方法和EndConnect方法与上类似。

 

下面我们来看看如何在Tcp协议下进行客户端与服务器端之间的通讯:

服务器端:  

复制代码
using System;
using System.Collections.Generic;
using System.Text;
#region 命名空间
using System.Net;
using System.Net.Sockets;
using System.Threading;
#endregion

namespace AsynServerConsole
{
    /// <summary>
    /// Tcp协议异步通讯类(服务器端)
    /// </summary>
    public class AsynTcpServer
    {
        #region Tcp协议异步监听
        /// <summary>
        /// Tcp协议异步监听
        /// </summary>
        public void StartListening()
        {
            //主机IP
            IPEndPoint serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8686);
            Socket tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            tcpServer.Bind(serverIp);
            tcpServer.Listen(100);
            Console.WriteLine("异步开启监听...");
            AsynAccept(tcpServer);
        }
        #endregion

        #region 异步连接客户端
        /// <summary>
        /// 异步连接客户端
        /// </summary>
        /// <param name="tcpServer"></param>
        public void AsynAccept(Socket tcpServer)
        {
            tcpServer.BeginAccept(asyncResult =>
            {
                Socket tcpClient = tcpServer.EndAccept(asyncResult);
                Console.WriteLine("server<--<--{0}", tcpClient.RemoteEndPoint.ToString());
                AsynSend(tcpClient, "收到连接...");//发送消息
                AsynAccept(tcpServer);
                AsynRecive(tcpClient);
            }, null);
        }
        #endregion

        #region 异步接受客户端消息
        /// <summary>
        /// 异步接受客户端消息
        /// </summary>
        /// <param name="tcpClient"></param>
        public void AsynRecive(Socket tcpClient)
        {
            byte[] data = new byte[1024];
            try
            {
                tcpClient.BeginReceive(data, 0, data.Length, SocketFlags.None,
                asyncResult =>
                {
                    int length = tcpClient.EndReceive(asyncResult);
                    Console.WriteLine("server<--<--client:{0}", Encoding.UTF8.GetString(data));
                    AsynSend(tcpClient, "收到消息...");
                    AsynRecive(tcpClient);
                }, null);
            }
            catch (Exception ex)
            {
                Console.WriteLine("异常信息:", ex.Message);
            }
        }
        #endregion

        #region 异步发送消息
        /// <summary>
        /// 异步发送消息
        /// </summary>
        /// <param name="tcpClient">客户端套接字</param>
        /// <param name="message">发送消息</param>
        public void AsynSend(Socket tcpClient, string message)
        {
            byte[] data = Encoding.UTF8.GetBytes(message);
            try
            {
                tcpClient.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
                {
                    //完成发送消息
                    int length = tcpClient.EndSend(asyncResult);
                    Console.WriteLine("server-->-->client:{0}", message);
                }, null);
            }
            catch (Exception ex)
            {
                Console.WriteLine("异常信息:{0}", ex.Message);
            }
        }
        #endregion
    }
}
复制代码

 

客户端:

复制代码
using System;
using System.Collections.Generic;
using System.Text;
#region 命名空间
using System.Net;
using System.Net.Sockets;
using System.Threading;
#endregion

namespace AsynClientConsole
{
    /// <summary>
    /// Tcp协议异步通讯类(客户端)
    /// </summary>
    public class AsynTcpClient
    {
        #region 异步连接
        /// <summary>
        /// Tcp协议异步连接服务器
        /// </summary>
        public void AsynConnect()
        {
            //主机IP
            IPEndPoint serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8686);
            Socket tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            tcpClient.BeginConnect(serverIp, asyncResult =>
            {
                tcpClient.EndConnect(asyncResult);
                Console.WriteLine("client-->-->{0}", serverIp.ToString());
                AsynSend(tcpClient, "我上线了...");
                AsynSend(tcpClient, "第一次发送消息...");
                AsynSend(tcpClient, "第二次发送消息...");
                AsynRecive(tcpClient);
            }, null);
        }
        #endregion

        #region 异步接受消息
        /// <summary>
        /// 异步连接客户端回调函数
        /// </summary>
        /// <param name="tcpClient"></param>
        public void AsynRecive(Socket tcpClient)
        {
            byte[] data = new byte[1024];
            tcpClient.BeginReceive(data, 0, data.Length, SocketFlags.None, asyncResult =>
            {
                int length = tcpClient.EndReceive(asyncResult);
                Console.WriteLine("client<--<--server:{0}", Encoding.UTF8.GetString(data));
                AsynRecive(tcpClient);
            }, null);
        }
        #endregion

        #region 异步发送消息
        /// <summary>
        /// 异步发送消息
        /// </summary>
        /// <param name="tcpClient">客户端套接字</param>
        /// <param name="message">发送消息</param>
        public void AsynSend(Socket tcpClient, string message)
        {
            byte[] data = Encoding.UTF8.GetBytes(message);
            tcpClient.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
            {
                //完成发送消息
                int length = tcpClient.EndSend(asyncResult);
                Console.WriteLine("client-->-->server:{0}", message);
            }, null);
        }
        #endregion
    }
}
复制代码

通讯效果如下图:

服务器:

客户端:

上面我们完成了基于Tcp协议下的Socket通讯,那么Udp协议下的通讯我们将以什么样的形式来通讯呢?毕竟Udp协议下是无连接模式。

基于Udp协议的异步通讯:

  其实与Tcp协议具有的方法类似,但由于Udp协议是无连接模式,我们所用到方法就无BeginConnect和EndConnect方法。我们所要做的就是收发消息的处理。

  在Udp协议的异步通讯中,我们需要注意一下几个编程点:

  1.在EndRecive方法中,由于无状态返回模式,不能返回发送端的Remote,所以我们需要在该方法中获取活动端的Remote,然后利用EndRecive方法结束接受该消息接受。

  2.客户端由于无需Connect到服务器端,但是需要先向服务器端发送一个请求如Send一些消息。让服务器端确定自己Remote,然后可利用Recive方法接收其他终端发送过来的消息。

下面将演示Udp协议下异步通讯:

服务器端:

 

复制代码
using System;
using System.Collections.Generic;
using System.Text;
#region 命名空间
using System.Net;
using System.Net.Sockets;
using System.Threading;
#endregion

namespace AsynServerConsole
{
    /// <summary>
    /// Udp协议异步通讯类(服务器端)
    /// </summary>
    public class AsynUdpServer
    {
        #region 容器对象
        /// <summary>
        /// 容器对象
        /// </summary>
        public class StateObject
        {
            //服务器端
            public Socket udpServer = null;
            //接受数据缓冲区
            public byte[] buffer = new byte[1024];
            //远程终端
            public EndPoint remoteEP;
        }

        public StateObject state;
        #endregion

        #region 服务器绑定终端节点
        public void ServerBind()
        {
            //主机IP
            IPEndPoint serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8686);
            Socket udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            udpServer.Bind(serverIp);
            Console.WriteLine("server ready...");
            IPEndPoint clientIp = new IPEndPoint(IPAddress.Any, 0);
            state = new StateObject();
            state.udpServer = udpServer;
            state.remoteEP = (EndPoint)clientIp;
            AsynRecive();
        }
        #endregion

        #region 异步接受消息
        public void AsynRecive()
        {
            state.udpServer.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, SocketFlags.None, ref state.remoteEP,
                new AsyncCallback(ReciveCallback), null);
        }
        #endregion

        #region 异步接受消息回调函数
        public void ReciveCallback(IAsyncResult asyncResult)
        {
            if (asyncResult.IsCompleted)
            {
                //获取发送端的终节点
                IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 0);
                EndPoint remoteEP = (EndPoint)ipep;
                state.udpServer.EndReceiveFrom(asyncResult, ref remoteEP);
                Console.WriteLine("server<--<--client:{0}", Encoding.UTF8.GetString(state.buffer));
                //向发送端通知:收到消息
                state.remoteEP = remoteEP;
                AsynSend("收到消息");
                //继续接受消息
                AsynRecive();
            }
        }
        #endregion

        #region 异步发送消息
        public void AsynSend(string message)
        {
            Console.WriteLine("server-->-->client:{0}", message);
            byte[] buffer = Encoding.UTF8.GetBytes(message);
            state.udpServer.BeginSendTo(buffer, 0, buffer.Length, SocketFlags.None, state.remoteEP,
                new AsyncCallback(SendCallback), null);
        }
        #endregion

        #region 异步发送消息回调函数
        public void SendCallback(IAsyncResult asyncResult)
        {
            //消息发送完毕
            if (asyncResult.IsCompleted)
            {
                state.udpServer.EndSendTo(asyncResult);
            }
        }
        #endregion
    }
}
复制代码

 

客户端:

 

 

复制代码
using System;
using System.Collections.Generic;
using System.Text;
#region 命名空间
using System.Net;
using System.Net.Sockets;
using System.Threading;
#endregion

namespace AsynClientConsole
{
    /// <summary>
    /// Udp协议异步通讯类(客户端)
    /// </summary>
    public class AsynUdpClient
    {
        #region 容器对象
        /// <summary>
        /// 容器对象
        /// </summary>
        public class StateObject
        {
            //客户端套接字
            public Socket udpClient = null;
            //接收信息缓冲区
            public byte[] buffer = new byte[1024];
            //服务器端终节点
            public IPEndPoint serverIp;
            //远程终端节点
            public EndPoint remoteEP;
        }

        public StateObject state;
        #endregion

        #region 客户端初始化
        public void InitClient()
        {
            state = new StateObject();
            state.udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            state.serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8686);
            state.remoteEP = (EndPoint)(new IPEndPoint(IPAddress.Any, 0));
            //此处注意:
            //  由于当前是客户端,所以没有绑定终节点
            //  不可直接接收消息,必须先向其他终端发送信息告知本机终节点
            AsynSend("第1次发送消息");
            AsynSend("第2次发送消息");
            AsynRecive();
        }
        #endregion

        #region 异步接收来自其他终端发送的消息
        public void AsynRecive()
        {
            state.udpClient.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, SocketFlags.None, ref state.remoteEP,
                new AsyncCallback(ReciveCallback), null);
        }
        #endregion

        #region 异步接收来自其他终端发送的消息回调函数
        public void ReciveCallback(IAsyncResult asyncResult)
        {
            //信息接收完成
            if (asyncResult.IsCompleted)
            {
                state.udpClient.EndReceiveFrom(asyncResult, ref state.remoteEP);
                Console.WriteLine("client<--<--{0}:{1}", state.remoteEP.ToString(), Encoding.UTF8.GetString(state.buffer));
                AsynRecive();
            }
        }
        #endregion

        #region 异步发送消息
        public void AsynSend(string message)
        {
            Console.WriteLine("client-->-->{0}:{1}", state.serverIp.ToString(), message);
            byte[] buffer = Encoding.UTF8.GetBytes(message);
            state.udpClient.BeginSendTo(buffer, 0, buffer.Length, SocketFlags.None, state.serverIp,
                new AsyncCallback(SendCallback), null);
        }
        #endregion

        #region 异步发送消息回调函数
        public void SendCallback(IAsyncResult asyncResult)
        {
            //消息发送完成
            if (asyncResult.IsCompleted)
            {
                state.udpClient.EndSendTo(asyncResult);
            }
        }
        #endregion
    }
}
复制代码

 

通讯效果如下图:

 

服务器:

 

客户端:

 

总结:基于异步模式的通讯无须采用多线程来服务多个客户端以及多个请求,这样的通讯模式效率更高。

  同步上面Tcp效果展示图,我们发现客户端分几次连续发送的消息被服务器端一次接收了,读成了一条数据,而这就是Socket通讯基于Tcp协议下发生的粘包问题,下面一种我们将着重对Tcp协议的通讯信息封包,拆包以解决上面问题。

  同样Udp协议通讯下属于无连接模式通讯,客户端只管将消息发送出去,或者由于网络原因,而造成的丢包问题,下一章也将采用一定的方式解决。

最后附上源码:Socket-Part2.zip

作者:曾庆雷
出处:http://www.cnblogs.com/zengqinglei
本页版权归作者和博客园所有,欢迎转载,但未经作者同意必须保留此段声明, 且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利
目录
相关文章
|
2月前
|
网络协议 安全 网络安全
什么是TCP/UDP/HTTP?它们如何影响你的内网穿透体验?
数据的传输离不开各种协议,它们就像现实世界中的交通规则,规定了数据如何打包、寻址、传输和接收。对于使用内网穿透的用户来说,理解TCP、UDP和HTTP这些基础协议的特点,能帮助你更好地理解其性能表现,并选择最适合的配置方案。
|
4月前
|
网络协议 API
区分TCP/IP、HTTP、Socket三者的差异
HTTP关注于应用层的协议规范,而Socket关注于为应用程序提供编程中的网络功能,这些功能本身是建立在底层的TCP/IP协议之上;HTTP是更高层次的抽象,定义了如何包装数据,而TCP/IP定义了如何传送数据,Socket则是两者之间在程序中的桥梁,负责实现细节。在实际应用中,通常HTTP通信也是通过Socket来完成,因为HTTP仅是具体内容的封装形式,而Socket则是传送方式的实现形式。
479 16
|
4月前
|
网络协议 安全 网络安全
详细阐述 TCP、UDP、ICMPv4 和 ICMPv6 协议-以及防火墙端口原理优雅草卓伊凡
详细阐述 TCP、UDP、ICMPv4 和 ICMPv6 协议-以及防火墙端口原理优雅草卓伊凡
430 2
|
7月前
|
网络协议 Java 开发工具
全平台开源即时通讯IM框架MobileIMSDK:7端+TCP/UDP/WebSocket协议,鸿蒙NEXT端已发布,5.7K Stars
全平台开源即时通讯IM框架MobileIMSDK:7端+TCP/UDP/WebSocket协议,鸿蒙NEXT端已发布,5.7K Stars
447 1
|
11月前
|
监控 网络协议 网络性能优化
不再困惑!一文搞懂TCP与UDP的所有区别
本文介绍网络基础中TCP与UDP的区别及其应用场景。TCP是面向连接、可靠传输的协议,适用于HTTP、FTP等需要保证数据完整性的场景;UDP是无连接、不可靠但速度快的协议,适合DNS、RIP等对实时性要求高的应用。文章通过对比两者在连接方式、可靠性、速度、流量控制和数据包大小等方面的差异,帮助读者理解其各自特点与适用场景。
|
11月前
|
存储 网络协议 安全
用于 syslog 收集的协议:TCP、UDP、RELP
系统日志是从Linux/Unix设备及网络设备生成的日志,可通过syslog服务器集中管理。日志传输支持UDP、TCP和RELP协议。UDP无连接且不可靠,不推荐使用;TCP可靠,常用于rsyslog和syslog-ng;RELP提供可靠传输和反向确认。集中管理日志有助于故障排除和安全审计,EventLog Analyzer等工具可自动收集、解析和分析日志。
721 2
|
12月前
|
网络协议 网络性能优化 数据处理
深入解析:TCP与UDP的核心技术差异
在网络通信的世界里,TCP(传输控制协议)和UDP(用户数据报协议)是两种核心的传输层协议,它们在确保数据传输的可靠性、效率和实时性方面扮演着不同的角色。本文将深入探讨这两种协议的技术差异,并探讨它们在不同应用场景下的适用性。
411 4
|
12月前
|
监控 网络协议 网络性能优化
网络通信的核心选择:TCP与UDP协议深度解析
在网络通信领域,TCP(传输控制协议)和UDP(用户数据报协议)是两种基础且截然不同的传输层协议。它们各自的特点和适用场景对于网络工程师和开发者来说至关重要。本文将深入探讨TCP和UDP的核心区别,并分析它们在实际应用中的选择依据。
357 3
|
网络协议 测试技术 网络安全
Python编程-Socket网络编程
Python编程-Socket网络编程
155 0
|
网络协议 开发者 Python
深度探索Python Socket编程:从理论到实践,进阶篇带你领略网络编程的魅力!
【7月更文挑战第25天】在网络编程中, Python Socket编程因灵活性强而广受青睐。本文采用问答形式深入探讨其进阶技巧。**问题一**: Socket编程基于TCP/IP,通过创建Socket对象实现通信,支持客户端和服务器间的数据交换。**问题二**: 提升并发处理能力的方法包括多线程(适用于I/O密集型任务)、多进程(绕过GIL限制)和异步IO(asyncio)。**问题三**: 提供了一个使用asyncio库实现的异步Socket服务器示例,展示如何接收及响应客户端消息。通过这些内容,希望能激发读者对网络编程的兴趣并引导进一步探索。
205 4