一、实验要求
具体要求包括:
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(); } } }
User.cs:
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