基於Socket服務器端實現本例主要是創建多客戶端與服務器之間的數據傳輸,首先設計服務器。打開VS2008,在D:\C#\ch17目錄下創建名爲SocketServer的Windows應用程序。打開工程,往當前窗體中添加控件,如表17-6所示。 表17-6 添加控件列表數據庫
控 件服務器 |
Name網絡 |
Textide |
ListBox函數 |
lbInfo測試 |
|
Labelthis |
labelspa |
|
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)。 |