【问题标题】:How to do async udp networking right?如何正确进行异步 udp 网络?
【发布时间】:2013-08-18 17:09:43
【问题描述】:

与这里的许多其他人一样,我正在尝试创建一个网络库。需求基本上是这样的:

  • 异步工作并为实时应用程序做好准备(我想到了 FPS 游戏)
  • 使用 UDP 并根据需要在顶部设置一个瘦协议层
  • 原生使用 IPv6
  • 支持多个平台(阅读:我想要 Mono 支持!)

现在在阅读了一些关于如何做到这一点(最鼓舞人心的是Gaffer on Games)之后,我设置了我的开发环境,思考了如何做到这一点,并想出了这个基本的工作流程:

  1. 初始化一个套接字并告诉它使用“UDPv6”
  2. 将该套接字绑定到一个端口并使异常不会打扰用户。有一个“绑定”属性告诉他套接字设置正确。
  3. 了解本地计算机上的 NIC 支持的最大 MTU。
  4. 初始化多个 SocketAsyncEventArgs,告诉其 Completed 事件调用私有调度方法,并将其缓冲区设置为步骤 3 中最大 MTU 的大小。
  5. 使用第一个 SAEA 对象调用 Sockets ReceiveFromAsync 方法。

当数据进来时,我会做以下事情:

  1. 使用下一个空闲 SAEA 对象调用 ReceiveFromAsync 方法
  2. 从当前 SAEA 对象中获取缓冲区和 Sender 信息并使其再次可用
  3. 使用收到的消息触发一个新事件。

我对这种方法做了一些测试,效果很好。我每 10 毫秒向它发送一条消息,其中包含 200 个字节的数据,持续 10000 个周期,CPU 或内存负载几乎没有增加。只有网卡负载在增加。但是我想出了一些问题|问题:

  • 当我处置我的 PeerSocket 类(即持有套接字)时,我处置每个 SAEA 对象。但由于其中至少有一个仍在侦听新消息,因此会引发 ObjectDisposedException。有没有办法让它停止听?
  • MTU 可能会因与其他对等方的方式不同而有所不同,也许每个 SAEA 对象的缓冲区应使用不同的指标来确定缓冲区大小?
  • 我还不确定如何处理碎片化的数据报。我将继续将“可靠性标头”写入要发送的数据报中,但是如果数据报被拆分,我不知道此标头信息,对吧?

该库有望在某一天对其他人有用,并且它的存储库是公开可用的。截至本题,当前提交可以在here

找到

【问题讨论】:

  • 你有什么问题?
  • 我所有的问题都是问题,我认为这已经说清楚了。

标签: c# asynchronous .net-4.0 udp socketasynceventargs


【解决方案1】:

哇,这真是一个巨大的主题。如果你在学习之前没有学习过网络套接字。我可以给你要点,但绝对不够。

客户:

public void Get()
    {
        string data;
        string input;
        IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050);

        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);


        try
        {
            socket.Connect(ipep);
        }
        catch (SocketException e)
        {
            Console.WriteLine("Unable to connect to server");
            Console.WriteLine(e.ToString());
            return;
        }

        NetworkStream ns = new NetworkStream(socket);
        StreamWriter sw = new StreamWriter(ns);
        StreamReader sr = new StreamReader(ns);

        data = sr.ReadLine();
        Console.WriteLine(data);

        while (true)
        {
            input = Console.ReadLine();
            if (input == "exite")
                break;

            sw.WriteLine(input);
            sw.Flush();

            data = sr.ReadLine();
            Console.WriteLine(data);
        }
        Console.WriteLine("Disconnected from server...");
        socket.Close();
        ns.Close();
        sr.Close();
        sr.Close();
    }

服务器:

public void Get()
    {
        string data;
        IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);

        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        socket.Bind(ipep);
        socket.Listen(10);
        Console.WriteLine("Waiting for a client...");

        Socket client = socket.Accept();
        IPEndPoint newclient = (IPEndPoint)client.RemoteEndPoint;
        Console.WriteLine("Connected with: {0}, at Port: {1}", newclient.Address, newclient.Port);

        NetworkStream ns = new NetworkStream(client);
        StreamReader sr = new StreamReader(ns);
        StreamWriter sw = new StreamWriter(ns);

        string welcome = "Welcome to my test server";
        sw.Write(welcome);
        sw.Flush();

        while (true)
        {
            try
            {
                data = sr.ReadLine();
            }
            catch (IOException)
            {
                break;
            }
            Console.WriteLine(data);
            sw.WriteLine(data);
            sw.Flush();
        }
        Console.WriteLine("Disconnected from {0}", newclient.Address);
        sw.Close();
        ns.Close();
        sr.Close();
    }

请在控制台应用程序上试用一下,看看它是如何工作的。

基本上,服务器打开端口(本例中为 9050)等待客户端连接,然后客户端连接到服务器,然后开始通信。

您提到您必须使用 UDP 套接字,我想您应该了解 udp,但如果不了解,您最好检查一下 TCP 和 UDP 之间的区别,尤其是验证数据是否到达所需目的地的方式(即席概念等)。

【讨论】:

  • 感谢 Jacob 的努力,但我想将 UDP 套接字与 IPv6 一起使用,与 SocketAsyncEventArgs 异步工作。我想我很了解网络基础知识,这就是为什么这不是“如何将两台 PC 相互连接”的问题;)
猜你喜欢
  • 1970-01-01
  • 2015-08-25
  • 1970-01-01
  • 2014-05-16
  • 2015-08-28
  • 2015-03-16
  • 1970-01-01
  • 2017-07-13
  • 2017-07-22
相关资源
最近更新 更多