C# Socket網路程式設計之客戶端群發訊息
阿新 • • 發佈:2019-02-02
接上一片部落格,接著socket的學習。此次寫的是伺服器端被多個客戶端連線,並且一個客戶端發訊息,其他連線的客戶端都可接收到。
伺服器端設計思路:
1.要有一個執行緒監聽埠,當有客戶端連線上時,就要產生一個socket物件負責和這個客戶端通訊,此時需要開啟一個執行緒處理與這個客戶端的通訊。
2.轉發給其他客戶端時,需要知道所有連線客戶端的資訊。所以建立一個集合,儲存連線客戶端資訊
3.建立一個客戶端集合類,能夠新增客戶端資訊,同時當一個客戶端離開後能夠從集合中移除資訊
4.建立一個接受客戶端訊息的類,能夠接受產生的socket的例項物件
ClientGeneric類:
class ClientGeneric
{
private Dictionary<string,Socket> clientDic=new Dictionary<string,Socket>();
private static ClientGeneric cg=new ClientGeneric();//單例模式,保證各個執行緒訪問同一個物件
public static ClientGeneric Instance()
{
return cg;
}
//將連線的客戶端資訊和產生的socket物件加入集合中
public void AddClient(string key,Socket socket)
{
clientDic.Add(key,socket);
}
//當一個客戶端離開後移除集合中客戶端資訊與產生的socket物件
public void RemoveClient(string key)
{
clientDic.Remove(key);
}
//群聊是傳送訊息,遍歷集合中客戶端資訊,為每一個客戶端傳送訊息
public void SendMsg(string msg)
{
byte[] msgBytes=Encoding.ASCII.GetBytes(msg.ToCharArray());
foreach(var kvp in clientDic)
{
kvp.Value.Send(msgBytes);
}
}
}
伺服器端接受客戶端會話處理類ServerSession:
class ServerSession
{
private Socket socket;
public ServerSession(Socket socket)
{
this.socket=socket;
}
public void GetMsg()
{
string remoteEndPoint=socket.RemoteEndPiont.ToString();//獲取連線客戶端的資訊
ClientGeneric.Instance().AddClient(remoteEndPiont,socket);
string msg=string.Empty;
byte[] bytes=new byte[1024*1024];
int len=0;
while(true)//迴圈獲取客戶端發來的訊息
{
len=socket.Receive(bytes);
msg=Encoding.UTF8.GetString(bytes,0,len);
if(msg.Equals("End"))
{
ClientGeneric.Instance().Remove(remoteEndPoint);//從集合中移除斷開的客戶端資訊
Thread.CurrentThread.Abort();//停止當前監聽的執行緒
socket.Close();
}
//將收到的資訊進行轉發給其他客戶端
ClientGeneric.Instance().SendMsg(msg);
}
}
}
伺服器監聽類:
public partial class ServerForm
{
Socket socket_Server = null; //定義一個套接字介面物件,並初始化值為空
Thread myThread = null; //定義一個執行緒物件,並初始化值為空
Socket socket_Connet = null; //用於與客戶端連線
string RemoteEndPoint; //客戶端的網路結點
private void buttonStartListen_Click(object sender, EventArgs e)
{
socket_Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//new一個Socket物件,注意這裡用的是流式Socket(針對於面向連線的TCP服務應用)而不是資料報式Socket(針對於面向無連線的UDP服務應用)。
string IP = textBoxIP.Text.Trim();
IPAddress ServerIP = IPAddress.Parse(IP); //提取IP地址
int port = int.Parse(textBoxPort.Text.Trim()); //port是埠號
IPEndPoint point = new IPEndPoint(ServerIP, port); //point為網路結點物件
socket_Server.Bind(point); //將結點繫結到套接字上
socket_Server.Listen(10); //設定連線佇列的最大長度,可根據伺服器的效能,可以設定更大程度。
textBoxListenMsg.AppendText("監聽成功!\t\n");
myThread = new Thread(Listen_Disp);
myThread.IsBackground = true; //設定是否為後臺執行緒,設定後臺執行緒是為了使當關閉視窗時所有其他執行緒關閉
myThread.Start();
}
void Listen_Disp()
{
while (true)
{
socket_Connet = socket_Server.Accept();
//當收到一個客戶端連線,就產生一個socket負責與其通訊,並開啟一個執行緒管理這個客戶端
ServerSession ss = new ServerSession(socket_Connet);
Thread t= new Thread(ss.GetMsg);
t.Start();
t.IsBackground = true;
RemoteEndPoint = socket_Connet.RemoteEndPoint.ToString(); //客戶端網路結點號
textBoxListenMsg.AppendText("成功與" + RemoteEndPoint + "客戶端建立連線!\t\n"); //顯示與客戶端連線情況
OnlineList_Disp(RemoteEndPoint); //顯示線上客戶端
}
}
void OnlineList_Disp(string Info)
{
listBoxOnlineList.Items.Add(Info); //線上列表中顯示連線的客戶端套接字
}
}
客戶端接收發送訊息:
public partial class Form1 : Form
{
Socket socket = null; //定義一個套接字,初始化為空
Thread thread = null; //定義一個執行緒
IPEndPoint point = null;
public Form1()
{
InitializeComponent();
RichTextBox.CheckForIllegalCrossThreadCalls = false; //允許跨執行緒訪問
}
private void buttonConnectToServer_Click(object sender, EventArgs e)
{
string IP = textBoxIP.Text.Trim();
int port = int.Parse(textBoxPort.Text.Trim());
IPAddress ip = IPAddress.Parse(IP);
point = new IPEndPoint(ip, port); //網路結點物件
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //初始化套接字
socket.Connect(point);//連線給定的IP地址和埠號
thread = new Thread(ShowMsg); //開啟一個監聽執行緒,監聽伺服器端發來的資料
thread.IsBackground = true; //後臺執行緒
thread.Start();
OnlineList_Disp(textBoxIP.Text.Trim());
}
void OnlineList_Disp(string Info)
{
listBoxOnlineList.Items.Add(Info); //線上列表中顯示連線的客戶端套接字
}
void ShowMsg()
{
byte[] bytes = new byte[1024 * 1024 * 3]; //設定位元組流陣列為3M
int length = 0;
string Msg = string.Empty;
while (true)
{
length = socket.Receive(bytes);
Msg = System.Text.Encoding.UTF8.GetString(bytes, 0, length); //注意要把位元組轉化為字串
richTextBoxReceiveMsg.AppendText("伺服器端:"+Msg+"\t\n"); //顯示接收的資訊
}
}
void Receive_Show(string Info)
{
richTextBoxReceiveMsg.AppendText(Info + "\t\n");
}
private void button2_Click(object sender, EventArgs e)
{
string sendMsg = richTextBoxMsg.Text.Trim(); //要傳送的資訊
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(sendMsg); //將要傳送的資訊轉化為位元組陣列,因為Socket傳送資料時是以位元組的形式傳送的
socket.Send(bytes); //傳送資料
richTextBoxReceiveMsg.AppendText("客戶端:"+sendMsg+"\t\n");
richTextBoxMsg.Text = "";
}
private void button_Interput_Click(object sender, EventArgs e)
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes("End"); //將要傳送的資訊轉化為位元組陣列,因為Socket傳送資料時是以位元組的形式傳送的
socket.Send(bytes); //傳送資料
thread.Abort();//將監聽執行緒停止,不在接聽伺服器端發來的資料
socket.Close();
}
}
伺服器端設計思路:
1.要有一個執行緒監聽埠,當有客戶端連線上時,就要產生一個socket物件負責和這個客戶端通訊,此時需要開啟一個執行緒處理與這個客戶端的通訊。
2.轉發給其他客戶端時,需要知道所有連線客戶端的資訊。所以建立一個集合,儲存連線客戶端資訊
3.建立一個客戶端集合類,能夠新增客戶端資訊,同時當一個客戶端離開後能夠從集合中移除資訊
4.建立一個接受客戶端訊息的類,能夠接受產生的socket的例項物件
ClientGeneric類:
class ClientGeneric
{
private Dictionary<string,Socket> clientDic=new Dictionary<string,Socket>();
private static ClientGeneric cg=new ClientGeneric();//單例模式,保證各個執行緒訪問同一個物件
public static ClientGeneric Instance()
{
return cg;
}
//將連線的客戶端資訊和產生的socket物件加入集合中
public void AddClient(string key,Socket socket)
{
clientDic.Add(key,socket);
}
//當一個客戶端離開後移除集合中客戶端資訊與產生的socket物件
public void RemoveClient(string key)
{
clientDic.Remove(key);
}
//群聊是傳送訊息,遍歷集合中客戶端資訊,為每一個客戶端傳送訊息
public void SendMsg(string msg)
{
byte[] msgBytes=Encoding.ASCII.GetBytes(msg.ToCharArray());
foreach(var kvp in clientDic)
{
kvp.Value.Send(msgBytes);
}
}
}
伺服器端接受客戶端會話處理類ServerSession:
class ServerSession
{
private Socket socket;
public ServerSession(Socket socket)
{
this.socket=socket;
}
public void GetMsg()
{
string remoteEndPoint=socket.RemoteEndPiont.ToString();//獲取連線客戶端的資訊
ClientGeneric.Instance().AddClient(remoteEndPiont,socket);
string msg=string.Empty;
byte[] bytes=new byte[1024*1024];
int len=0;
while(true)//迴圈獲取客戶端發來的訊息
{
len=socket.Receive(bytes);
msg=Encoding.UTF8.GetString(bytes,0,len);
if(msg.Equals("End"))
{
ClientGeneric.Instance().Remove(remoteEndPoint);//從集合中移除斷開的客戶端資訊
Thread.CurrentThread.Abort();//停止當前監聽的執行緒
socket.Close();
}
//將收到的資訊進行轉發給其他客戶端
ClientGeneric.Instance().SendMsg(msg);
}
}
}
伺服器監聽類:
public partial class ServerForm
{
Socket socket_Server = null; //定義一個套接字介面物件,並初始化值為空
Thread myThread = null; //定義一個執行緒物件,並初始化值為空
Socket socket_Connet = null; //用於與客戶端連線
string RemoteEndPoint; //客戶端的網路結點
private void buttonStartListen_Click(object sender, EventArgs e)
{
socket_Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//new一個Socket物件,注意這裡用的是流式Socket(針對於面向連線的TCP服務應用)而不是資料報式Socket(針對於面向無連線的UDP服務應用)。
string IP = textBoxIP.Text.Trim();
IPAddress ServerIP = IPAddress.Parse(IP); //提取IP地址
int port = int.Parse(textBoxPort.Text.Trim()); //port是埠號
IPEndPoint point = new IPEndPoint(ServerIP, port); //point為網路結點物件
socket_Server.Bind(point); //將結點繫結到套接字上
socket_Server.Listen(10); //設定連線佇列的最大長度,可根據伺服器的效能,可以設定更大程度。
textBoxListenMsg.AppendText("監聽成功!\t\n");
myThread = new Thread(Listen_Disp);
myThread.IsBackground = true; //設定是否為後臺執行緒,設定後臺執行緒是為了使當關閉視窗時所有其他執行緒關閉
myThread.Start();
}
void Listen_Disp()
{
while (true)
{
socket_Connet = socket_Server.Accept();
//當收到一個客戶端連線,就產生一個socket負責與其通訊,並開啟一個執行緒管理這個客戶端
ServerSession ss = new ServerSession(socket_Connet);
Thread t= new Thread(ss.GetMsg);
t.Start();
t.IsBackground = true;
RemoteEndPoint = socket_Connet.RemoteEndPoint.ToString(); //客戶端網路結點號
textBoxListenMsg.AppendText("成功與" + RemoteEndPoint + "客戶端建立連線!\t\n"); //顯示與客戶端連線情況
OnlineList_Disp(RemoteEndPoint); //顯示線上客戶端
}
}
void OnlineList_Disp(string Info)
{
listBoxOnlineList.Items.Add(Info); //線上列表中顯示連線的客戶端套接字
}
}
客戶端接收發送訊息:
public partial class Form1 : Form
{
Socket socket = null; //定義一個套接字,初始化為空
Thread thread = null; //定義一個執行緒
IPEndPoint point = null;
public Form1()
{
InitializeComponent();
RichTextBox.CheckForIllegalCrossThreadCalls = false; //允許跨執行緒訪問
}
private void buttonConnectToServer_Click(object sender, EventArgs e)
{
string IP = textBoxIP.Text.Trim();
int port = int.Parse(textBoxPort.Text.Trim());
IPAddress ip = IPAddress.Parse(IP);
point = new IPEndPoint(ip, port); //網路結點物件
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //初始化套接字
socket.Connect(point);//連線給定的IP地址和埠號
thread = new Thread(ShowMsg); //開啟一個監聽執行緒,監聽伺服器端發來的資料
thread.IsBackground = true; //後臺執行緒
thread.Start();
OnlineList_Disp(textBoxIP.Text.Trim());
}
void OnlineList_Disp(string Info)
{
listBoxOnlineList.Items.Add(Info); //線上列表中顯示連線的客戶端套接字
}
void ShowMsg()
{
byte[] bytes = new byte[1024 * 1024 * 3]; //設定位元組流陣列為3M
int length = 0;
string Msg = string.Empty;
while (true)
{
length = socket.Receive(bytes);
Msg = System.Text.Encoding.UTF8.GetString(bytes, 0, length); //注意要把位元組轉化為字串
richTextBoxReceiveMsg.AppendText("伺服器端:"+Msg+"\t\n"); //顯示接收的資訊
}
}
void Receive_Show(string Info)
{
richTextBoxReceiveMsg.AppendText(Info + "\t\n");
}
private void button2_Click(object sender, EventArgs e)
{
string sendMsg = richTextBoxMsg.Text.Trim(); //要傳送的資訊
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(sendMsg); //將要傳送的資訊轉化為位元組陣列,因為Socket傳送資料時是以位元組的形式傳送的
socket.Send(bytes); //傳送資料
richTextBoxReceiveMsg.AppendText("客戶端:"+sendMsg+"\t\n");
richTextBoxMsg.Text = "";
}
private void button_Interput_Click(object sender, EventArgs e)
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes("End"); //將要傳送的資訊轉化為位元組陣列,因為Socket傳送資料時是以位元組的形式傳送的
socket.Send(bytes); //傳送資料
thread.Abort();//將監聽執行緒停止,不在接聽伺服器端發來的資料
socket.Close();
}
}