搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序

原文地址(英文):http://www.networkcomms.net/creating-a-wpf-chat-client-server-application/

注意:本教程是相当广泛的,如果你是在短请也看到我们的东西 开始如何在几分钟内创建一个客户端服务器应用程序教程。

注2:本例中包括,明显延长进一步证明功能,在包中包含的示例 包下载

在我们开始之前确保您已经安装了Visual Studio 2010中表达或晚,这应该有 .net4.0 或更高版本。

1。 创建Visual Studio项目

  • 创建一个新的包含visual c# visual studio解决方案的 WPF应用程序 “项目命名它” WPFChatExample
  • 右键单击项目刚刚创建,选择“ 属性 ”。 确保的 目标框架 “是” .net4.0“而不是” 。 .net4.0客户端配置文件 ”。 你现在应该有这样的。

新鲜的visual studio创建应用程序命名为“WPFChatExample”

2。 添加NetworkComms.net DLL项目

  • NetworkComms.Net 下载包包含DLL在所有支持的平台上,但我们感兴趣的只是.net4.0>>发布完整的DLL。 这个DLL复制到相同的位置,我们在步骤1中创建的解决方案。
  • 我们现在需要添加一个项目引用NetworkComms。 净DLL我们只是补充道。 右键单击“ WPFChatExample “项目并选择” 添加引用… ”。 在打开的窗口中选择Browse选项卡并选择我们刚刚添加的DLL。
  • 如果你扩大 引用 文件夹内的项目你现在应该看到NetworkComms。 净参考你就像这样:

“WPFChatExample”WPF应用程序包含一个引用NetworkComms完成.net DLL。

 

3所示。 添加WPF元素

  • 我们需要添加文本框和按钮,我们打算与WPF布局。 双击“开始 MainWindow.xaml 的文件,主要查看器中打开:

开放的主窗口。 xaml文件显示‘设计’和‘xaml”。

 
  • 如果你想,你可以现在添加每个文本框和按钮。 为了节省时间但是我们提供了一个基础布局,您可以复制和粘贴。 复制并粘贴以下代码来替代所有现有的代码在XAML视图 MainWindow.xaml ”:
c# 
  1. <Window x:Class="WPFChatExample.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. Title="NetworkComms .Net WPF Chat Example" Height="" Width="" Background="#FF7CA0FF" ResizeMode="CanMinimize">
  5. <Grid>
  6. <TextBox Height="" HorizontalAlignment="Left" Margin="68,8,0,0" Name="serverIP" VerticalAlignment="Top" Width="" />
  7. <Label Content="Server IP:" Height="" HorizontalAlignment="Left" Margin="8,6,0,0" Name="label1" VerticalAlignment="Top" />
  8. <TextBox Height="" HorizontalAlignment="Left" Margin="199,8,0,0" Name="serverPort" VerticalAlignment="Top" Width="" />
  9. <Label Content="Port:" Height="" HorizontalAlignment="Left" Margin="166,6,0,0" Name="label2" VerticalAlignment="Top" />
  10. <TextBox Height="" HorizontalAlignment="Left" Margin="11,38,0,0" Name="chatBox" VerticalAlignment="Top" Width="" IsReadOnly="True" VerticalScrollBarVisibility="Visible" />
  11. <Label Content="Messages from:" Height="" HorizontalAlignment="Left" Margin="369,84,0,0" Name="label3" VerticalAlignment="Top" Width="" />
  12. <TextBox Height="" HorizontalAlignment="Left" Margin="373,108,0,0" Name="messagesFrom" VerticalAlignment="Top" Width="" IsReadOnly="True" VerticalScrollBarVisibility="Auto" />
  13. <Label Content="Local Name:" Height="" HorizontalAlignment="Left" Margin="293,7,0,0" Name="label4" VerticalAlignment="Top" />
  14. <TextBox Height="" HorizontalAlignment="Left" Margin="373,8,0,0" Name="localName" VerticalAlignment="Top" Width="" />
  15. <Label Content="Message:" Height="" HorizontalAlignment="Left" Margin="5,272,0,0" Name="label5" VerticalAlignment="Top" />
  16. <TextBox Height="" HorizontalAlignment="Left" Margin="62,274,0,0" Name="messageText" VerticalAlignment="Top" Width="" />
  17. <Button Content="Send" Height="" HorizontalAlignment="Left" Margin="373,274,0,0" Name="sendMessageButton" VerticalAlignment="Top" Width=""/>
  18. <CheckBox Content="Enable Server" Height="" HorizontalAlignment="Left" Margin="377,44,0,0" x:Name="enableServer" VerticalAlignment="Top"/>
  19. <CheckBox Content="Use Encryption" Height="" HorizontalAlignment="Left" Margin="377,65,0,0" Name="useEncryptionBox" VerticalAlignment="Top"/>
  20. </Grid>
  21. </Window>
  • 设计窗口现在应该显示相当于你刚从上面贴的XAML。 这给了我们的基本布局聊天应用程序:

后复制粘贴的例子xaml代码设计窗口现在应该显示的基本布局。

 
  • 媒体对你的键盘的F5′,确保项目成功构建(即错误列表窗口在Visual studio仍然是空的)。 如果项目不建立在这一点上请回去在本教程中,确保您已经完成了所有的必要步骤。 如果项目构建你现在应该看到WPF应用程序,当然,我们仍然需要添加的所有功能。

WPF聊天应用程序的例子。 所有的布局元素添加了但是没有任何功能。

 

4所示。 添加ChatMessage包装类

  • 下一步是创建一个包装器类的消息我们将发送和接收,即一个对象我们发送和接收包含所有必要的信息。 右键单击该项目并选择“ 添加 “>” 新项目… ”。 这应该引出的 添加新项 窗口中,一个选项列表,你可以添加到项目中。 确保的 ”项被选中时,在窗口的底部输入名称” ChatMessage.cs ”。 现在点击“ 添加 ”。 新的类文件应该自动打开,你现在应该是这样的:

这个新类,名为“ChatMessage.cs”。 这将是用作聊天信息的包装器。

 
  • 复制并粘贴以下代码,取代现有的所有代码在我们刚刚创建的类,“ ChatMessage.cs ”:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. //我们需要包括以下三本类的命名空间
    using NetworkCommsDotNet;
  7. using NetworkCommsDotNet.Tools;
  8. using ProtoBuf;
  9.  
  10. namespace WPFChatExample
  11. {
  12. /// <summary>
  13. /// A wrapper class for the messages that we intend to send and receive.
  14. /// The [ProtoContract] attribute informs NetworkComms .Net that we intend to
  15. /// serialise(连载) (turn into bytes) this object. At the base level the
  16. /// serialisation(连载) is performed by protobuf.net.
  17. /// </summary>
  18. [ProtoContract]
  19. class ChatMessage
  20. {
  21. /// <summary>
  22. /// chatmessage标识源.
  23. /// We use this variable as the constructor for the ShortGuid.
  24. /// The [ProtoMember(1)] attribute informs the serialiser that when
  25. /// an object of type ChatMessage is serialised we want to include this variable
  26. /// </summary>
  27. [ProtoMember()]
  28. string _sourceIdentifier;
  29.  
  30. /// <summary>
  31. /// The source identifier is accessible as a ShortGuid
  32. /// </summary>
  33. public ShortGuid SourceIdentifier { get { return new ShortGuid(_sourceIdentifier); } }
  34.  
  35. /// <summary>
  36. /// The name of the source of this ChatMessage.
  37. /// We use shorthand declaration, get and set.
  38. /// The [ProtoMember(2)] attribute informs the serialiser that when
  39. /// an object of type ChatMessage is serialised we want to include this variable
  40. /// </summary>
  41. [ProtoMember()]
  42. public string SourceName { get; private set; }
  43.  
  44. /// <summary>
  45. /// The actual message.
  46. /// </summary>
  47. [ProtoMember()]
  48. public string Message { get; private set; }
  49.  
  50. /// <summary>
  51. /// The index of this message. Every message sent by a particular source
  52. /// has an incrementing(增值) index.
  53. /// </summary>
  54. [ProtoMember()]
  55. public long MessageIndex { get; private set; }
  56.  
  57. /// <summary>
  58. /// The number of times this message has been relayed.
  59. /// </summary>
  60. [ProtoMember()]
  61. public int RelayCount { get; private set; }
  62.  
  63. /// <summary>
  64. /// We must include a private constructor to be used by the deserialisation step.
  65. /// </summary>
  66. private ChatMessage() { }
  67.  
  68. /// <summary>
  69. /// Create a new ChatMessage
  70. /// </summary>
  71. /// <param name="sourceIdentifier">The source identifier</param>
  72. /// <param name="sourceName">The source name</param>
  73. /// <param name="message">The message to be sent</param>
  74. /// <param name="messageIndex">The index of this message</param>
  75. public ChatMessage(ShortGuid sourceIdentifier, string sourceName, string message, long messageIndex)
  76. {
  77. this._sourceIdentifier = sourceIdentifier;
  78. this.SourceName = sourceName;
  79. this.Message = message;
  80. this.MessageIndex = messageIndex;
  81. this.RelayCount = ;
  82. }
  83.  
  84. /// <summary>
  85. /// Increment the relay count variable
  86. /// </summary>
  87. public void IncrementRelayCount()
  88. {
  89. RelayCount++;
  90. }
  91. }
  92. }

5。 将功能添加到代码元素MainWindow.xaml

  • 现在我们将注意力转向的代码元素 MainWindow.xaml ”。 右键单击访问代码元素的 MainWindow.xaml ”,选择“ 视图代码 从上下文菜单中。 您应该看到一个代码文件,其中包含之前,所有的代码我们随后要添加将在 主窗口 类:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Data;
  8. using System.Windows.Documents;
  9. using System.Windows.Input;
  10. using System.Windows.Media;
  11. using System.Windows.Media.Imaging;
  12. using System.Windows.Navigation;
  13. using System.Windows.Shapes;
  14.  
  15. namespace WPFChatExample
  16. {
  17. /// <summary>
  18. /// Interaction logic for MainWindow.xaml
  19. /// </summary>
  20. public partial class MainWindow : Window
  21. {
  22. public MainWindow()
  23. {
  24. InitializeComponent();
  25. }
  26. }
  27. }

MainWindow.xaml

  • 因为我们要执行网络任务在这门课中,我们首先需要添加相关的名称空间引用。 下面所有的 使用系统… “您想要添加名称空间引用:
  1. //We need to include the following namespaces
  2. using System.Net;
  3. using NetworkCommsDotNet;
  4. using NetworkCommsDotNet.DPSBase;
  5. using NetworkCommsDotNet.Tools;
  6. using NetworkCommsDotNet.Connections;
  7. using NetworkCommsDotNet.Connections.TCP;

 
  • 接下来我们要添加一些类变量来帮助我们跟踪当前应用程序状态。 我们想跟踪:
  1. 我们已经收到最新消息。
  2. 最大数量的时候我们将传递一个信息。
  3. 一个可选的加密密钥。
  4. 本地索引时我们将使用发送新消息。
  • 跟踪这些项目添加以下代码的类:
  1. #region Private Fields
  2. /// <summary>
  3. /// Dictionary to keep track of which peer messages have already been written to the chat window
  4. /// </summary>
  5. Dictionary<ShortGuid, ChatMessage> lastPeerMessageDict = new Dictionary<ShortGuid, ChatMessage>();
  6.  
  7. /// <summary>
  8. /// The maximum number of times a chat message will be relayed
  9. /// </summary>
  10. int relayMaximum = ;
  11.  
  12. /// <summary>
  13. /// An optional encryption key to use should one be required.
  14. /// This can be changed freely but must obviously be the same
  15. /// for both sender and receiver.
  16. /// </summary>
  17. string encryptionKey = "ljlhjf8uyfln23490jf;m21-=scm20--iflmk;";
  18.  
  19. /// <summary>
  20. /// A local counter used to track the number of messages sent from
  21. /// this instance.
  22. /// </summary>
  23. long messageSendIndex = ;
  24. #endregion
  • 接下来,我们将添加的方法使用WPF GUI。 前两个方法可用于从任何线程更新聊天和MessageFrom文本框:
  1. /// <summary>
  2. /// Append the provided message to the chatBox text box.
  3. /// </summary>
  4. /// <param name="message"></param>
  5. private void AppendLineToChatBox(string message)
  6. {
  7. //To ensure we can successfully append to the text box from any thread
  8. //we need to wrap the append within an invoke action.
  9. chatBox.Dispatcher.BeginInvoke(new Action<string>((messageToAdd) =>
  10. {
  11. chatBox.AppendText(messageToAdd + "\n");
  12. chatBox.ScrollToEnd();
  13. }), new object[] { message });
  14. }
  15.  
  16. /// <summary>
  17. /// Refresh the messagesFrom text box using the recent message history.
  18. /// </summary>
  19. private void RefreshMessagesFromBox()
  20. {
  21. //We will perform a lock here to ensure the text box is only
  22. //updated one thread at time
  23. lock (lastPeerMessageDict)
  24. {
  25. //Use a linq expression to extract an array of all current users from lastPeerMessageDict
  26. string[] currentUsers = (from current in lastPeerMessageDict.Values orderby current.SourceName select current.SourceName).ToArray();
  27.  
  28. //To ensure we can successfully append to the text box from any thread
  29. //we need to wrap the append within an invoke action.
  30. this.messagesFrom.Dispatcher.BeginInvoke(new Action<string[]>((users) =>
  31. {
  32. //First clear the text box
  33. messagesFrom.Text = "";
  34.  
  35. //Now write out each username
  36. foreach (var username in users)
  37. messagesFrom.AppendText(username + "\n");
  38. }), new object[] { currentUsers });
  39. }
  40. }
  • 接下来下有五个方法将被附加到WPF元素布局在步骤6。 他们将被用来发送消息,切换加密,本地服务器模式开关并正确地关闭一切当我们完成了对应用程序:
  1. /// <summary>
  2. /// Send any entered message when we click the send button.
  3. /// </summary>
  4. /// <param name="sender"></param>
  5. /// <param name="e"></param>
  6. private void SendMessageButton_Click(object sender, RoutedEventArgs e)
  7. {
  8. SendMessage();
  9. }
  10.  
  11. /// <summary>
  12. /// Send any entered message when we press enter or return
  13. /// </summary>
  14. /// <param name="sender"></param>
  15. /// <param name="e"></param>
  16. private void MessageText_KeyUp(object sender, KeyEventArgs e)
  17. {
  18. if (e.Key == Key.Enter || e.Key == Key.Return)
  19. SendMessage();
  20. }
  21.  
  22. /// <summary>
  23. /// Toggle encryption
  24. /// </summary>
  25. /// <param name="sender"></param>
  26. /// <param name="e"></param>
  27. private void UseEncryptionBox_CheckedToggle(object sender, RoutedEventArgs e)
  28. {
  29. if (useEncryptionBox.IsChecked != null && (bool)useEncryptionBox.IsChecked)
  30. {
  31. RijndaelPSKEncrypter.AddPasswordToOptions(NetworkComms.DefaultSendReceiveOptions.Options, encryptionKey);
  32. NetworkComms.DefaultSendReceiveOptions.DataProcessors.Add(DPSManager.GetDataProcessor<RijndaelPSKEncrypter>());
  33. }
  34. else
  35. NetworkComms.DefaultSendReceiveOptions.DataProcessors.Remove(DPSManager.GetDataProcessor<RijndaelPSKEncrypter>());
  36. }
  37.  
  38. /// <summary>
  39. /// Correctly shutdown NetworkComms .Net when closing the WPF application
  40. /// </summary>
  41. /// <param name="sender"></param>
  42. /// <param name="e"></param>
  43. private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
  44. {
  45. //Ensure we shutdown comms when we are finished
  46. NetworkComms.Shutdown();
  47. }
  48.  
  49. /// <summary>
  50. /// Toggle whether the local application is acting as a server
  51. /// </summary>
  52. /// <param name="sender"></param>
  53. /// <param name="e"></param>
  54. private void EnableServer_Toggle(object sender, RoutedEventArgs e)
  55. {
  56. //Enable or disable the local server mode depending on the checkbox IsChecked value
  57. if (enableServer.IsChecked != null && (bool)enableServer.IsChecked)
  58. ToggleServerMode(true);
  59. else
  60. ToggleServerMode(false);
  61. }
  • 接下来我们添加的方法可以用来切换应用程序的本地服务器模式:
  1. /// <summary>
  2. /// Wrap the functionality required to enable/disable the local application server mode
  3. /// </summary>
  4. /// <param name="enableServer"></param>
  5. private void ToggleServerMode(bool enableServer)
  6. {
  7. if (enableServer)
  8. {
  9. //Start listening for new incoming TCP connections
  10. //Parameters ensure we listen across all adaptors using a random port
  11. Connection.StartListening(ConnectionType.TCP, new IPEndPoint(IPAddress.Any, ));
  12.  
  13. //Write the IP addresses and ports that we are listening on to the chatBox
  14. chatBox.AppendText("Listening for incoming TCP connections on:\n");
  15. foreach (IPEndPoint listenEndPoint in Connection.ExistingLocalListenEndPoints(ConnectionType.TCP))
  16. chatBox.AppendText(listenEndPoint.Address + ":" + listenEndPoint.Port + "\n");
  17. }
  18. else
  19. {
  20. NetworkComms.Shutdown();
  21. chatBox.AppendText("Server disabled. No longer accepting connections and all existing connections have been closed.");
  22. }
  23. }
 
  • 接下来,我们将创建一个方法,可以通过NetworkComms执行。 网络聊天消息时已经收到。 在此方法中,我们可以把任何我们想做的但是因为我们正在聊天应用程序可能希望的方法:
  1. 打印消息ChatBox文本框。
  2. 从文本框更新消息。
  3. 传递消息给其他同行。
  • 可以执行这些功能的方法如下:
  1. /// <summary>
  2. /// Performs whatever functions we might so desire when we receive an incoming ChatMessage
  3. /// </summary>
  4. /// <param name="header">The PacketHeader corresponding with the received object</param>
  5. /// <param name="connection">The Connection from which this object was received</param>
  6. /// <param name="incomingMessage">The incoming ChatMessage we are after</param>
  7. private void HandleIncomingChatMessage(PacketHeader header, Connection connection, ChatMessage incomingMessage)
  8. {
  9. //We only want to write a message once to the chat window
  10. //Because we allow relaying and may receive the same message twice
  11. //we use our history and message indexes to ensure we have a new message
  12. lock (lastPeerMessageDict)
  13. {
  14. if (lastPeerMessageDict.ContainsKey(incomingMessage.SourceIdentifier))
  15. {
  16. if (lastPeerMessageDict[incomingMessage.SourceIdentifier].MessageIndex < incomingMessage.MessageIndex)
  17. {
  18. //If this message index is greater than the last seen from this source we can safely
  19. //write the message to the ChatBox
  20. AppendLineToChatBox(incomingMessage.SourceName + " - " + incomingMessage.Message);
  21.  
  22. //We now replace the last received message with the current one
  23. lastPeerMessageDict[incomingMessage.SourceIdentifier] = incomingMessage;
  24. }
  25. }
  26. else
  27. {
  28. //If we have never had a message from this source before then it has to be new
  29. //by definition
  30. lastPeerMessageDict.Add(incomingMessage.SourceIdentifier, incomingMessage);
  31. AppendLineToChatBox(incomingMessage.SourceName + " - " + incomingMessage.Message);
  32. }
  33. }
  34.  
  35. //Once we have written to the ChatBox we refresh the MessagesFromWindow
  36. RefreshMessagesFromBox();
  37.  
  38. //This last section of the method is the relay function
  39. //We start by checking to see if this message has already been relayed
  40. //the maximum number of times
  41. if (incomingMessage.RelayCount < relayMaximum)
  42. {
  43. //If we are going to relay this message we need an array of
  44. //all other known connections
  45. var allRelayConnections = (from current in NetworkComms.GetExistingConnection() where current != connection select current).ToArray();
  46.  
  47. //We increment(增量) the relay count before we send
  48. incomingMessage.IncrementRelayCount();
  49.  
  50. //We will now send the message to every other connection
  51. foreach (var relayConnection in allRelayConnections)
  52. {
  53. //We ensure we perform the send within a try catch
  54. //To ensure a single failed send will not prevent the
  55. //relay to all working connections.
  56. try { relayConnection.SendObject("ChatMessage", incomingMessage); }
  57. catch (CommsException) { /* Catch the comms exception, ignore and continue */ }
  58. }
  59. }
  60. }
  • NetworkComms。 网有一个广泛的功能和使用情况。 其中一个允许您执行代码每次连接断开连接。 在这个例子中,我们将创建一个方法写ChatBox断开的消息。 方法如下:
  1. /// <summary>
  2. /// Performs whatever functions we might so desire when an existing connection is closed.
  3. /// </summary>
  4. /// <param name="connection">The closed connection</param>
  5. private void HandleConnectionClosed(Connection connection)
  6. {
  7. //We are going to write a message to the ChatBox when a user disconnects
  8. //We perform the following within a lock so that threads proceed one at a time
  9. lock (lastPeerMessageDict)
  10. {
  11. //Extract the remoteIdentifier from the closed connection
  12. ShortGuid remoteIdentifier = connection.ConnectionInfo.NetworkIdentifier;
  13.  
  14. //If at some point we received a message with this identifier we can
  15. //include the source name in the disconnection message.
  16. if (lastPeerMessageDict.ContainsKey(remoteIdentifier))
  17. AppendLineToChatBox("Connection with '" + lastPeerMessageDict[remoteIdentifier].SourceName + "' has been closed.");
  18. else
  19. AppendLineToChatBox("Connection with '" + connection.ToString() + "' has been closed.");
  20.  
  21. //Last thing is to remove this entry from our message history
  22. lastPeerMessageDict.Remove(connection.ConnectionInfo.NetworkIdentifier);
  23. }
  24.  
  25. //Refresh the messages from box to reflect this disconnection
  26. RefreshMessagesFromBox();
  27. }

下一个方法将用于发送任何消息,我们创建:

  1. /// <summary>
  2. /// Send our message.
  3. /// </summary>
  4. private void SendMessage()
  5. {
  6. //If we have tried to send a zero length string we just return
  7. if (messageText.Text.Trim() == "") return;
  8.  
  9. //We may or may not have entered some server connection information
  10. ConnectionInfo serverConnectionInfo = null;
  11. if (serverIP.Text != "")
  12. {
  13. try { serverConnectionInfo = new ConnectionInfo(serverIP.Text.Trim(), int.Parse(serverPort.Text)); }
  14. catch (Exception)
  15. {
  16. MessageBox.Show("Failed to parse the server IP and port. Please ensure it is correct and try again", "Server IP & Port Parse Error", MessageBoxButton.OK);
  17. return;
  18. }
  19. }
  20.  
  21. //We wrap everything we want to send in the ChatMessage class we created
  22. ChatMessage messageToSend = new ChatMessage(NetworkComms.NetworkIdentifier, localName.Text, messageText.Text, messageSendIndex++);
  23.  
  24. //We add our own message to the message history in-case it gets relayed back to us
  25. lock (lastPeerMessageDict) lastPeerMessageDict[NetworkComms.NetworkIdentifier] = messageToSend;
  26.  
  27. //We write our own message to the chatBox
  28. AppendLineToChatBox(messageToSend.SourceName + " - " + messageToSend.Message);
  29.  
  30. //We refresh the MessagesFrom box so that it includes our own name
  31. RefreshMessagesFromBox();
  32.  
  33. //We clear the text within the messageText box.
  34. this.messageText.Text = "";
  35.  
  36. //If we provided server information we send to the server first
  37. if (serverConnectionInfo != null)
  38. {
  39. //We perform the send within a try catch to ensure the application continues to run if there is a problem.
  40. try { TCPConnection.GetConnection(serverConnectionInfo).SendObject("ChatMessage", messageToSend); }
  41. catch (CommsException) { MessageBox.Show("A CommsException occurred while trying to send message to " + serverConnectionInfo, "CommsException", MessageBoxButton.OK); }
  42. }
  43.  
  44. //If we have any other connections we now send the message to those as well
  45. //This ensures that if we are the server everyone who is connected to us gets our message
  46. var otherConnectionInfos = (from current in NetworkComms.AllConnectionInfo() where current != serverConnectionInfo select current).ToArray();
  47. foreach (ConnectionInfo info in otherConnectionInfos)
  48. {
  49. //We perform the send within a try catch to ensure the application continues to run if there is a problem.
  50. try { TCPConnection.GetConnection(info).SendObject("ChatMessage", messageToSend); }
  51. catch (CommsException) { MessageBox.Show("A CommsException occurred while trying to send message to " + info, "CommsException", MessageBoxButton.OK); }
  52. }
  53. }
  • 最后我们需要添加的代码元素内的 MainWindow.xaml NetworkComms。net的是正确的初始化。 为了正确地初始化NetworkComms。 Net我们需要:
  1. 我们的机器的主机名设置默认本地名称。
  2. 触发的方法 HandleIncomingMessage “当我们收到一包类型” ChatMessage ”。
  3. 触发的方法 HandleConnectionClosed 当一个现有的连接关闭。
  • 我们在主窗口类构造函数执行这些初始化任务,代之以下面的代码:
  1. public MainWindow()
  2. {
  3. InitializeComponent();
  4.  
  5. //Write the IP addresses and ports that we are listening on to the chatBox
  6. chatBox.AppendText("Initialised WPF chat example.");
  7.  
  8. //Add a blank line after the initialisation output
  9. chatBox.AppendText("\n");
  10.  
  11. //Set the default Local Name box using to the local host name
  12. localName.Text = HostInfo.HostName;
  13.  
  14. //Configure NetworkComms .Net to handle and incoming packet of type 'ChatMessage'
  15. //e.g. If we receive a packet of type 'ChatMessage' execute the method 'HandleIncomingChatMessage'
  16. NetworkComms.AppendGlobalIncomingPacketHandler<ChatMessage>("ChatMessage", HandleIncomingChatMessage);
  17.  
  18. //Configure NetworkComms .Net to perform an action when a connection is closed
  19. //e.g. When a connection is closed execute the method 'HandleConnectionClosed'
  20. NetworkComms.AppendGlobalConnectionCloseHandler(HandleConnectionClosed);
  21. }

6。 将事件添加到WPF布局

  • 应用程序的最后一步是添加必要的事件,这样按钮和文本框的布局可以用来发送消息。 这是通过编辑XAML的主窗口。 这是本教程的步骤3中相同的XAML编辑,但回顾一下,访问XAML通过双击 MainWindow.xaml 在解决方案资源管理器窗口中。
  • 当应用程序关闭Window_Closing我们想运行方法。 取代XAML的顶部部分目前是这样的:
  1. <Window x:Class="WPFChatExample.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. Title="NetworkComms .Net WPF Chat Example" Height="" Width="" Background="#FF7CA0FF" ResizeMode="CanMinimize">
 这个(注意添加关闭=“Window_Closing”结束时):

  1. <Window x:Class="WPFChatExample.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. Title="NetworkComms .Net WPF Chat Example" Height="" Width="" Background="#FF7CA0FF" ResizeMode="CanMinimize" Closing="Window_Closing">
  • 我们想要一个当我们点击“发送消息 发送 ”按钮。 我们通过更换连接:
  1. <Button Content="Send" Height="" HorizontalAlignment="Left" Margin="373,274,0,0" Name="sendMessageButton" VerticalAlignment="Top" Width="" />

这里以下排版不是很好,太晚了看花眼了,请参照原文(英文)

 
  • 我们希望用户能够输入后按回车或返回发送消息的消息框。 我们通过更换连接:
  1. <Button Content="Send" Height="" HorizontalAlignment="Left" Margin="373,274,0,0" Name="sendMessageButton" VerticalAlignment="Top" Width="" Click="SendMessageButton_Click"/>
 
  • 接下来,我们需要添加事件检查,取消勾选“启用服务器”蜱虫盒。 我们通过更换连接:

这里以下排版不是很好,太晚了看花眼了,请参照原文(英文)

  1. <Button Content="Send" Height="" HorizontalAlignment="Left" Margin="373,274,0,0" Name="sendMessageButton" VerticalAlignment="Top" Width="" Click="SendMessageButton_Click"/>
  1. <TextBox Height="" HorizontalAlignment="Left" Margin="62,274,0,0" Name="messageText" VerticalAlignment="Top" Width="" />
  1. <TextBox Height="" HorizontalAlignment="Left" Margin="62,274,0,0" Name="messageText" VerticalAlignment="Top" Width="" KeyUp="MessageText_KeyUp"/>
  1. <CheckBox Content="Enable Server" Height="" HorizontalAlignment="Left" Margin="377,44,0,0" x:Name="enableServer" VerticalAlignment="Top" />

  1. <CheckBox Content="Use Encryption" Height="" HorizontalAlignment="Left" Margin="377,65,0,0" Name="useEncryptionBox" VerticalAlignment="Top" />
  • 最后,我们需要添加的事件检查和取消勾选“ 使用加密 “蜱虫盒。 我们通过更换线:
  1. <CheckBox Content="Use Encryption" Height="" HorizontalAlignment="Left" Margin="377,65,0,0" Name="useEncryptionBox" VerticalAlignment="Top" Checked="UseEncryptionBox_CheckedToggle" Unchecked="UseEncryptionBox_CheckedToggle" />
 
  • 就是这样。 现在,我们可以看到我们的努力。

7所示。 测试你的WPF聊天应用程序

  • 我们终于抵达了测试阶段。 我们现在要开至少两个WPF聊天应用程序的实例。 要做到这一点,我们首先需要构建项目在调试模式下(确保Visual Studio显示的 调试 在顶部菜单),通过解决方案上单击右键并选择“ 构建解决方案 ”或紧迫的” F6 键盘上的。
  • 现在浏览到构建应用程序的位置。 一种方法是右键单击项目在Visual Studio,选择“ 在Windows资源管理器打开文件夹 ”。 寻找一个文件夹名为“ ”,在“ 调试 ”。
  • 现在,您应该看到一个可执行文件名为“ WPFChatExample.exe ”,双击这两次打开两个实例的例子。 注意:当你打开应用程序从你的系统防火墙可能会得到一个通知。 重要的是要提供必要的权限(见防火墙文档)否则将无法交流的例子。
  • 选择哪一个应用程序将作为一个服务器(指定的应用程序)。检查的 启用服务器 “tickbox。 该应用程序现在应该显示哪些ipaddress和港口可供连接:

完成应用程序的示例输出本地服务器后启用。

  • 选择一个合适的服务器IP地址和端口(通常127.0.0.1或192.168 . * . *)从应用程序A所示的输出和输入这些信息到其他应用程序(应用程序B)。
  • 现在消息输入到应用程序B并点击发送或按enter。 现在的消息将出现在两个应用程序。 一旦连接建立了以这种方式信息现在可以进入两个应用程序中,它就会出现在另一个。
  • 我们刚刚展示的是最基本的连接情况如下,以下应用程序B选择应用程序的服务器:

最基本的连接配置。 应用程序B已选定的应用程序服务器。

  • 我们可以添加另一个应用程序,还贴上C和指定服务器应用程序如下:

另一个基本的连接配置。 应用程序B已选定的应用程序服务器。 应用C也选择的应用程序服务器。

  • 我们可以变得更时髦的因为我们添加了继电器的功能。 而不是应用程序C指定应用程序的服务器可以设置应用程序C应用程序A .一旦应用程序连接的服务器以这种方式进入一个消息客户端C将传送通过B:

一个更先进的连接配置。 应用程序B已选定的应用程序服务器。 C应用程序已经选择的应用程序服务器。

  • 最后一个示例配置有三个应用程序设置他们的戒指。 C应用程序B使用应用服务器,应用程序C使用应用程序的服务器和应用程序使用应用程序B的服务器。 这个配置工作,因为我们有最大数量的继电器/消息和使用消息历史,以防止重复写入到聊天窗口:

最先进的连接配置使用三个客户。 选择应用程序B作为其服务器应用程序。 C应用程序B已经选定的应用程序服务器。 应用程序选择C应用程序的服务器。

如果一切工作

  • 如果你发现了这篇文章有用或有什么想法,我们可以如何改进,请给我们评论。

如果你有问题

  1. 确保您已经正确配置防火墙允许必要的交通。
  2. 如果你还有问题请上我们的 论坛我们将非常乐意帮助。

更多信息

  1. 看到我们的 开始基本的客户端服务器应用程序文章。
  2. 看到我们的网上 API参考这就解释了所有的方法做什么。
  3. 问我们的任何问题 论坛

搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 (1)的更多相关文章

  1. (4opencv)如何基于GOCW,创建一个实时视频程序

    直接使用提供的代码框架进行修改,是最快得到效果的方法:但是这样的灵活性较差,而且真正的程序员从来都不会停滞在这一步:我们需要的是"将框架解析到最小化.理清楚每个构建之间的关系",只 ...

  2. 在 Visual Studio 中创建一个简单的 C# 控制台应用程序

    转载:https://blog.csdn.net/qq_43994242/article/details/87260824 快速入门:使用 Visual Studio 创建第一个 C# 控制台应用 h ...

  3. 4.I/O复用以及基于I/O复用的回射客户端/服务器

    I/O复用:当一个或多个I/O条件满足时,我们就被通知到,这种能力被称为I/O复用. 1.I/O复用的相关系统调用 posix的实现提供了select.poll.epoll两类系统调用以及相关的函数来 ...

  4. 创建一个简单的maven的web程序

    最近学习Hadoop,发现学习要想用hadoop作为后台运行web程序,必须应用maven,所以学习了今天学习了一下maven,然后搭建了一个简单的web程序 首先我使用的是eclipse中自带的ma ...

  5. 简单创建一个完整的struts2框架小程序

    要完成一个struts2框架的搭建, 1.首先应该从官网上下载最新的jar包,网络连接:http://struts.apache.org/download.cgi#struts2514.1,选择下载F ...

  6. 微信小程序开发(一)创建一个小程序Hello World!

    开发微信小程序并不是很难,网上有很多小程序开发资料,尤其是微信官方的<小程序开发指南>最详细. 下面是我开发小程序的历程: 第一步,请前往https://mp.weixin.qq.com/ ...

  7. 如何用Unity创建一个的简单的HoloLens 3D程序

    注:本文提到的代码示例下载地址>How to create a Hello World 3D holographic app with Unity 之前我们有讲过一次如何在HoloLens中创建 ...

  8. 用 JSQMessagesViewController 创建一个 iOS 聊天 App - 第 2 部分

    原文链接 : Create an iOS Chat App using JSQMessagesViewController – Part 2 原文作者 : Mariusz Wisniewski 译者 ...

  9. 一步一步创建聊天程序2-利用epoll来创建简单的聊天室

    如图,这个是看视频时,最后的作业,除了客户端未使用select实现外,其它的要求都有简单实现. 服务端代码如下: #include <stdio.h> #include <strin ...

随机推荐

  1. AutoMapper:Unmapped members were found. Review the types and members below. Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type

    异常处理汇总-后端系列 http://www.cnblogs.com/dunitian/p/4523006.html 应用场景:ViewModel==>Mode映射的时候出错 AutoMappe ...

  2. ubuntu如何安装nodejs最新版 本

    如何正确的安装nodejs? 我们可以先安装nvm, git clone https://github.com/creationix/nvm.git ~/.nvm 然后打开 ~/.bashrc ,   ...

  3. seaJs学习笔记2 – seaJs组建库的使用

    原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...

  4. JDK动态代理

    一.基本概念 1.什么是代理? 在阐述JDK动态代理之前,我们很有必要先来弄明白代理的概念.代理这个词本身并不是计算机专用术语,它是生活中一个常用的概念.这里引用维基百科上的一句话对代理进行定义: A ...

  5. Lind.DDD.LindAspects方法拦截的介绍

    回到目录 什么是LindAspects 之前写了关于Aspects的文章<Lind.DDD.Aspects通过Plugins实现方法的动态拦截~Lind里的AOP>,今天主要在设计思想上进 ...

  6. nginx服务器安装及配置文件详解

    nginx在工作中已经有好几个环境在使用了,每次都是重新去网上扒博客,各种编译配置,今天自己也整理一份安装文档和nginx.conf配置选项的说明,留作以后参考.像负载均衡配置(包括健康检查).缓存( ...

  7. 5.2 Array类型的方法汇总

    所有对象都具有toString(),toLocaleString(),valueOf()方法. 1.数组转化为字符串 toString(),toLocaleString() ,数组调用这些方法,则返回 ...

  8. OpenGL: 纹理采样 texture sample

    Sampler (GLSL) Sampler通常是在Fragment shader(片元着色器)内定义的,这是一个uniform类型的变量,即处理不同的片元时这个变量是一致不变的.一个sampler和 ...

  9. thinkphp-无限分类下根据任意部门获取顶级部门ID

    根据所得到的部门编号获取顶级部门ID: 参数 - department_id 表格组织架构: tab_departments department_id parent_id name 1 1 顶级 2 ...

  10. tg2015 信息传递 (洛谷p2661)

    题目描述 有n个同学(编号为1到n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学. 游戏开始时,每人都只知道自己的生日.之后每一 ...