1、程式分為伺服器端和用戶端;
2、任何一個客戶,均可以與伺服器進行通訊;
3、伺服器端能及時顯示已串連的用戶端狀態,然後將之告知給所有的用戶端;
4、客戶與伺服器串連成功以後,可以與任何一個其他使用者進行聊天通訊;
5、客戶如果退出程式,伺服器要將之告知其他的客戶。
一、伺服器的設計與編程。
伺服器的設計介面如下所示
伺服器端的代碼如下所示:
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;//添加的命名空間using System.Net;using System.Net.Sockets;using System.IO;using System.Threading;namespace WindowsFormsApplication1{ public partial class MainForm : Form { //用來儲存串連的使用者 private List<User> userList = new List<User>(); //使用原生IP地址 IPAddress localAddress; //定義電腦上使用的連接埠號碼 private const int port = 51888; private TcpListener myListener; //定義一個標誌,用來判斷是否正常退出所有接受線程 bool isNormalExit = false; //載入主表單時初始化 public MainForm() { InitializeComponent(); listBoxStatus.HorizontalScrollbar = true; IPAddress[] addrIP = Dns.GetHostAddresses(Dns.GetHostName()); localAddress = addrIP[0]; buttonStop.Enabled = false; } //單擊監聽按鈕的處理事件 private void buttonStart_Click(object sender, EventArgs e) { myListener = new TcpListener(localAddress, port); myListener.Start(); AddItemToListBox(string.Format("開始在{0}:{1}監聽客戶串連", localAddress, port)); //建立一個線程監聽用戶端串連請求 Thread myThread = new Thread(ListenClientConnect); myThread.Start(); buttonStart.Enabled = false; buttonStop.Enabled = true; } //接收用戶端串連 private void ListenClientConnect() { TcpClient newClient = null; while (true) { try { newClient = myListener.AcceptTcpClient(); } catch { //當單擊“停止監聽”或者退出此表單時AcceptTcpClient()會產生異常,因此利用此異常退出迴圈 break; } User user = new User(newClient); Thread threadReceive = new Thread(ReceiveData); threadReceive.Start(user); userList.Add(user); AddItemToListBox(string.Format("[{0}]進入", newClient.Client.RemoteEndPoint)); AddItemToListBox(string.Format("當前串連使用者數:{0}", userList.Count)); } } private void ReceiveData(object userState) { User user = (User)userState; TcpClient client = user.client; while (isNormalExit == false) { string receiveString = null; try { //從網路流中讀出字串,此方法會自動判斷字串長度首碼 receiveString = user.br.ReadString(); } catch { if (isNormalExit == false) { AddItemToListBox(string.Format("與[{0}]失去聯絡,已終止接收該使用者資訊", client.Client.RemoteEndPoint)); RemoveUser(user); } break; } AddItemToListBox(string.Format("來自[{0}]:{1}", user.client.Client.RemoteEndPoint, receiveString)); string[] splitString = receiveString.Split(','); switch (splitString[0]) { case "Login": user.userName = splitString[1]; SendToAllClient(user, receiveString); break; case "Logout": SendToAllClient(user, receiveString); RemoveUser(user); return; case "Talk": string talkString = receiveString.Substring(splitString[0].Length + splitString[1].Length + 2); AddItemToListBox(string.Format("{0}對{1}說:{2}", user.userName, splitString[1], talkString)); SendToClient(user, "talk," + user.userName + "," + talkString); foreach (User target in userList) { if (target.userName == splitString[1] && user.userName != splitString[1]) { SendToClient(target, "talk," + user.userName + "," + talkString); break; } } break; default: AddItemToListBox("什麼意思啊:" + receiveString); break; } } } private void SendToClient(User user, string message) { try { //將字串寫入網路流,此方法會自動附加字串長度首碼 user.bw.Write(message); user.bw.Flush(); AddItemToListBox(string.Format("向[{0}]發送:{1}", user.userName, message)); } catch { AddItemToListBox(string.Format("向[{0}]發送資訊失敗", user.userName)); } } //發送資訊給所有客戶,name指定發給哪個使用者,message儲存資訊的內容 private void SendToAllClient(User user, string message) { string command = message.Split(',')[0].ToLower(); if (command == "login") { for (int i = 0; i < userList.Count; i++) { SendToClient(userList[i], message); if (userList[i].userName != user.userName) { SendToClient(user, "login," + userList[i].userName); } } } else if (command == "logout") { for (int i = 0; i < userList.Count; i++) { if (userList[i].userName != user.userName) { SendToClient(userList[i], message); } } } } //移除使用者,user代表指定要刪除的使用者 private void RemoveUser(User user) { userList.Remove(user); user.close(); AddItemToListBox(string.Format("當前串連使用者數:{0}", userList.Count)); } private delegate void AddItemToListBoxDelegate(string str); //想ListBox中追加狀態資訊,str儲存要追加的資訊 private void AddItemToListBox(string str) { if (listBoxStatus.InvokeRequired) { AddItemToListBoxDelegate d = AddItemToListBox; listBoxStatus.Invoke(d, str); } else { listBoxStatus.Items.Add(str); listBoxStatus.SelectedIndex = listBoxStatus.Items.Count - 1; listBoxStatus.ClearSelected(); } } //按下"停止監聽"按鈕的處理事件 private void buttonStop_Click(object sender, EventArgs e) { AddItemToListBox("開始停止服務,並依次使使用者退出!"); isNormalExit = true; for (int i = userList.Count - 1; i >= 0; i--) { RemoveUser(userList[i]); } myListener.Stop(); buttonStart.Enabled = true; buttonStop.Enabled = false; } //關閉視窗時觸發的事件 private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { if (myListener != null) { //引發buttonStop的Click事件 buttonStop.PerformClick(); } } }}
二、用戶端的設計與編程。
用戶端的設計介面如下所示
用戶端的代碼如下所示:
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;//添加的命名空間using System.Net;using System.Net.Sockets;using System.IO;using System.Threading;namespace SyncChatClient{ public partial class MainForm : Form { private bool isExit = false; private TcpClient client; private BinaryReader br; private BinaryWriter bw; public MainForm() { InitializeComponent(); Random r = new Random((int)DateTime.Now.Ticks); textBoxUserName.Text = "user" + r.Next(100, 999); listBoxOnlineStatus.HorizontalScrollbar = true; } //單擊"串連伺服器"按鈕的Click事件 private void buttonConnect_Click(object sender, EventArgs e) { buttonConnect.Enabled = false; try { //此處為方便示範,實際使用時要將Dns.GetHostName()改為伺服器網域名稱 client = new TcpClient(Dns.GetHostName(), 51888); AddTalkMessage("串連成功"); } catch { AddTalkMessage("串連失敗"); buttonConnect.Enabled = true; return; } //擷取網路流 NetworkStream networkStream = client.GetStream(); //將網路流作為二進位讀寫對象 br = new BinaryReader(networkStream); bw = new BinaryWriter(networkStream); SendMessage("Login," + textBoxUserName.Text); Thread threadReceive = new Thread(new ThreadStart(ReceiveData)); threadReceive.IsBackground = true; threadReceive.Start(); } //處理接受的伺服器端資料 private void ReceiveData() { string receiveString = null; while (isExit == false) { try { //從網路流中讀出字串 receiveString = br.ReadString(); } catch { if (isExit == false) { MessageBox.Show("與伺服器失去聯絡."); } break; } string[] splitString = receiveString.Split(','); string command = splitString[0].ToLower(); switch (command) { case "login": AddOnline(splitString[1]); break; case "logout": RemoveUserName(splitString[1]); break; case "talk": AddTalkMessage(splitString[1] + ":\r\n"); AddTalkMessage(receiveString.Substring(splitString[0].Length + splitString[1].Length + 2)); break; default: AddTalkMessage("什麼意思啊:" + receiveString); break; } } Application.Exit(); } //向伺服器端發送訊息 private void SendMessage(string message) { try { //將字串寫入網路流,此方法會自動附加字串長度首碼 bw.Write(message); bw.Flush(); } catch { AddTalkMessage("發送失敗!"); } } private delegate void MessageDelegate(string message); //在richTextBoxTalkInfo中追加聊天資訊 private void AddTalkMessage(string message) { if(richTextBoxTalkInfo.InvokeRequired) { MessageDelegate d=new MessageDelegate(AddTalkMessage); richTextBoxTalkInfo.Invoke(d,new Object[]{message}); } else { richTextBoxTalkInfo.AppendText(message+Environment.NewLine); richTextBoxTalkInfo.ScrollToCaret(); } } private delegate void AddOnlineDelegate(string message); //在listBoxOnlineStatus中添加線上的其他用戶端資訊 private void AddOnline(string userName) { if (listBoxOnlineStatus.InvokeRequired) { AddOnlineDelegate d = new AddOnlineDelegate(AddOnline); listBoxOnlineStatus.Invoke(d, new object[] { userName }); } else { listBoxOnlineStatus.Items.Add(userName); listBoxOnlineStatus.SelectedIndex = listBoxOnlineStatus.Items.Count - 1; listBoxOnlineStatus.ClearSelected(); } } private delegate void RemoveUserNameDelegate(string userName); //在listBoxOnlineStatus中移除不線上的其他用戶端資訊 private void RemoveUserName(string userName) { if (listBoxOnlineStatus.InvokeRequired) { RemoveUserNameDelegate d = RemoveUserName; listBoxOnlineStatus.Invoke(d, userName); } else { listBoxOnlineStatus.Items.Remove(userName); listBoxOnlineStatus.SelectedIndex = listBoxOnlineStatus.Items.Count - 1; listBoxOnlineStatus.ClearSelected(); } } private void buttonSend_Click(object sender, EventArgs e) { if (listBoxOnlineStatus.SelectedIndex != -1) { SendMessage("Talk," + listBoxOnlineStatus.SelectedItem + "," + textBoxSend.Text + "\r\n"); textBoxSend.Clear(); } else { MessageBox.Show("請先在[當前線上]中選擇一個對話者"); } } //關閉視窗時觸發的事件 private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { if (client != null) { SendMessage("Logout," + textBoxUserName.Text); isExit = true; br.Close(); bw.Close(); client.Close(); } } //在發送資訊文字框中按下【Enter】鍵觸發的事件 private void textBoxSend_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == (char)Keys.Return) { buttonSend.PerformClick(); } } }}
至此,程式編寫完成。
資源下載連結地址:http://download.csdn.net/detail/scrystally/4730653