搭建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客戶端配置文件 ”。 你現在應該有這樣的。
2。 添加NetworkComms.net DLL項目
- NetworkComms.Net 下載包 包含DLL在所有支持的平台上,但我們感興趣的只是.net4.0>>發布完整的DLL。 這個DLL復制到相同的位置,我們在步驟1中創建的解決方案。
- 我們現在需要添加一個項目引用NetworkComms。 凈DLL我們只是補充道。 右鍵單擊“ WPFChatExample “項目並選擇” 添加引用… ”。 在打開的窗口中選擇Browse選項卡並選擇我們剛剛添加的DLL。
- 如果你擴大 引用 文件夾內的項目你現在應該看到NetworkComms。 凈參考你就像這樣:
3所示。 添加WPF元素
- 我們需要添加文本框和按鈕,我們打算與WPF布局。 雙擊“開始 MainWindow.xaml 的文件,主要查看器中打開:
- 如果你想,你可以現在添加每個文本框和按鈕。 為了節省時間但是我們提供了一個基礎布局,您可以復制和粘貼。 復制並粘貼以下代碼來替代所有現有的代碼在XAML視圖 MainWindow.xaml ”:
- 設計窗口現在應該顯示相當於你剛從上面貼的XAML。 這給了我們的基本布局聊天應用程序:
- 媒體對你的鍵盤的F5′,確保項目成功構建(即錯誤列表窗口在Visual studio仍然是空的)。 如果項目不建立在這一點上請回去在本教程中,確保您已經完成了所有的必要步驟。 如果項目構建你現在應該看到WPF應用程序,當然,我們仍然需要添加的所有功能。
4所示。 添加ChatMessage包裝類
- 下一步是創建一個包裝器類的消息我們將發送和接收,即一個對象我們發送和接收包含所有必要的信息。 右鍵單擊該項目並選擇“ 添加 “>” 新項目… ”。 這應該引出的 添加新項 窗口中,一個選項列表,你可以添加到項目中。 確保的 類 ”項被選中時,在窗口的底部輸入名稱” ChatMessage.cs ”。 現在點擊“ 添加 ”。 新的類文件應該自動打開,你現在應該是這樣的:
- 復制並粘貼以下代碼,取代現有的所有代碼在我們剛剛創建的類,“ ChatMessage.cs ”:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 //我們需要包括以下三本類的命名空間
7 using NetworkCommsDotNet; 8 using NetworkCommsDotNet.Tools; 9 using ProtoBuf; 10 11 namespace WPFChatExample 12 { 13 /// <summary> 14 /// A wrapper class for the messages that we intend to send and receive. 15 /// The [ProtoContract] attribute informs NetworkComms .Net that we intend to 16 /// serialise(連載) (turn into bytes) this object. At the base level the 17 /// serialisation(連載) is performed by protobuf.net. 18 /// </summary> 19 [ProtoContract] 20 class ChatMessage 21 { 22 /// <summary> 23 /// chatmessage標識源. 24 /// We use this variable as the constructor for the ShortGuid. 25 /// The [ProtoMember(1)] attribute informs the serialiser that when 26 /// an object of type ChatMessage is serialised we want to include this variable 27 /// </summary> 28 [ProtoMember(1)] 29 string _sourceIdentifier; 30 31 /// <summary> 32 /// The source identifier is accessible as a ShortGuid 33 /// </summary> 34 public ShortGuid SourceIdentifier { get { return new ShortGuid(_sourceIdentifier); } } 35 36 /// <summary> 37 /// The name of the source of this ChatMessage. 38 /// We use shorthand declaration, get and set. 39 /// The [ProtoMember(2)] attribute informs the serialiser that when 40 /// an object of type ChatMessage is serialised we want to include this variable 41 /// </summary> 42 [ProtoMember(2)] 43 public string SourceName { get; private set; } 44 45 /// <summary> 46 /// The actual message. 47 /// </summary> 48 [ProtoMember(3)] 49 public string Message { get; private set; } 50 51 /// <summary> 52 /// The index of this message. Every message sent by a particular source 53 /// has an incrementing(增值) index. 54 /// </summary> 55 [ProtoMember(4)] 56 public long MessageIndex { get; private set; } 57 58 /// <summary> 59 /// The number of times this message has been relayed. 60 /// </summary> 61 [ProtoMember(5)] 62 public int RelayCount { get; private set; } 63 64 /// <summary> 65 /// We must include a private constructor to be used by the deserialisation step. 66 /// </summary> 67 private ChatMessage() { } 68 69 /// <summary> 70 /// Create a new ChatMessage 71 /// </summary> 72 /// <param name="sourceIdentifier">The source identifier</param> 73 /// <param name="sourceName">The source name</param> 74 /// <param name="message">The message to be sent</param> 75 /// <param name="messageIndex">The index of this message</param> 76 public ChatMessage(ShortGuid sourceIdentifier, string sourceName, string message, long messageIndex) 77 { 78 this._sourceIdentifier = sourceIdentifier; 79 this.SourceName = sourceName; 80 this.Message = message; 81 this.MessageIndex = messageIndex; 82 this.RelayCount = 0; 83 } 84 85 /// <summary> 86 /// Increment the relay count variable 87 /// </summary> 88 public void IncrementRelayCount() 89 { 90 RelayCount++; 91 } 92 } 93 }
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 }
- 因為我們要執行網絡任務在這門課中,我們首先需要添加相關的名稱空間引用。 下面所有的 使用系統… “您想要添加名稱空間引用:
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 #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 = 3; 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 = 0; 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, 0)); 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執行。 網絡聊天消息時已經收到。 在此方法中,我們可以把任何我們想做的但是因為我們正在聊天應用程序可能希望的方法:
- 打印消息ChatBox文本框。
- 從文本框更新消息。
- 傳遞消息給其他同行。
- 可以執行這些功能的方法如下:

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我們需要:
- 我們的機器的主機名設置默認本地名稱。
- 觸發的方法 HandleIncomingMessage “當我們收到一包類型” ChatMessage ”。
- 觸發的方法 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="341" Width="512" Background="#FF7CA0FF" ResizeMode="CanMinimize">
- 我們想要一個當我們點擊“發送消息 發送 ”按鈕。 我們通過更換連接:
<Button Content="Send" Height="23" HorizontalAlignment="Left" Margin="373,274,0,0" Name="sendMessageButton" VerticalAlignment="Top" Width="117" />
這里以下排版不是很好,太晚了看花眼了,請參照原文(英文)
- 我們希望用戶能夠輸入后按回車或返回發送消息的消息框。 我們通過更換連接:
1 <Button Content="Send" Height="23" HorizontalAlignment="Left" Margin="373,274,0,0" Name="sendMessageButton" VerticalAlignment="Top" Width="117" Click="SendMessageButton_Click"/>
- 接下來,我們需要添加事件檢查,取消勾選“啟用服務器”蜱蟲盒。 我們通過更換連接:
這里以下排版不是很好,太晚了看花眼了,請參照原文(英文)
- 最后,我們需要添加的事件檢查和取消勾選“ 使用加密 “蜱蟲盒。 我們通過更換線:
- 就是這樣。 現在,我們可以看到我們的努力。
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應用程序的服務器。
如果一切工作 
- 如果你發現了這篇文章有用或有什么想法,我們可以如何改進,請給我們評論。
如果你有問題
- 確保您已經正確配置防火牆允許必要的交通。
- 如果你還有問題請上我們的 論壇 我們將非常樂意幫助。
更多信息
- 看到我們的 開始 或 基本的客戶端服務器應用程序 文章。
- 看到我們的網上 API參考 這就解釋了所有的方法做什么。
- 問我們的任何問題 論壇 。