一、實驗要求
具體要求包括:
1,Server支持多客戶訪問;
2,C與S之間使用TCP連接;
3,C與C之間直接通信(不是通過S傳遞)。
4,C與C之間直接通信既可以使用TCP,也可以使用UDP。
5,可以使用Socket,也可以使用TcpClient/UdpClient等;
6,實驗示意圖如下:
二、實驗思路
(1)客戶端登陸服務端時,打開客戶端的監聽併發送客戶端的用戶名和端口號。
(2)客戶端發送信息時,服務端根據客戶端的用戶名返回端口號。
(3)客戶端根據服務端返回的端口號與信息接收方進行tcp通信。
(4)客戶端與客戶端通信時,不保留連接,僅在需要發送信息時建立連接。
三、應用界面
(1)服務器界面:
(2)客戶端界面(未登錄):
(3)客戶端界面(登陸):
(4)客戶端與客戶端互相通信:
(5)客戶端給多個客戶端發送消息:
(6)客戶端退出:
源碼如下:
ClientForm.cs:
using System;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Collections.Generic;
using System.Text;
namespace Client
{
public partial class ClientForm : Form
{
public ClientForm()
{
InitializeComponent();
UserNameList = new List();
UserPortList = new List();
isMessageCome = false;
this.label_State.Text = "未檢測到服務器";
this.FormClosing += ClientWindow_Closing;
}
private bool isMessageCome { get; set; }
private bool isLogin { get; set; }
private TcpListener listener;
private TcpClient SocketForClient;
private BinaryReader br_C;
private BinaryWriter bw_C;
private string receiveString;
private List UserNameList { get; set; }
private List UserPortList { get; set; }
private TcpClient client_S;
private NetworkStream networkStream_S;
private BinaryReader br_S;
private BinaryWriter bw_S;
private IPAddress localIpAddress;
private int remotePort;
private string remoteHost;
private string userName { get; set; }
private string userPort { get; set; }
private enum clientState
{
Login,
Talk,
Logout
};
private delegate void Delegate1();
private delegate void Delegate2();
private void ClientWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (client_S != null)
{
SendMessageToServer(clientState.Logout, "退出服務器。", textBox_Port.Text);
br_S.Close();
bw_S.Close();
client_S.Close();
}
}
private void ClientLogin()
{
GetHostAddress();
remotePort = 51888;
remoteHost = Dns.GetHostName();
try
{
client_S = new TcpClient(remoteHost, remotePort);
this.label_State.Text = "連接服務器成功。";
NetworkStream networkStream = client_S.GetStream();
br_S = new BinaryReader(networkStream);
bw_S = new BinaryWriter(networkStream);
Thread threadReceive = new Thread(ReceiveDataFromServer);
threadReceive.IsBackground = true;
threadReceive.Start();
UpdateClientForm();
//打開監聽器
listener = new TcpListener(localIpAddress, Convert.ToInt16(userPort));
listener.Start();
Thread thread_for_listener = new Thread(ReceiveClient);
thread_for_listener.IsBackground = true;
thread_for_listener.Start();
}
catch
{
//MessageBox.Show(ex.ToString());
this.label_State.Text = "服務器尚未啓動,連接服務器失敗。";
return;
}
}
//接收客戶端與客戶端之間的連接
private void ReceiveClient()
{
receiveString = null;
while (true)
{
try
{
//MessageBox.Show("8", DateTime.Now.ToString());
SocketForClient = listener.AcceptTcpClient();
networkStream_S = SocketForClient.GetStream();
bw_C = new BinaryWriter(networkStream_S);
br_C = new BinaryReader(networkStream_S);
receiveString = br_C.ReadString();
//MessageBox.Show(receiveString, this.userName);
if (receiveString != null)
{
Delegate1 d1 = new Delegate1(AddInfo_C);
textBox_ReceivedMessage.Invoke(d1);
}
br_C.Close();
bw_C.Close();
networkStream_S.Close();
SocketForClient.Close();
}
catch
{
break;
}
}
}
//從服務器接收數據
private void ReceiveDataFromServer()
{
receiveString = null;
while (true)
{
try
{
receiveString = br_S.ReadString();
//MessageBox.Show("5", DateTime.Now.ToString());
if (isUserInfo(receiveString))
{
//更新在線用戶列表
this.UserNameList.Clear();
string[] s = receiveString.Split(',');
for (int i = 0; i < s.Length - 1; i++)
{
if (s[i] != this.userName)
{
UserNameList.Add(s[i]);
}
}
Delegate2 d2 = new Delegate2(AddUserToList);
this.listBox_UserName.Invoke(d2);
}
else if (isUserPort(receiveString))
{
this.UserPortList.Clear();
string[] s = receiveString.Split(',');
int count = Convert.ToInt32(s[0]);
for (int i = 0; i < count; i++)
{
if (s[i + 1] != this.userPort)
{
UserPortList.Add(s[i + 1]);
}
}
string message = "";
for (int i = count + 1; i < s.Length - 1; i++)
{
message += s[i];
}
//MessageBox.Show(message);
for (int i = 0; i < UserPortList.Count; i++)
{
//MessageBox.Show(UserPortList.Count.ToString());
SendMessageToClient(UserPortList[i], message);
}
}
else
{
if (textBox_ReceivedMessage.InvokeRequired)
{
Delegate1 d1 = new Delegate1(AddInfo_S);
textBox_ReceivedMessage.Invoke(d1);
}
}
}
catch
{
break;
}
}
}
//添加信息到面板中
private void AddInfo_S()
{
textBox_ReceivedMessage.Text += DateTime.Now.ToString() + " Server:" + receiveString + "\r\n";
}
private void AddInfo_C()
{
string[] s = receiveString.Split(',');
StringBuilder sb = new StringBuilder();
for (int i = 1; i < s.Length; i++)
{
sb.Append(s[i]);
}
textBox_ReceivedMessage.Text += DateTime.Now.ToString() + " " + s[0] + ":" + sb.ToString() + "\r\n";
}
//添加信息到在線用戶面板中
private void AddUserToList()
{
this.listBox_UserName.Items.Clear();
foreach (string name in UserNameList)
{
this.listBox_UserName.Items.Add(name);
}
}
//判斷是否是用戶信息
private bool isUserInfo(string receiveString)
{
string[] s = receiveString.Split(',');
if (s[s.Length - 1] == "@UserName")
{
return true;
}
return false;
}
//判斷是否是用戶端口號
private bool isUserPort(string receiveString)
{
string[] s = receiveString.Split(',');
if (s[s.Length - 1] == "@UserPort")
{
return true;
}
return false;
}
//發送狀態和信息
//重載1,啓動狀態和離開狀態
private void SendMessageToServer(clientState state, string message, string port)
{
try
{
if (message != "" && port != "")
{
switch (state)
{
case clientState.Login:
bw_S.Write("Login," + textBox_UserName.Text + "," + port);
break;
case clientState.Logout:
bw_S.Write("Logout," + message);
break;
default:
break;
}
bw_S.Flush();
}
}
catch { return; }
}
//重載2,交流狀態
private void SendMessageToServer(clientState state, List list, string message)
{
try
{
if (clientState.Talk == state && list.Count > 0 && message != "")
{
//MessageBox.Show("2", DateTime.Now.ToString());
bw_S.Write("Talk," + GetUserNameFromListBox(list) + message);
bw_S.Flush();
}
}
catch { return; }
}
//獲取需要列表中選中的用戶名
private string GetUserNameFromListBox(List list)
{
string names = "";
int i = list.Count;
names = i.ToString() + ",";
foreach (string name in list)
{
names += name + ",";
}
return names;
}
public void UpdateClientForm()
{
this.textBox_IPAddress.Text = localIpAddress.ToString();
this.userName = textBox_UserName.Text;
this.userPort = textBox_Port.Text;
}
private void GetHostAddress()
{
IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
foreach (var v in ips)
{
if (v.AddressFamily == AddressFamily.InterNetwork)
{
localIpAddress = v;
break;
}
}
}
//判斷信息傳送對象
private List GetListBoxSelectedItems(ListBox listBox_UserName)
{
List list = new List();
for (int i = 0; i < listBox_UserName.SelectedItems.Count; i++)
{
list.Add(listBox_UserName.SelectedItems[i].ToString());
}
return list;
}
private void btn_SendMessage_Click(object sender, EventArgs e)
{
if (isLogin)
{
//MessageBox.Show("1", DateTime.Now.ToString());
SendMessageToServer(clientState.Talk, GetListBoxSelectedItems(this.listBox_UserName), this.textBox_SendMessage.Text);
textBox_SendMessage.Clear();
}
}
//根據端口號發送信息
private void SendMessageToClient(string userPort, string message)
{
try
{
//MessageBox.Show("7", DateTime.Now.ToString());
TcpClient c = new TcpClient(remoteHost, Convert.ToInt32(userPort));
NetworkStream networkStream = c.GetStream();
BinaryWriter bw = new BinaryWriter(networkStream);
BinaryReader br = new BinaryReader(networkStream);
bw.Write(this.userName + "," + message);
bw.Flush();
bw.Close();
br.Close();
networkStream.Close();
c.Close();
//MessageBox.Show("關閉流");
}
catch
{
MessageBox.Show("error 01");
}
}
private void btn_Login_Click(object sender, EventArgs e)
{
if (textBox_UserName.Text != "" && textBox_Port.Text != "")
{
ClientLogin();
SendMessageToServer(clientState.Login, userName, userPort);
textBox_UserName.Enabled = false;
textBox_Port.Enabled = false;
btn_Login.Enabled = false;
isLogin = true;
}
}
private void ClientForm_Load(object sender, EventArgs e)
{
}
}
}
ServerForm.cs :
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using Client;
namespace Server
{
public partial class ServerForm : Form
{
//Variables6
public static IPAddress localIpAddress;
public static int port = 51888;
private TcpListener myListener;
private TcpClient SocketForClient;
private NetworkStream networkStream;
private Thread myThread;
private string message;
private delegate void delegate1();
private delegate void delegate_AddInfo();
//Attributes
//Functions
public ServerForm()
{
InitializeComponent();
this.Closing += ThreadServer_Closing;
try
{
GetHostAddress();
UpdateServerForm();
OpenTcpListener();
OpenOtherListener();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
MessageBox.Show("服務器初始化失敗", "提示");
}
}
//後臺檢測需不需要更新面板
private void UpdateInfoListener()
{
while (true)
{
while (CC.updated == true)
{
delegate1 d = new delegate1(UpdateClientList);
this.dataGridView1.Invoke(d);
CC.updated = false;
}
}
}
//後臺檢測功能
private void OpenOtherListener()
{
Thread messageListener = new Thread(MessageListener);
messageListener.IsBackground = true;
messageListener.Start();
Thread updateInfoListener = new Thread(UpdateInfoListener);
updateInfoListener.IsBackground = true;
updateInfoListener.Start();
}
//後臺檢測是否有信息需要輸出到message面板中
private void MessageListener()
{
while (true)
{
while (CC.messageQueue.Count > 0)
{
message = CC.messageQueue.Dequeue();
delegate_AddInfo delegate1 = new delegate_AddInfo(AddInfo);
textBox_ReceivedMessage.Invoke(delegate1);
}
}
}
private void AddInfo()
{
this.textBox_ReceivedMessage.Text += message + "\r\n";
}
//獲取本機地址
private void GetHostAddress()
{
IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
foreach (var v in ips)
{
if (v.AddressFamily == AddressFamily.InterNetwork)
{
localIpAddress = v;
break;
}
}
}
//更新服務器面板部分信息
public void UpdateServerForm()
{
this.textBox_IPAddress.Text = localIpAddress.ToString();
this.textBox_Port.Text = port.ToString();
}
//開啓tcp監聽器
private void OpenTcpListener()
{
try
{
myListener = new TcpListener(localIpAddress, port);
myListener.Start();
label_State.Text = string.Format("開始在{0}:{1}監聽客戶連接", localIpAddress, port);
myThread = new Thread(ListenClientConnection);
myThread.IsBackground = true;
myThread.Start();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
label_State.Text = "服務器啓動監聽失敗,請嘗試重新啓動。";
}
}
//tcp監聽器
private void ListenClientConnection()
{
while (true)
{
try
{
SocketForClient = myListener.AcceptTcpClient();
networkStream = SocketForClient.GetStream();
User user = new User(SocketForClient);
CC.userList.Add(user);
user.ReceiveData();
delegate1 d = new delegate1(UpdateClientList);
this.dataGridView1.Invoke(d);
CC.SendToClient(user, "Hello!");
}
catch
{
break;
}
}
}
//客戶端關閉的時候
private void ThreadServer_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (myListener != null)
{
if (SocketForClient != null)
{
SocketForClient.Close();
networkStream.Close();
}
myListener.Stop();
this.label_State.Text = "服務器已關閉";
}
}
//添加在線用戶到數組中
private void AddClientToCC()
{
CC.Count = 0;
foreach (DataGridViewRow dr in this.dataGridView1.Rows)
{
CC.Count++;
}
CC.Count--;
int i = 0;
foreach (DataGridViewRow dr in this.dataGridView1.Rows)
{
if (i < CC.Count)
{
CC.userNamePort[i, 0] = dr.Cells[0].Value.ToString();
CC.userNamePort[i, 1] = dr.Cells[1].Value.ToString();
i++;
}
}
}
//更新客戶端列表
private void UpdateClientList()
{
if (CC.userList.Count > 0)
{
try
{
this.dataGridView1.Rows.Clear();
System.Threading.Thread.Sleep(1000);
//創建表的行,並與表相關聯。
foreach (User user in CC.userList)
{
DataGridViewRow dr = new DataGridViewRow();
dr.CreateCells(dataGridView1);
dr.Cells[0].Value = user.UserName;
dr.Cells[1].Value = user.UserPort;
this.dataGridView1.Rows.Add(dr);
}
AddClientToCC();
//發送消息給所有客戶端以更新用戶列表信息
CC.UpdateClientList(CC.userList);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
else
{
this.dataGridView1.Rows.Clear();
}
}
//發送消息的按鈕事件
private void btn_SendMessage_Click(object sender, EventArgs e)
{
if (this.textBox_SendMessage.Text != "")
{
string sendMessage = this.textBox_SendMessage.Text;
List selectedUser = new List();
foreach (User user in CC.userList)
{
//遍歷選中的行對象
foreach (DataGridViewRow dr in this.dataGridView1.SelectedRows)
{
//判斷是否存在於user列表中
if (dr.Cells[0].Value.ToString() == user.UserName && dr.Cells[1].Value.ToString() == user.UserPort)
{
selectedUser.Add(user);
break;
}
}
}
CC.SendToClient(selectedUser, sendMessage);
this.textBox_SendMessage.Text = "";
}
}
//創建客戶端
private void btn_CreateClient_Click(object sender, EventArgs e)
{
ClientForm cForm = new ClientForm();
cForm.Show();
}
private void ServerForm_Load(object sender, EventArgs e)
{
}
private void btn_Flush_Click(object sender, EventArgs e)
{
UpdateClientList();
}
}
}
CC.cs:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace Server
{
public class CC
{
public static List userList { get; set; }
public static Queue messageQueue { get; set; }
public static Dictionary Users { get; set; }
public static bool updated { get; set; }
public static string[,] userNamePort { get; set; }
public static int Count { get; set; }
static CC()
{
userList = new List();
userNamePort = new string[100,2];
messageQueue = new Queue();
Users = new Dictionary();
}
public static void SendToServer(string message)
{
messageQueue.Enqueue(message);
}
public static void RemoveUser(User user)
{
userList.Remove(user);
}
public static void SendToClient(List selectedUser, string message)
{
foreach (User user in selectedUser)
{
user.bw.Write(message);
user.bw.Flush();
}
}
public static void SendToClient(User selectedUser, string message)
{
//MessageBox.Show("4", DateTime.Now.ToString());
selectedUser.bw.Write(message);
selectedUser.bw.Flush();
}
//更新用戶信息列表
public static void UpdateClientList(List selectedUser)
{
string info = "";
foreach (User user in selectedUser)
{
info += user.UserName + ",";
}
info += "@UserName";
foreach (User user in selectedUser)
{
user.bw.Write(info);
user.bw.Flush();
}
}
internal static void SendToClient(string message)
{
throw new NotImplementedException();
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace Server
{
public class User
{
public BinaryReader br { get; private set; }
public BinaryWriter bw { get; private set; }
public string receiveString { get; set; }
public bool isRead { get; set; }
private TcpClient client;
private NetworkStream networkStream;
private Thread IOThread;
private delegate void delegateAddInfo();
private string mmessage;
public string UserName;
public string UserPort;
public User(TcpClient client)
{
this.client = client;
networkStream = client.GetStream();
br = new BinaryReader(this.networkStream);
bw = new BinaryWriter(this.networkStream);
}
public void ReceiveData()
{
IOThread = new Thread(ReceiveFromClient);
IOThread.Start();
}
//接收信息
private void ReceiveFromClient()
{
while (true)
{
receiveString = null;
try
{
receiveString = br.ReadString();
}
catch
{
CC.RemoveUser(this);
return;
}
string[] split = receiveString.Split(',');
switch (split[0])
{
case "Login":
UserName = split[1];
UserPort = split[split.Length - 1];
mmessage = DateTime.Now.ToString() + " " + UserName + "[" + UserPort + "]" + "成功連接服務器。";
break;
case "Talk":
mmessage = DateTime.Now.ToString() + " " + UserName + "[" + UserPort + "]" + GetMessage(receiveString);
break;
case "Logout":
mmessage = DateTime.Now.ToString() + " " + UserName + "[" + UserPort + "]" + "已與服務器斷開。";
//刪除並更新面板
CC.RemoveUser(this);
CC.updated = true;
CC.UpdateClientList(CC.userList);
break;
default:
break;
}
CC.SendToServer(mmessage);
}
}
//處理信息中的用戶信息,查看轉發對象。
private List SendMessageToWho(string receiveString)
{
List users = new List();
string[] s = receiveString.Split(',');
try
{
int number = Convert.ToInt32(s[1]);
if (number > 0)
{
for (int i = 0, j = 2; i < number; i++, j++)
{
users.Add(s[j]);
}
}
return users;
}
catch { return null; }
}
//處理信息
private string GetMessage(string receviceString)
{
//MessageBox.Show("3", DateTime.Now.ToString());
string[] s = receviceString.Split(',');
StringBuilder sb = new StringBuilder();
StringBuilder sb_Return = new StringBuilder();
List list = SendMessageToWho(receiveString);
sb.Append("向");
sb_Return.Append(list.Count.ToString() + ",");
for (int i = 0; i < list.Count; i++)
{
sb.Append(list[i] + ",");
sb_Return.Append(GetUserPort(list[i]) + ",");
}
sb.Append("發送了消息:");
for (int i = list.Count + 2; i < s.Length; i++)
{
sb.Append(s[i]);
sb_Return.Append(s[i]);
}
sb_Return.Append(",@UserPort");
CC.SendToClient(this,sb_Return.ToString());
return sb.ToString();
}
private string GetUserPort(string UserName)
{
for(int i = 0;i