SignalR从WPF服务器向WPF客户端发送消息

时间:2018-12-06 01:07:45

标签: .net signalr

我一直在研究一个示例,该示例在自托管WPF服务器应用程序中使用SignalR 2与WPF客户端进行通信。我已经下载了该项目-在下面显示该项目-一切正常:

WPF服务器:

    public partial class MainWindow : Window
{
    public IDisposable SignalR { get; set; }
    const string ServerURI = "http://localhost:8080";

    public MainWindow()
    {
        InitializeComponent();
    }

    /// <summary>
    /// Calls the StartServer method with Task.Run to not
    /// block the UI thread. 
    /// </summary>
    private void ButtonStart_Click(object sender, RoutedEventArgs e)
    {
        WriteToConsole("Starting server...");
        ButtonStart.IsEnabled = false;            
        Task.Run(() => StartServer());
    }

    /// <summary>
    /// Stops the server and closes the form. Restart functionality omitted
    /// for clarity.
    /// </summary>
    private void ButtonStop_Click(object sender, RoutedEventArgs e)
    {
        SignalR.Dispose();
        Close();
    }

    /// <summary>
    /// Starts the server and checks for error thrown when another server is already 
    /// running. This method is called asynchronously from Button_Start.
    /// </summary>
    private void StartServer()
    {
        try
        {
            SignalR = WebApp.Start(ServerURI);
        }
        catch (TargetInvocationException)
        {
            WriteToConsole("A server is already running at " + ServerURI);
            this.Dispatcher.Invoke(() => ButtonStart.IsEnabled = true);
            return;
        }
        this.Dispatcher.Invoke(() => ButtonStop.IsEnabled = true);
        WriteToConsole("Server started at " + ServerURI);
    }
    ///This method adds a line to the RichTextBoxConsole control, using Dispatcher.Invoke if used
    /// from a SignalR hub thread rather than the UI thread.
    public void WriteToConsole(String message)
    {
        if (!(RichTextBoxConsole.CheckAccess()))
        {
            this.Dispatcher.Invoke(() =>
                WriteToConsole(message)
            );
            return;
        }
        RichTextBoxConsole.AppendText(message + "\r");
    }
}
/// <summary>
/// Used by OWIN's startup process. 
/// </summary>
class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCors(CorsOptions.AllowAll);
        app.MapSignalR();
    }
}
/// <summary>
/// Echoes messages sent using the Send message by calling the
/// addMessage method on the client. Also reports to the console
/// when clients connect and disconnect.
/// </summary>
public class MyHub : Hub
{
    public void Send(string name, string message)
    {
        Clients.All.addMessage(name, message);
    }
    public override Task OnConnected()
    {
        //Use Application.Current.Dispatcher to access UI thread from outside the MainWindow class
        Application.Current.Dispatcher.Invoke(() => 
            ((MainWindow)Application.Current.MainWindow).WriteToConsole("Client connected: " + Context.ConnectionId));

        return base.OnConnected();
    }
    public override Task OnDisconnected()
    {
        //Use Application.Current.Dispatcher to access UI thread from outside the MainWindow class
        Application.Current.Dispatcher.Invoke(() => 
            ((MainWindow)Application.Current.MainWindow).WriteToConsole("Client disconnected: " + Context.ConnectionId));

        return base.OnDisconnected();
    }

}

这是WPF客户端:

    public partial class MainWindow : Window
{
    /// <summary>
    /// This name is simply added to sent messages to identify the user; this 
    /// sample does not include authentication.
    /// </summary>
    public String UserName { get; set; }
    public IHubProxy HubProxy { get; set; }
    const string ServerURI = "http://localhost:8080/signalr";
    public HubConnection Connection { get; set; }

    public MainWindow()
    {
        InitializeComponent();
    }

    private void ButtonSend_Click(object sender, RoutedEventArgs e)
    {
        HubProxy.Invoke("Send", UserName, TextBoxMessage.Text);
        TextBoxMessage.Text = String.Empty;
        TextBoxMessage.Focus();
    }

    /// <summary>
    /// Creates and connects the hub connection and hub proxy. This method
    /// is called asynchronously from SignInButton_Click.
    /// </summary>
    private async void ConnectAsync()
    {
        Connection = new HubConnection(ServerURI);
        Connection.Closed += Connection_Closed;
        HubProxy = Connection.CreateHubProxy("MyHub");
        //Handle incoming event from server: use Invoke to write to console from SignalR's thread
        HubProxy.On<string, string>("AddMessage", (name, message) =>
            this.Dispatcher.Invoke(() =>
                RichTextBoxConsole.AppendText(String.Format("{0}: {1}\r", name, message))
            )
        );
        try
        {
            await Connection.Start();
        }
        catch (HttpRequestException)
        {
            StatusText.Content = "Unable to connect to server: Start server before connecting clients.";
            //No connection: Don't enable Send button or show chat UI
            return;
        }

        //Show chat UI; hide login UI
        SignInPanel.Visibility = Visibility.Collapsed;
        ChatPanel.Visibility = Visibility.Visible;
        ButtonSend.IsEnabled = true;
        TextBoxMessage.Focus();
        RichTextBoxConsole.AppendText("Connected to server at " + ServerURI + "\r");
    }

    /// <summary>
    /// If the server is stopped, the connection will time out after 30 seconds (default), and the 
    /// Closed event will fire.
    /// </summary>
    void Connection_Closed()
    {
        //Hide chat UI; show login UI
        var dispatcher = Application.Current.Dispatcher;
        dispatcher.Invoke(() => ChatPanel.Visibility = Visibility.Collapsed);
        dispatcher.Invoke(() => ButtonSend.IsEnabled = false);
        dispatcher.Invoke(() => StatusText.Content = "You have been disconnected.");
        dispatcher.Invoke(() => SignInPanel.Visibility = Visibility.Visible);
    }

    private void SignInButton_Click(object sender, RoutedEventArgs e)
    {
        UserName = UserNameTextBox.Text;
        //Connect to server (use async method to avoid blocking UI thread)
        if (!String.IsNullOrEmpty(UserName))
        {     
            StatusText.Visibility = Visibility.Visible;
            StatusText.Content = "Connecting to server...";
            ConnectAsync();
        }
    }

    private void WPFClient_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        if (Connection != null)
        {
            Connection.Stop();
            Connection.Dispose();
        }
    }  
}

让我感到困惑的是服务器MyHub类中的“发送”方法,该方法将文本推送到所有连接的客户端。在该方法中,似乎在所有客户端上都调用了一个名为“ addMessage(String,String)”的方法,但是该方法未在客户端代码中的任何地方声明。这是怎么回事?

1 个答案:

答案 0 :(得分:0)

在客户端代码中通过以下方式声明addMessage(String,String)方法:

HubProxy.On<string, string>("AddMessage", (name, message) =>
        this.Dispatcher.Invoke(() =>
            RichTextBoxConsole.AppendText(String.Format("{0}: {1}\r", name, message))
        )
    );

按照https://docs.microsoft.com/en-us/previous-versions/aspnet/jj917974%28v%3dvs.100%29

该方法是如下操作委托:

https://docs.microsoft.com/en-us/dotnet/api/system.action-2?&view=netframework-4.8