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