如何在Visual C#中执行其他操作时正确侦听UDP数据包#

时间:2015-08-02 19:29:06

标签: c#

好吧,所以我对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);
                    }
                }
            }
        });
    }

1 个答案:

答案 0 :(得分:2)

你可以放心地忽略Blindy的建议。 UdpClient.ReceiveAsync()方法是专门围绕Task范例设计的,并且比专用线程接收数据更有效。由于Task是等待的,因此将ReceiveAsync()方法与GUI程序(例如Winforms或WPF)集成也更容易。也就是说,对于所有工作,您希望在UI线程中执行ReceiveAsync()调用,而不是在另一个Task中执行。

不幸的是,由于缺少a good, minimal, complete code example清楚地说明了您的问题,因此无法确定这看起来如何。但它最有可能涉及使UDPListener()方法成为async方法并从UI线程调用它。此外,由于您说该方法无法访问非静态成员,因此明显的解决方案是使该方法本身是非静态的。 E.g:

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。即使您在调用udpClient之前创建了s.SendTo()对象,实际上也会将回复发送到s套接字,而不是udpClient.Client套接字。
  3. 同样,如果没有一个好的代码示例,就无法知道服务器实际上做了什么。但无论它的行为是普通服务器还是非标准实现,您发布的代码都无法可靠地从服务器接收响应。

    您应该修改您的设计,以便您的客户端只创建一个套接字(例如,一个初始UdpClient实例)。您可以通过致电ReceiveAsync()来准备沟通,只有在您完成后才能做好准备,从而确保您已准备好接收回复,然后您就可以发送数据。

    确保您只创建 此单个对象。

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

    最后,我强烈建议您学习现有的套接字编程教程,尤其是Winsock Programmer's FAQ。这些材料都没有直接适用于.NET套接字API,但是您遇到的大多数问题都是完全那里记录的事情。如果没有对套接字编程的基本理解,通常无法使用.NET套接字API。

    如果上述内容无法让您朝着正确的方向前进,请确保您提出的任何未来问题都包含一个良好的代码示例,如上面提供的链接所述。请注意,对于任何网络问题,完整的代码示例包括两者客户端和服务器。任何人都无法完全理解您的问题,更不用说测试和修复您的代码示例,而不需要两者的实现。