如何正确地监听UDP数据包,同时在Visual c#中做其他事情

本文关键字:Visual 其他 监听 正确地 UDP 数据包 | 更新日期: 2023-09-27 18:05:54

好吧,我对c#很陌生,对图形化编程也很陌生。我正在使用Visual Studio 2015并在c#中编写应用程序。这段代码我已经摆弄了一段时间了。实际上,我的程序将发送HELLO到服务器,但是服务器不会发送HELLO返回。我现在在客户端和服务器之间没有防火墙,但是这个进程正在等待回复。老实说,我甚至不想这样做,我希望监听器总是在后台运行当用户做其他事情时,这样我的程序才能正常运行。所以我来找你哦,伟大的Stackoverflow…因为我肯定做错了!有人能告诉我正确的方向吗?

当前代码:

    private void button1_Click(object sender, EventArgs e)
    {
        byte[] data = new byte[512];
        byte[] result;
        SHA512 shaM = new SHA512Managed();
        result = shaM.ComputeHash(Encoding.UTF8.GetBytes(this.password.Text));
        var hash = BitConverter.ToString(result).Replace("-", "");
        this.send_message("127.0.0.1", 10545, 10545, "HELLO");
        this.send_message("127.0.0.1", 10545, 10545, "AUTH:" + this.login.Text + ":" + hash);
        //ListenForData.Start();
    }
    private void send_message(string server, int localPort, int remotePort, string message)
    {
        label4.Text = "Listening on port:" + localPort;
        IPEndPoint lep = new IPEndPoint(IPAddress.Any, localPort);
        Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        IPAddress loginServer = IPAddress.Parse(server.ToString());
        byte[] sendbuf = Encoding.ASCII.GetBytes(message);
        IPEndPoint ep = new IPEndPoint(loginServer, remotePort);
        s.SendTo(sendbuf, ep);
        try
        {
            UdpClient udpClient = new UdpClient(lep);
            byte[] bytes = udpClient.Receive(ref lep);
            label4.Text = ep.ToString();
        } catch ( Exception ex )
        {
            Console.WriteLine(ex.ToString());
        }
    }

UDPATE:

   private static void UDPListener(object obj)
    {
        Task.Run(async () =>
        {
            using (var udpClient = new UdpClient(10545))
            {
                string rMessage = "";
                string[] rArgs = new string[0];
                while (true)
                {
                    //IPEndPoint object will allow us to read datagrams sent from any source.
                    var receivedResults = await udpClient.ReceiveAsync();
                    rMessage += Encoding.ASCII.GetString(receivedResults.Buffer);
                    rArgs = rMessage.Split(new char[] { ':' });
                    if( rArgs[0] == "HELLO")
                    {
                        Console.Write("Received HELLO from server.");
                        byte[] data = new byte[512];
                        byte[] result;
                        SHA512 shaM = new SHA512Managed();
                        result = shaM.ComputeHash(Encoding.UTF8.GetBytes(obj.password.Text));
                        var hash = BitConverter.ToString(result).Replace("-", "");
                        send_message("127.0.0.1", 10545, 10545, "AUTH:" + obj.login.Text + ":" + hash);
                    }
                }
            }
        });
    }

如何正确地监听UDP数据包,同时在Visual c#中做其他事情

您可以放心地忽略Blindy的建议。UdpClient.ReceiveAsync()方法是专门围绕Task范式设计的,它比将一个线程专门用于接收数据要高效得多。因为Task是可等待的,所以将ReceiveAsync()方法与GUI程序(例如Winforms或WPF)集成也更容易。也就是说,为了使所有这些工作,您需要在UI线程中执行ReceiveAsync()调用,而不是在另一个Task中执行。

不幸的是,由于缺少一个良好的、最小的完整的代码示例来清楚地说明您的问题,所以不可能确切地说它看起来是什么样子。但它很可能涉及到使UDPListener()方法成为async方法,并从UI线程调用它。此外,既然您说方法不能访问非静态成员,那么显而易见的解决方案是使方法本身非静态。例如:
private static async Task UDPListener(object obj)
{
    using (var udpClient = new UdpClient(10545))
    {
        string rMessage = "";
        string[] rArgs = new string[0];
        while (true)
        {
            //IPEndPoint object will allow us to read datagrams sent from any source.
            var receivedResults = await udpClient.ReceiveAsync();
            rMessage += Encoding.ASCII.GetString(receivedResults.Buffer);
            rArgs = rMessage.Split(new char[] { ':' });
            if( rArgs[0] == "HELLO")
            {
                Console.Write("Received HELLO from server.");
                byte[] data = new byte[512];
                byte[] result;
                SHA512 shaM = new SHA512Managed();
                result = shaM.ComputeHash(Encoding.UTF8.GetBytes(obj.password.Text));
                var hash = BitConverter.ToString(result).Replace("-", "");
                send_message("127.0.0.1", 10545, 10545, "AUTH:" + obj.login.Text + ":" + hash);
            }
        }
    }
}

从你更新的问题中还不清楚你是否已经解决了你没有收到回复的问题。但是如果你没有改变你的send_message()方法,它有一些明显的问题,特别是在创建一个单独的UdpClient实例来接收数据报的上下文中。

最明显的问题是,在之后的发送消息之前,您没有创建期望用于接收响应的UdpClient实例。这是错误的,原因有二:
  1. 在您创建套接字之前,远程端点完全有可能发送响应。如果发生这种情况,数据报将被丢弃。
  2. 更重要的是,服务器的通常设计是将响应发送到首先向其发送消息的远程端点。例如,在这种情况下,它将是套接字s。即使在调用s.SendTo()之前创建了udpClient对象,应答实际上也会发送到s套接字,而不是udpClient.Client套接字。
同样,如果没有一个好的代码示例,就不可能知道您的服务器实际在做什么。但是,无论它的行为像一个正常的服务器,还是一些非标准的实现,你发布的代码不能可靠地从服务器接收响应。

你应该修复你的设计,这样你的客户端只创建一个套接字(例如一个初始的UdpClient实例)。您可以通过调用ReceiveAsync()来为通信做准备,并且只有在您这样做之后,从而确保您准备好接收响应,然后您可以发送数据。

确保您只创建这个对象。

还要注意客户端实现通常不绑定到特定的端口。相反,您可以让操作系统分配端口。只有服务器需要绑定到特定的端口,这样来自未知端点的入站请求就可以知道将请求发送到哪个端口。服务器的接收操作将包括客户端的端口号,因此服务器将知道将其响应发送到哪个端口,而无需客户端选择任何特殊的端口号。

最后,我强烈建议您学习现有的套接字编程教程,特别是Winsock Programmer's FAQ。这里没有任何材料可以直接应用于。net套接字API,但是您遇到的大多数问题都是所描述的。如果没有对套接字编程的基本理解,是不可能使用。net套接字API的。

如果上面没有让你朝着正确的方向前进,请确保你以后问的任何问题都包括一个好的代码示例,就像我上面提供的链接所描述的那样。请注意,对于任何网络问题,完整的代码示例都包括客户机和服务器。如果没有两者的实现,任何人都不可能完全理解你的问题,更不用说测试和修复你的代码示例。