伺服端
1: using System.Net; //匯入網路通訊協定相關函數
2: using System.Net.Sockets; //匯入網路插座功能函數
3: using System.Threading; //匯入多執行緒功能函數
4: using System.Collections; //匯入集合物件功能
1: //公用變數宣告
2: TcpListener Server; //伺服端網路監聽器(相當於電話總機)
3: Socket Client; //給客戶用的連線物件(相當於電話分機)
4: Thread Th_Svr; //伺服器監聽用執行緒(電話總機開放中)
5: Thread Th_Clt; //客戶用的通話執行緒(電話分機連線中)
6: Hashtable HT = new Hashtable(); //客戶名稱與通訊物件的集合(雜湊表)(key:Name, Socket)
等待客戶端連線
1: #region 接受客戶連線要求的程式
2: private void ServerSub()
3: {
4: //Server IP 和 Port
5: IPEndPoint EP = new IPEndPoint(IPAddress.Parse(TextBox1.Text), int.Parse(TextBox2.Text));
6: Server = new TcpListener(EP);
7: Server.Start(100);
8: while (true)
9: {
10: Client = Server.AcceptSocket();
11: Th_Clt = new Thread(Listen); //建立監聽這個客戶連線的獨立執行緒
12: Th_Clt.IsBackground = true; //設定為背景執行緒
13: Th_Clt.Start(); //開始執行緒的運作
14: }
15: }
16: #endregion
開始執行後會執行到 AcceptSocket()這一行,
等待客戶連線後才會繼續往下執行: 開啟一條客戶端執行緒 Th_Clt = new Thread(Listen)
並設定成背景執行, 方便Server後續隨時關閉連線
接著, 來看監聽客戶端訊息的程式 Listen()
1: #region 監聽客戶訊息的程式
2: private void Listen()
3: {
4: Socket sck = Client;//複製Client通訊物件到個別客戶專用物件Sck
5: Thread Th = Th_Clt;//複製執行緒Th_Clt到區域變數Th
6: while (true) //持續監聽客戶傳來的訊息
7: {
8: try //用 Sck 來接收此客戶訊息,inLen 是接收訊息的 Byte 數目
9: {
10: byte[] B = new byte[1023]; //建立接收資料用的陣列,長度須大於可能的訊息
11: int inLen = sck.Receive(B); //接收網路資訊(Byte陣列)
12: string Msg = Encoding.Default.GetString(B, 0, inLen); //翻譯實際訊息(長度inLen)
13: string Cmd = Msg.Substring(0, 1); //取出命令碼 (第一個字)
14: string Str = Msg.Substring(1); //取出命令碼之後的訊息
15: switch (Cmd)//依據命令碼執行功能
16: {
17: case "0"://有新使用者上線:新增使用者到名單中
18: HT.Add(Str, sck); //連線加入雜湊表,Key:使用者,Value:連線物件(Socket)
19: Listbox1.Items.Add(Str); //加入上線者名單
20: break;
21: case "9":
22: HT.Remove(Str); //移除使用者名稱為Name的連線物件
23: Listbox1.Items.Remove(Str); //自上線者名單移除Name
24: Th.Abort(); //結束此客戶的監聽執行緒
25: break;
26: }
27: }
28: catch (Exception)
29: {
30: //有錯誤時忽略,通常是客戶端無預警強制關閉程式,測試階段常發生
31: }
32: }
33: }
34:
35: #endregion
36:
每位客戶連線後個別執行新的獨立執行緒Th_Clt=new Thread(Listen),
在Listen()副程式中
(1) 讓client socket掛上server端socket, Socket sck = Client = Server.AcceptSocket()
(2) 複製執行緒Th_Clt到區域變數Th, Th= Th_Clt = new Thread(Listen);
然後while(true){ ….}就會不斷監控該客戶傳來的訊息
byte[] B = new byte[1023]; //建立接收資料用的陣列,長度須大於可能的訊息
int inLen = sck.Receive(B); //接收網路資訊(Byte陣列)
--------------------------------------------------------------------------------------------------------------------------------------------
編碼 ByteToString
string Msg = Encoding.Default.GetString(B, 0, inLen); //翻譯實際訊息(長度inLen)
(1) 取出命令碼 (第一個字)
string Cmd = Msg.Substring(0, 1);
(2) 取出命令碼之後的訊息(user name)
string Str = Msg.Substring(1);
----------------------------------------------------------------------------------------------------------------------------------------------
依據命令碼Cmd執行功能
switch (Cmd)//依據命令碼執行功能
{
case "0"://有新使用者上線:新增使用者到名單中
HT.Add(Str, sck); //連線加入雜湊表,Key:使用者,Value:連線物件(Socket)
Listbox1.Items.Add(Str); //加入上線者名單
break;
case "9":
HT.Remove(Str); //移除使用者名稱為Name的連線物件
Listbox1.Items.Remove(Str); //自上線者名單移除Name
Th.Abort(); //結束此客戶的監聽執行緒
break;
}
-----------------------------------------------------------------------------------------------------------------------------------------------
Client 端應用程式相對簡單些
1: using System.Net; //匯入網路通訊協定相關函數
2: using System.Net.Sockets; //匯入網路插座功能函數
宣告公用變數
1: //公用變數
2: Socket T; //通訊物件
3: string User; //使用者
(1) 利用IPEndPoint開啟伺服器端點
IPEndPoint EP = new IPEndPoint(IPAddress.Parse(IP), Port);
(2) 建立可以雙向通訊的TCP連線
T = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
(3) 連上伺服器的端點EP(類似撥號給電話總機)
T.Connect(EP);
1: string IP = TextBox1.Text; //伺服器IP
2: int Port = int.Parse(TextBox2.Text); //伺服器Port
3: IPEndPoint EP = new IPEndPoint(IPAddress.Parse(IP), Port); //伺服器的連線端點資訊
4: //建立可以雙向通訊的TCP連線
5: T = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
6: User = TextBox3.Text; //使用者名稱
7: timer1.Enabled = true;
8:
9: try
10: {
11: T.Connect(EP); //連上伺服器的端點EP(類似撥號給電話總機)
12: Send("0" + User); //連線後隨即傳送自己的名稱給伺服器
13: }
14: catch (Exception)
15: {
16: MessageBox.Show("無法連上伺服器!"); //連線失敗時顯示訊息
17: return;
18: }
19:
20: Button1.Enabled = false; //讓連線按鍵失效,避免重複連線
21: Button3.Enabled = true;
(1) Send()副程式傳送字串至伺服器
(2) Str2Byte: Encoding.Default.GetBytes()
1: private void Send(string Str)
2: {
3: byte[] B = Encoding.Default.GetBytes(Str); //翻譯字串Str為Byte陣列B
4: int ret = T.Send(B, 0, B.Length, SocketFlags.None); //使用連線物件傳送資料
5: //MessageBox.Show(ret.ToString());
6: }
7:
加入一個timer1和button2 用來顯示連線狀態
1: private void timer1_Tick(object sender, EventArgs e)
2: {
3: try
4: {
5: string IP = TextBox1.Text; //伺服器IP
6: int Port = int.Parse(TextBox2.Text); //伺服器Port
7: IPEndPoint EP = new IPEndPoint(IPAddress.Parse(IP), Port); //伺服器的連線端點資訊
8: //建立可以雙向通訊的TCP連線
9: T = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
10: T.Connect(EP);
11: if(Button2.BackColor != Color.Green)
12: Button2.BackColor = Color.Green;
13: }
14: catch
15: {
16: if (Button2.BackColor != Color.Red)
17: Button2.BackColor = Color.Red;
18: }
19: }
連線成功:顯示綠燈
連線失敗或是伺服器關閉:顯示紅燈
-------------------------------------------------------------------------------------------------------------------------------------------------------
小結:
(1)關於EndPoint
伺服端:
server = new TcpListener(EP);
客戶端:
Socket T = new Socket(…);
T.Connect(EP);
(2)關於編碼
伺服端: 伺服端接收到Byte資料, 轉回String判讀
Encoding.Default.GetString(…)
客戶端: 傳送字串到伺服端, 須以Byte單位傳送
Encoding.Default.GetByte(…)
-------------------------------------------------------------------------------------------------------------------------------------------------------
範例程式
ServerAP: download
ClientAP: download