基於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)。