基於Socket伺服器端實現本例主要是建立多用戶端與伺服器之間的資料轉送,首先設計伺服器。開啟VS2008,在D:\C#\ch17目錄下建立名為SocketServer的Windows應用程式。開啟工程,往當前表單中添加控制項,如表17-6所示。 表17-6 添加控制項列表
控 件 |
Name |
Text |
ListBox |
lbInfo |
|
Label |
label |
|
Button |
button1 |
啟動伺服器 |
設計好的介面17-2所示。 接下來開始運用前面的知識設計伺服器,主要分為以下步驟。 (1)首先是對一些命名空間的包含,包括System.Net、System.Net.Sockets、System.IO和System.Threading。然後定義一系列的全域變數,如下所示。
private Socket s; //定義Socket對象 private Thread th; //用戶端串連伺服器的線程 public Socket cSocket; //單個用戶端串連的Socket對象 public NetworkStream ns; //網路流 public StreamReader sr; //流讀取 public StreamWriter sw; //流寫入 private delegate void SetTextCallback(); //用於操作主線程式控制件 (2)接下來是設計用戶端串連的管理,主要包括伺服器與用戶端之間的串連和收發資料問題,把它們放在一個函數Communication裡,如下所示。
public void Communication() { while (true) { try { cSocket = s.Accept(); //用cSocket來代表該用戶端串連 if (cSocket.Connected) //測試是否串連成功 { ns = new NetworkStream(cSocket); //建立網路流,便於資料的讀取 sr = new StreamReader(ns); //執行個體化流讀取對象 sw = new StreamWriter(ns); //執行個體化寫入流對象 test(); //從流中讀取 sw.WriteLine("收到請求,允許串連"); //向流中寫入資料 sw.Flush(); //清理緩衝區 } else { MessageBox.Show("串連失敗"); } } catch (SocketException ex) { MessageBox.Show(ex.Message); //捕獲Socket異常 } catch (Exception es) { MessageBox.Show("其他異常" + es.Message); //捕獲其他異常 } } } //以下代碼的用法在第16章有關線程的用法時曾提到過,主要用於從當前線程操作主線程中的控制項,這裡就不在贅//述 public void send() { lbInfo.Items.Add(sr.ReadLine()+"\n"); } public void test() { SetTextCallback stcb = new SetTextCallback(send); Invoke(stcb); }
(3)定義好與用戶端的串連後,接下來需要利用線程來啟動,雙擊“啟動伺服器”按鈕,添加如下代碼。 button1.Enabled = false; s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//建立Socket對象 IPAddress serverIP = IPAddress.Parse("222.18.142.171"); IPEndPoint server = new IPEndPoint(serverIP,13); //執行個體化伺服器的IP和連接埠 s.Bind(server); s.Listen(10); try { th = new Thread(new ThreadStart(Communication)); //建立線程 th.Start(); //啟動線程 label1.Text = "伺服器啟動成功!"; } catch(Exception ex) { MessageBox.Show("伺服器啟動失敗!" + ex.Message); } (4)最後在關閉伺服器視窗的時候,還應該關閉線程和當前Socket串連,代碼如下所示。
protected override void Dispose(bool disposing) { try { if (disposing && (components != null)) { components.Dispose(); th.Abort(); //禁用當前Socket串連中的資料收發 s.Shutdown(System.Net.Sockets.SocketShutdown.Both); s.Close(); } base.Dispose(disposing); } catch { return; } } //接著為當前表單的FormClosed事件添加如下代碼。 this.Close(); 至此,伺服器就設計完了,下面接著看用戶端。 基於Socket用戶端實現開啟VS2008,在D:\C#\ch17目錄下建立名為SocketClient的Windows應用程式。開啟工程,往當前表單中添加控制項,如表17-7所示。 表17-7 添加控制項列表
控 件 名 |
Name |
Text |
GroupBox |
groupBox1 |
向伺服器發送訊息 |
Label |
label1 |
發送訊息: |
TextBox |
textBox1 |
|
Button |
button2 |
發送 |
ListBox |
lbInfo |
|
GroupBox |
groupBox1 |
伺服器回饋資訊 |
用戶端的設計主要分為以下步驟。 (1)首先要做的工作還是要引用一些命名空間,如下所示。
using System.IO; using System.Net.Sockets; using System.Net; 然後定義一系列的全域變數,如下所示。 private Socket s; //定義Socket對象 public NetworkStream ns; //網路流 public StreamReader sr; //流讀取 public StreamWriter sw; //流寫入 (2)雙擊“發送”按鈕,添加如下代碼。 s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); serverIP = IPAddress.Parse("222.18.142.171"); //伺服器IP try { s.Connect(serverIP, 13); //串連伺服器,連接埠號碼用13 } catch (Exception ex) { MessageBox.Show(ex.Message); } try { ns = new NetworkStream(s); //執行個體化網路流 sr = new StreamReader(ns); //執行個體化流讀取對象 sw = new StreamWriter(ns); //執行個體化寫入流對象 sw.WriteLine(textBox1.Text); //將textBox1.Text的資料寫入流 sw.Flush(); //清理緩衝區 lbInfo.Items.Add(sr.ReadLine()); //將從流中讀取的資料寫入lbInfo28 } catch (Exception ex) { MessageBox.Show(ex.Message); //捕獲異常 }
(3)最後,關閉Socket串連,釋放資源,可以直接在表單的FormClosed事件中進行,如下所示。 s.Shutdown(SocketShutdown.Both); s.Close(); 至此,伺服器和用戶端已經全部設計完畢,下面看運行效果。 基於Socket的C/S執行個體的運行首先,啟動伺服器程式,單擊“啟動伺服器”按鈕,17-4所示。接著再啟動用戶端,並在textBox1輸入資料(中英文皆可),多次單擊“發送”按鈕,17-5所示。 此時,伺服器的狀態17-6所示。 收到用戶端發送的資料 從圖17-6中可以看出,此時伺服器已經收到來自用戶端的資料,而圖17-5中顯示用戶端也收到了來自伺服器的回饋訊息。此外,本例還能實現多個用戶端向一個伺服器同時收發資料,並已經在三台電腦(一個伺服器和兩個用戶端)上測試通過。本例只是實現了伺服器與用戶端最簡單的資料轉送,讀者如果有興趣可以在此基礎上再添加其他功能,比如可以利用前面講的資料庫知識,在伺服器端寫個使用者登入驗證,讓用戶端通過驗證後才能收發資料等。 在網路中,收發資料會經常使用Socket。本節中主要運用了幾節中介紹的Socket基礎知識完成了一個簡單的基於流的C/S模式例子。在下一節中,將介紹另一種傳輸方式——基於資料報(UDP)。
|