c# - UWP 和 WPF 之间的 (UDP) 通信

标签 c# wpf uwp udp communication

我有两个应用程序(一个 UWP 和一个 WPF)在同一台机器上运行,它们应该按如下方式进行通信:
WPF 应用程序从 USB 端口读取数据并将数据发送到 UWP。
UWP 应用程序获取数据并使用它。

现在,我一直在尝试使用 UDP 作为通信 channel 来实现这一点,但 UWP 应用程序没有收到任何内容。
如果我创建两个 UWP 应用程序,它们可以毫无问题地通过 UDP 进行通信。不幸的是,我需要 WPF 一个!

我很确定这是一个非常常见的场景,应该有一个简单的解决方案,但我一直在寻找几天没有找到解决方案。

顺便说一句,我找到了 similar question ,但它是关于 C++ 的,而我需要一个 C# 解决方案。

编辑:

这是一些最低限度的运行示例,也许这有助于阐明这个问题。

UWP 监听器

using System;
using System.Diagnostics;
using System.IO;
using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace UDP1_UWP
{
    public sealed partial class DatagramSocketPage : Page
    {
        public DatagramSocketPage()
        {
            this.InitializeComponent();
        }

        static string ClientPortNumber = "1336";
        static string ServerPortNumber = "1337";

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            this.StartServer();
        }

        private async void StartServer()
        {
            try
            {
                var serverDatagramSocket = new Windows.Networking.Sockets.DatagramSocket();


                serverDatagramSocket.MessageReceived += ServerDatagramSocket_MessageReceived;

                this.serverListBox.Items.Add("server is about to bind...");


                await serverDatagramSocket.BindServiceNameAsync(DatagramSocketPage.ServerPortNumber);

                this.serverListBox.Items.Add(string.Format("server is bound to port number {0}", DatagramSocketPage.ServerPortNumber));
            }
            catch (Exception ex)
            {
                Windows.Networking.Sockets.SocketErrorStatus webErrorStatus = Windows.Networking.Sockets.SocketError.GetStatus(ex.GetBaseException().HResult);
                this.serverListBox.Items.Add(webErrorStatus.ToString() != "Unknown" ? webErrorStatus.ToString() : ex.Message);
            }
        }

        private async void ServerDatagramSocket_MessageReceived(Windows.Networking.Sockets.DatagramSocket sender, Windows.Networking.Sockets.DatagramSocketMessageReceivedEventArgs args)
        {
            string request;
            using (DataReader dataReader = args.GetDataReader())
            {
                request = dataReader.ReadString(dataReader.UnconsumedBufferLength).Trim();
            }

            await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => this.serverListBox.Items.Add(string.Format("server received the request: \"{0}\"", request)));

            // Echo the request back as the response.
            using (Stream outputStream = (await sender.GetOutputStreamAsync(args.RemoteAddress, DatagramSocketPage.ClientPortNumber)).AsStreamForWrite())
            {
                using (var streamWriter = new StreamWriter(outputStream))
                {
                    await streamWriter.WriteLineAsync(request);
                    await streamWriter.FlushAsync();
                }
            }

            await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => this.serverListBox.Items.Add(string.Format("server sent back the response: \"{0}\"", request)));


        }

    }
}

WPF 发件人
我尝试使用 WinRT 库,以便两个应用程序使用相同的类。为此,我
  • 创建了一个空的 WPF 项目
  • 添加了对 C:\Program Files (x86)\Windows 的引用
    Kits\10\UnionMetadata\10.0.17763.0\Windows.winmd 和 C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5\System.Runtime.WindowsRuntime.dll

  • 现在我可以在我的 WPF 应用程序中使用以下命名空间:
  • 使用 Windows.Networking;
  • 使用 Windows.Networking.Sockets;
  • 使用 Windows.Storage.Streams;

  • 这是代码:

    程序.cs
    using System;
    using XSensUDPSender.UDP;
    
    namespace XSensUDPServer
    {
        class Program
        {
    
            static void Main(string[] args)
            {
                UDPClient c = new UDPClient();
                c.StartClientAsync();
    
                string text;
                do
                {
                    Console.WriteLine("Message to send: ");
                    text = Console.ReadLine();
                    c.SendMessageAsync(text);
                } while (text != "quit");
    
            }
        }
    }
    

    UDPClient.cs
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Threading.Tasks;
    using Windows.Networking;
    using Windows.Networking.Sockets;
    using Windows.Storage.Streams;
    
    namespace XSensUDPSender.UDP
    {
        class UDPClient
        {
            private DatagramSocket clientDatagramSocket;
            private HostName hostName;
            private string ClientPortNumber = "1336";
            private string ServerPortNumber = "1337";
    
            public UDPClient(string aClientPort = "1336", string aServerPort = "1337")
            {
                ClientPortNumber = aClientPort;
                ServerPortNumber = aServerPort;
            }
    
            public async void StartClientAsync()
            {
                try
                {
                    // Create the DatagramSocket and establish a connection to the echo server.
                    clientDatagramSocket = new Windows.Networking.Sockets.DatagramSocket();
    
                    clientDatagramSocket.MessageReceived += ClientDatagramSocket_MessageReceived;
    
                    // The server hostname that we will be establishing a connection to. In this example, the server and client are in the same process.
                    hostName = new Windows.Networking.HostName("localhost");
    
                    Console.WriteLine("CLIENT is about to bind...");
                    await clientDatagramSocket.BindServiceNameAsync(ClientPortNumber);
                    Console.WriteLine(string.Format("CLIENT is bound to port number {0}", ClientPortNumber));
    
    
                }
                catch (Exception ex)
                {
                    Windows.Networking.Sockets.SocketErrorStatus webErrorStatus = Windows.Networking.Sockets.SocketError.GetStatus(ex.GetBaseException().HResult);
                    Debug.WriteLine(webErrorStatus.ToString() != "Unknown" ? webErrorStatus.ToString() : ex.Message);
                }
            }
    
            private void ClientDatagramSocket_MessageReceived(Windows.Networking.Sockets.DatagramSocket sender, Windows.Networking.Sockets.DatagramSocketMessageReceivedEventArgs args)
            {
                string response;
                using (DataReader dataReader = args.GetDataReader())
                {
                    response = dataReader.ReadString(dataReader.UnconsumedBufferLength).Trim();
                    Console.WriteLine("CLIENT RECEIVED: " + response);
                }
    
            }
    
    
            public async Task SendMessageAsync(string request)
            {
                // Send a request to the echo server.
                using (var serverDatagramSocket = new Windows.Networking.Sockets.DatagramSocket())
                {
                    using (Stream outputStream = (await serverDatagramSocket.GetOutputStreamAsync(hostName, ServerPortNumber)).AsStreamForWrite())
                    {
                        using (var streamWriter = new StreamWriter(outputStream))
                        {
                            await streamWriter.WriteLineAsync(request);
                            await streamWriter.FlushAsync();
                        }
                    }
                }
    
    
            }
    
        }
    }
    

    最佳答案

    @XavierXie-MSFT 不是重复的。实际上我自己找到了答案:

    As a consequence of network isolation, Windows disallows establishing a socket connection (Sockets or WinSock) between two UWP apps running on the same machine; whether that's via the local loopback address (127.0.0.0), or by explicitly specifying the local IP address. For details about mechanisms by which UWP apps can communicate with one another, see App-to-app communication.



    来源:https://docs.microsoft.com/en-us/windows/uwp/networking/sockets

    因此,显然,这是一个“功能”而不是错误。虽然,恕我直言,微软再次出手了。他们怎么会想到阻止本地主机请求!???

    无论如何,为了解决这个问题,我找到了一个不同的解决方案:我正在使用 App Services
    (查看 https://docs.microsoft.com/en-us/windows/uwp/launch-resume/how-to-create-and-consume-an-app-service )。
    所以,现在我有了包含后台应用服务的 UWP 应用。

    整体基础架构可以想象如下:
    UWP <--> 后台应用服务 <--> WPF

    我受到了启发:https://csharp.christiannagel.com/2018/10/09/desktopbridge/

    UWP 应用将 AppServiceConnection 实例化到后台服务。
    WPF 应用程序还将 AppServiceConnection 实例化到后台服务。
    后台服务有两个 AppServiceConnection:一个绑定(bind)到 UWP 应用,另一个绑定(bind)到 WPF 应用。

    当 UWP (resp. WPF) 想要与 WPF (resp. UWP) 进行通信时,它会向后台服务发送消息。后台服务将消息转发到 WPF (resp. UWP),获取响应并将响应转发回 UWP (resp. WPF)。

    不过,现在我面临一个新问题:WPF 的 AppServiceConnection 在成功向 UWP 发送 226 条消息后被关闭。然而,WPF 不断接收来自 UWP 的消息。
    也许这篇文章可以帮助我解决这个新问题(我会及时通知你):
    UWP AppServiceConnection - SendResponseAsync returns AppServiceResponseStatus.Failure

    关于c# - UWP 和 WPF 之间的 (UDP) 通信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55276958/

    相关文章:

    c# - UWP 等待用户与 ContentDialog 交互

    c# - Entity Framework Fluent API 映射问题

    WPF-获取内存中UIElement的大小?

    WPF - 阻止列表框项目选择

    c# - Windows Phone 8 应用程序中的 ListView

    c++ - 如何在不使用 UWP 的情况下在 C++ 中使用命名空间 Windows.Gaming.Input?

    c# - 添加 UWP 后台任务导致 AppManifest 错误

    c# - 连接字符串加密,什么意思?

    c# - 使用条件断点时,Visual Studio 会减慢执行速度

    c# - 有没有一种简单的方法可以让 ClassLibrary 获取 session 值?