UN2L12——UDP异步通信常用方法

本章代码关键字

1
2
3
4
5
6
7
socket.BeginSendTo()                    //异步发送消息,传入字节数组,偏移量,最大发送字节数,标识,目标IP与端口,回调函数和回调函数的参数
socket.EndSendTo() //结束异步发送消息,传入回调函数的参数,返回发送了多少字节
socket.BeginReceiveFrom() //异步接收消息,传入字节数组,偏移量,最大接收字节数,标识,记录发送者IP与端口的对象,回调函数和回调函数的参数
socket.EndReceiveFrom() //结束异步接收消息方法,传入回调函数的参数,返回接收了多少字节
socketAsyncEventArgs.RemoteEndPoint //设置发送到哪个IP地址和端口号或者记录发送者IP地址和端口号的属性
socket.SendToAsync() //异步发送消息
socket.ReceiveFromAsync() //异步接收消息

Socket的UDP通信中的异步方法

通过之前的学习,UDP用到的通信相关方法主要就是SendTo​和ReceiveFrom
所以在讲解UDP异步通信时,也主要是围绕着收发消息相关方法来讲解

由于学习了TCP相关的知识点,所以UDP的相关内容的学习就变得简单了
他们异步通信的唯一的区别就是API不同,使用规则都是一致的

  1. Begin​开头的API

    UDP通信的异步方法使用方式上和TCP通信异步方法大同小异,可参考:TCP_Begin开头的API

    • UDP发送消息Begin​开头异步方法

      UDP开始异步发送消息使用socket.BeginSendTo()

      • 参数一:要发送的字节数组
      • 参数二:偏移量,从字节数组的第几位开始发送,将一个消息分开发送时可以使用
      • 参数三:发送消息字节数组最多发送出去多少字节,将一个消息分开发送时可以使用
      • 参数四:SocketFlag​枚举,也就是标识,一般传入空标识SocketFlag.None​即可
      • 参数五:要发送消息到哪个IP地址和端口号
      • 参数六:回调函数,参数为IAsyncResult
      • 参数七:传入到回调函数内的参数,一般传入socket​自己,在回调函数内通过iAsyncResult.AsyncState​获取

      在发送了消息后,会执行参数五的回调函数,并将参数六传入进去,可通过iAsyncResult.AsyncState​获取
      通过socket.EndSendTo()​可以获取发送出去了多少字节,需要传入回调函数的参数
      一般不需要获取发送出去了多少字节,除非要将一个消息分批发送

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      void Start()
      {
      Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
      byte[] bytes = Encoding.UTF8.GetBytes("123123123123123");
      EndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
      socket.BeginSendTo(bytes, 0, bytes.Length, SocketFlags.None, ipPoint, SendToOver, socket);
      }

      private void SendToOver(IAsyncResult result)
      {
      try
      {
      Socket s = result.AsyncState as Socket;
      s.EndSendTo(result);
      print("发送成功");
      }
      catch (SocketException e)
      {
      print("发送失败:" + e.SocketErrorCode + e.Message);
      }
      }
    • UDP接收消息Begin​开头异步方法

      开始异步接收消息使用socket.BeginReceiveFrom()

      • 参数一:用于接收消息的字节数组
      • 参数二:偏移量,相当于从接收消息字节数组的第几位开始接收,处理分包、黏包可以就利用该参数
      • 参数三:接收消息字节数组还能接收多少字节
      • 参数四:SocketFlag​枚举,也就是标识,一般传入空标识SocketFlag.None​即可
      • 参数五:用来记录发送者IP地址和端口号的EndPoint
      • 参数六:回调函数,参数为IAsyncResult
      • 参数七:传入到回调函数内的参数,一般传入记录发送者的EndPoint​和socket​自己,在回调函数内通过iAsyncResult.AsyncState​获取

      在接收到消息后,会执行参数五的回调函数,并将参数六传入进去,可通过iAsyncResult.AsyncState​获取
      需要通过socket.EndReceiveFrom()​获取接收到了多少字节,需要传入回调函数的参数,
      在回调函数内就可以处理消息,执行socket.BeginReceiveFrom()​开始下一次消息监听

      同样的,因为socket.BeginReceiveFrom()​是异步方法,回调方法会继续执行,所以不构成递归,不需要担心爆栈(前提是正常运行)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      private byte[] cacheBytes = new byte[512];

      void Start()
      {
      Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
      EndPoint ipPoint = new IPEndPoint(IPAddress.Any, 0);
      socket.BeginReceiveFrom(cacheBytes, 0, cacheBytes.Length, SocketFlags.None, ref ipPoint, ReceiveToOver, (socket, ipPoint));
      }

      private void ReceiveToOver(IAsyncResult result)
      {
      try
      {
      (Socket s, EndPoint ipPoint) info = ((Socket, EndPoint))result.AsyncState;
      //返回值 就是接收了多少个字节数
      int num = info.s.EndReceiveFrom(result, ref info.ipPoint);
      //处理消息
      print(Encoding.UTF8.GetString(cacheBytes, 0, num));
      //处理完消息,又继续接收消息
      info.s.BeginReceiveFrom(cacheBytes, 0, cacheBytes.Length, SocketFlags.None, ref info.ipPoint, ReceiveToOver, info);
      }
      catch (SocketException e)
      {
      print("接收消息失败:" + e.SocketErrorCode + e.Message);
      }
      }
  2. Async​结尾的API

    UDP通信的异步方法使用方式上和TCP通信异步方法大同小异,可参考:TCP_Async结尾的API

    • UDP发送消息Async​结尾异步方法

      首先实例化一个SocketAsyncEventArgs​,然后设置完成异步方法后执行的回调函数
      回调函数有两个参数,一个是执行异步方法的Socket​(object​类型),一个是传入到异步方法的SocketAsyncEventArgs
      然后,需要通过socketAsyncEventArgs.SetBuffer()​来设置要发送出去的字节数组
      通过socketAsyncEventArgs.RemoteEndPoint​来设置要发送到哪个IP地址和端口号

      在回调函数中,需要通过socketAsyncEventArgs.SocketError​来判断消息发送是否成功

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      void Start()
      {
      Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
      byte[] bytes = Encoding.UTF8.GetBytes("123123123123123");
      EndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);

      SocketAsyncEventArgs SendToArgs = new SocketAsyncEventArgs();
      SendToArgs.SetBuffer(bytes, 0, bytes.Length); //设置要发送的数据
      SendToArgs.RemoteEndPoint = ipPoint; //设置要发送的IP地址
      SendToArgs.Completed += SendToAsyncCallBack; //设置完成事件
      socket.SendToAsync(SendToArgs);
      }

      private void SendToAsyncCallBack(object s, SocketAsyncEventArgs args)
      {
      if (args.SocketError == SocketError.Success)
      {
      print("发送成功");
      }
      else
      {
      print("发送失败");
      }
      }
    • UDP接收消息Async​结尾异步方法

      首先实例化一个SocketAsyncEventArgs​,然后设置完成异步方法后执行的回调函数
      回调函数有两个参数,一个是执行异步方法的Socket​(object​类型),一个是传入到异步方法的SocketAsyncEventArgs
      然后,需要通过socketAsyncEventArgs.SetBuffer()​来设置要接收消息的字节数组
      通过socketAsyncEventArgs.RemoteEndPoint​来设置记录发送者的IP地址和端口号的EndPoint

      在回调函数中,需要通过socketAsyncEventArgs.SocketError​来判消息接收是否成功
      通过socketAsyncEventArgs.Buffer​来获取接收到消息的字节数组
      通过socketAsyncEventArgs.BytesTransferred​来获取接收到了多少字节数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      private byte[] cacheBytes = new byte[512];

      void Start()
      {
      Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

      SocketAsyncEventArgs receiveFromArgs = new SocketAsyncEventArgs();
      receiveFromArgs.SetBuffer(cacheBytes, 0, cacheBytes.Length); //设置要接收数据的数组
      receiveFromArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); //设置用于记录发送者IP和端口号的EndPoint
      receiveFromArgs.Completed += ReceiveFromAsyncCallBack; //设置完成事件
      socket.ReceiveFromAsync(receiveFromArgs);
      }

      private void ReceiveFromAsyncCallBack(object s, SocketAsyncEventArgs args)
      {
      if (args.SocketError == SocketError.Success)
      {
      print("接收成功");
      //处理消息
      print(Encoding.UTF8.GetString(args.Buffer, 0, args.BytesTransferred));
      Socket socket = s as Socket;
      //设置从第几位开始接,最多接多少字节
      args.SetBuffer(0, cacheBytes.Length);
      socket.ReceiveFromAsync(args);
      }
      else
      {
      print("接收失败");
      }
      }