• 网络编程-Socket编程(异步通讯)(Tcp,Udp)


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

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

    基于Tcp协议异步:

      BeginAccept方法和EndAccept方法

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

      BeginRecive方法和EndRecive方法

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

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

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

    服务器端: 

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Text;
      4 #region 命名空间
      5 using System.Net;
      6 using System.Net.Sockets;
      7 using System.Threading;
      8 #endregion
      9 
     10 namespace AsynServerConsole
     11 {
     12     /// <summary>
     13     /// Tcp协议异步通讯类(服务器端)
     14     /// </summary>
     15     public class AsynTcpServer
     16     {
     17         #region Tcp协议异步监听
     18         /// <summary>
     19         /// Tcp协议异步监听
     20         /// </summary>
     21         public void StartListening()
     22         {
     23             //主机IP
     24             IPEndPoint serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8686);
     25             Socket tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
     26             tcpServer.Bind(serverIp);
     27             tcpServer.Listen(100);
     28             Console.WriteLine("异步开启监听...");
     29             AsynAccept(tcpServer);
     30         }
     31         #endregion
     32 
     33         #region 异步连接客户端
     34         /// <summary>
     35         /// 异步连接客户端
     36         /// </summary>
     37         /// <param name="tcpServer"></param>
     38         public void AsynAccept(Socket tcpServer)
     39         {
     40             tcpServer.BeginAccept(asyncResult =>
     41             {
     42                 Socket tcpClient = tcpServer.EndAccept(asyncResult);
     43                 Console.WriteLine("server<--<--{0}", tcpClient.RemoteEndPoint.ToString());
     44                 AsynSend(tcpClient, "收到连接...");//发送消息
     45                 AsynAccept(tcpServer);
     46                 AsynRecive(tcpClient);
     47             }, null);
     48         }
     49         #endregion
     50 
     51         #region 异步接受客户端消息
     52         /// <summary>
     53         /// 异步接受客户端消息
     54         /// </summary>
     55         /// <param name="tcpClient"></param>
     56         public void AsynRecive(Socket tcpClient)
     57         {
     58             byte[] data = new byte[1024];
     59             try
     60             {
     61                 tcpClient.BeginReceive(data, 0, data.Length, SocketFlags.None,
     62                 asyncResult =>
     63                 {
     64                     int length = tcpClient.EndReceive(asyncResult);
     65                     Console.WriteLine("server<--<--client:{0}", Encoding.UTF8.GetString(data));
     66                     AsynSend(tcpClient, "收到消息...");
     67                     AsynRecive(tcpClient);
     68                 }, null);
     69             }
     70             catch (Exception ex)
     71             {
     72                 Console.WriteLine("异常信息:", ex.Message);
     73             }
     74         }
     75         #endregion
     76 
     77         #region 异步发送消息
     78         /// <summary>
     79         /// 异步发送消息
     80         /// </summary>
     81         /// <param name="tcpClient">客户端套接字</param>
     82         /// <param name="message">发送消息</param>
     83         public void AsynSend(Socket tcpClient, string message)
     84         {
     85             byte[] data = Encoding.UTF8.GetBytes(message);
     86             try
     87             {
     88                 tcpClient.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
     89                 {
     90                     //完成发送消息
     91                     int length = tcpClient.EndSend(asyncResult);
     92                     Console.WriteLine("server-->-->client:{0}", message);
     93                 }, null);
     94             }
     95             catch (Exception ex)
     96             {
     97                 Console.WriteLine("异常信息:{0}", ex.Message);
     98             }
     99         }
    100         #endregion
    101     }
    102 }
    View Code

    客户端:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Text;
     4 #region 命名空间
     5 using System.Net;
     6 using System.Net.Sockets;
     7 using System.Threading;
     8 #endregion
     9 
    10 namespace AsynClientConsole
    11 {
    12     /// <summary>
    13     /// Tcp协议异步通讯类(客户端)
    14     /// </summary>
    15     public class AsynTcpClient
    16     {
    17         #region 异步连接
    18         /// <summary>
    19         /// Tcp协议异步连接服务器
    20         /// </summary>
    21         public void AsynConnect()
    22         {
    23             //主机IP
    24             IPEndPoint serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8686);
    25             Socket tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    26             tcpClient.BeginConnect(serverIp, asyncResult =>
    27             {
    28                 tcpClient.EndConnect(asyncResult);
    29                 Console.WriteLine("client-->-->{0}", serverIp.ToString());
    30                 AsynSend(tcpClient, "我上线了...");
    31                 AsynSend(tcpClient, "第一次发送消息...");
    32                 AsynSend(tcpClient, "第二次发送消息...");
    33                 AsynRecive(tcpClient);
    34             }, null);
    35         }
    36         #endregion
    37 
    38         #region 异步接受消息
    39         /// <summary>
    40         /// 异步连接客户端回调函数
    41         /// </summary>
    42         /// <param name="tcpClient"></param>
    43         public void AsynRecive(Socket tcpClient)
    44         {
    45             byte[] data = new byte[1024];
    46             tcpClient.BeginReceive(data, 0, data.Length, SocketFlags.None, asyncResult =>
    47             {
    48                 int length = tcpClient.EndReceive(asyncResult);
    49                 Console.WriteLine("client<--<--server:{0}", Encoding.UTF8.GetString(data));
    50                 AsynRecive(tcpClient);
    51             }, null);
    52         }
    53         #endregion
    54 
    55         #region 异步发送消息
    56         /// <summary>
    57         /// 异步发送消息
    58         /// </summary>
    59         /// <param name="tcpClient">客户端套接字</param>
    60         /// <param name="message">发送消息</param>
    61         public void AsynSend(Socket tcpClient, string message)
    62         {
    63             byte[] data = Encoding.UTF8.GetBytes(message);
    64             tcpClient.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
    65             {
    66                 //完成发送消息
    67                 int length = tcpClient.EndSend(asyncResult);
    68                 Console.WriteLine("client-->-->server:{0}", message);
    69             }, null);
    70         }
    71         #endregion
    72     }
    73 }
    View Code

    通讯效果如下图:

    服务器:

    客户端:

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

    基于Udp协议的异步通讯:

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

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

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

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

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

    服务器端:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Text;
     4 #region 命名空间
     5 using System.Net;
     6 using System.Net.Sockets;
     7 using System.Threading;
     8 #endregion
     9 
    10 namespace AsynServerConsole
    11 {
    12     /// <summary>
    13     /// Udp协议异步通讯类(服务器端)
    14     /// </summary>
    15     public class AsynUdpServer
    16     {
    17         #region 容器对象
    18         /// <summary>
    19         /// 容器对象
    20         /// </summary>
    21         public class StateObject
    22         {
    23             //服务器端
    24             public Socket udpServer = null;
    25             //接受数据缓冲区
    26             public byte[] buffer = new byte[1024];
    27             //远程终端
    28             public EndPoint remoteEP;
    29         }
    30 
    31         public StateObject state;
    32         #endregion
    33 
    34         #region 服务器绑定终端节点
    35         public void ServerBind()
    36         {
    37             //主机IP
    38             IPEndPoint serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8686);
    39             Socket udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    40             udpServer.Bind(serverIp);
    41             Console.WriteLine("server ready...");
    42             IPEndPoint clientIp = new IPEndPoint(IPAddress.Any, 0);
    43             state = new StateObject();
    44             state.udpServer = udpServer;
    45             state.remoteEP = (EndPoint)clientIp;
    46             AsynRecive();
    47         }
    48         #endregion
    49 
    50         #region 异步接受消息
    51         public void AsynRecive()
    52         {
    53             state.udpServer.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, SocketFlags.None, ref state.remoteEP,
    54                 new AsyncCallback(ReciveCallback), null);
    55         }
    56         #endregion
    57 
    58         #region 异步接受消息回调函数
    59         public void ReciveCallback(IAsyncResult asyncResult)
    60         {
    61             if (asyncResult.IsCompleted)
    62             {
    63                 //获取发送端的终节点
    64                 IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 0);
    65                 EndPoint remoteEP = (EndPoint)ipep;
    66                 state.udpServer.EndReceiveFrom(asyncResult, ref remoteEP);
    67                 Console.WriteLine("server<--<--client:{0}", Encoding.UTF8.GetString(state.buffer));
    68                 //向发送端通知:收到消息
    69                 state.remoteEP = remoteEP;
    70                 AsynSend("收到消息");
    71                 //继续接受消息
    72                 AsynRecive();
    73             }
    74         }
    75         #endregion
    76 
    77         #region 异步发送消息
    78         public void AsynSend(string message)
    79         {
    80             Console.WriteLine("server-->-->client:{0}", message);
    81             byte[] buffer = Encoding.UTF8.GetBytes(message);
    82             state.udpServer.BeginSendTo(buffer, 0, buffer.Length, SocketFlags.None, state.remoteEP,
    83                 new AsyncCallback(SendCallback), null);
    84         }
    85         #endregion
    86 
    87         #region 异步发送消息回调函数
    88         public void SendCallback(IAsyncResult asyncResult)
    89         {
    90             //消息发送完毕
    91             if (asyncResult.IsCompleted)
    92             {
    93                 state.udpServer.EndSendTo(asyncResult);
    94             }
    95         }
    96         #endregion
    97     }
    98 }
    View Code

    客户端:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Text;
     4 #region 命名空间
     5 using System.Net;
     6 using System.Net.Sockets;
     7 using System.Threading;
     8 #endregion
     9 
    10 namespace AsynClientConsole
    11 {
    12     /// <summary>
    13     /// Udp协议异步通讯类(客户端)
    14     /// </summary>
    15     public class AsynUdpClient
    16     {
    17         #region 容器对象
    18         /// <summary>
    19         /// 容器对象
    20         /// </summary>
    21         public class StateObject
    22         {
    23             //客户端套接字
    24             public Socket udpClient = null;
    25             //接收信息缓冲区
    26             public byte[] buffer = new byte[1024];
    27             //服务器端终节点
    28             public IPEndPoint serverIp;
    29             //远程终端节点
    30             public EndPoint remoteEP;
    31         }
    32 
    33         public StateObject state;
    34         #endregion
    35 
    36         #region 客户端初始化
    37         public void InitClient()
    38         {
    39             state = new StateObject();
    40             state.udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    41             state.serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8686);
    42             state.remoteEP = (EndPoint)(new IPEndPoint(IPAddress.Any, 0));
    43             //此处注意:
    44             //  由于当前是客户端,所以没有绑定终节点
    45             //  不可直接接收消息,必须先向其他终端发送信息告知本机终节点
    46             AsynSend("第1次发送消息");
    47             AsynSend("第2次发送消息");
    48             AsynRecive();
    49         }
    50         #endregion
    51 
    52         #region 异步接收来自其他终端发送的消息
    53         public void AsynRecive()
    54         {
    55             state.udpClient.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, SocketFlags.None, ref state.remoteEP,
    56                 new AsyncCallback(ReciveCallback), null);
    57         }
    58         #endregion
    59 
    60         #region 异步接收来自其他终端发送的消息回调函数
    61         public void ReciveCallback(IAsyncResult asyncResult)
    62         {
    63             //信息接收完成
    64             if (asyncResult.IsCompleted)
    65             {
    66                 state.udpClient.EndReceiveFrom(asyncResult, ref state.remoteEP);
    67                 Console.WriteLine("client<--<--{0}:{1}", state.remoteEP.ToString(), Encoding.UTF8.GetString(state.buffer));
    68                 AsynRecive();
    69             }
    70         }
    71         #endregion
    72 
    73         #region 异步发送消息
    74         public void AsynSend(string message)
    75         {
    76             Console.WriteLine("client-->-->{0}:{1}", state.serverIp.ToString(), message);
    77             byte[] buffer = Encoding.UTF8.GetBytes(message);
    78             state.udpClient.BeginSendTo(buffer, 0, buffer.Length, SocketFlags.None, state.serverIp,
    79                 new AsyncCallback(SendCallback), null);
    80         }
    81         #endregion
    82 
    83         #region 异步发送消息回调函数
    84         public void SendCallback(IAsyncResult asyncResult)
    85         {
    86             //消息发送完成
    87             if (asyncResult.IsCompleted)
    88             {
    89                 state.udpClient.EndSendTo(asyncResult);
    90             }
    91         }
    92         #endregion
    93     }
    94 }
    View Code

    通讯效果如下图:

    服务器:

    客户端:

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

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

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

    作者:曾庆雷
    出处:http://www.cnblogs.com/zengqinglei
    本页版权归作者和博客园所有,欢迎转载,但未经作者同意必须保留此段声明, 且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利
  • 相关阅读:
    JavaScript 变量类型 保存内存中的位置 和 引用
    https连接过程
    微信消息自动回复 json版
    RabbitMQ安装
    nginx反向代理
    小程序接口记录
    nginx同服务器不同目录的差别配置
    nginx URL隐藏index.php
    Laravel 打印SQL语句
    laravel PostTooLargeException
  • 原文地址:https://www.cnblogs.com/anyihen/p/12984518.html
Copyright © 2020-2023  润新知