熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

C#網絡編程初探

2013-11-13 10:50:11  來源: .NET編程 
我們知道C#和C++的差異之一就是他本身沒有類庫所使用的類庫是Net框架中的類庫Net FrameWork SDKNet FrameWork SDK中為網絡編程提供了二個名稱空間SystemNetSystemNetSocketsC#就是通過這二個名稱空間中封裝的類和方法實現網絡通訊的

  首先我們解釋一下在網絡編程時候經常遇到的幾個概念同步(synchronous)異步(asynchronous)阻塞(Block)和非阻塞(Unblock)

  所謂同步方式就是發送方發送數據包以後不等接受方響應就接著發送下一個數據包異步方式就是當發送方發送一個數據包以後一直等到接受方響應後才接著發送下一個數據包而阻塞套接字是指執行此套接字的網絡調用時直到調用成功才返回否則此套節字就一直阻塞在網絡調用上比如調用StreamReader 類的Readlin ( )方法讀取網絡緩沖區中的數據如果調用的時候沒有數據到達那麼此Readlin ( )方法將一直掛在調用上直到讀到一些數據此函數調用才返回而非阻塞套接字是指在執行此套接字的網絡調用時不管是否執行成功都立即返回同樣調用StreamReader 類的Readlin ( )方法讀取網絡緩沖區中數據不管是否讀到數據都立即返回而不會一直掛在此函數調用上在Windows網絡通信軟件開發中最為常用的方法就是異步非阻塞套接字平常所說的C/S(客戶端/服務器)結構的軟件采用的方式就是異步非阻塞模式的

  其實在用C#進行網絡編程中我們並不需要了解什麼同步異步阻塞和非阻塞的原理和工作機制因為在Net FrameWrok SDK中已經已經把這些機制給封裝好了下面我們就用C#開一個具體的網絡程序來說明一下問題

  一.本文中介紹的程序設計及運行環境

   (微軟視窗 服務器版

   (Net Framework SDK Beta 以上版本

  二.服務器端程序設計的關鍵步驟以及解決辦法

  在下面接受的程序中我們采用的是異步阻塞的方式

  (首先要要在給定的端口上面創建一個tcpListener對象偵聽網絡上面的請求當接收到連結請求後通過調用tcpListener對象的AcceptSocket方法產生一個用於處理接入連接請求的Socket的實例下面是具體實現代碼

//創建一個tcpListener對象此對象主要是對給定端口進行偵聽
tcpListener = new TcpListener ( ) ;
//開始偵聽
tcpListenerStart ( ) ;
//返回可以用以處理連接的Socket實例
socketForClient = tcpListenerAcceptSocket ( ) ;

  (接受和發送客戶端數據

  此時Socket實例已經產生如果網絡上有請求在請求通過以後Socket實例構造一個NetworkStream對象NetworkStream對象為網絡訪問提供了基礎數據流我們通過名稱空間SystemIO中封裝的二個類StreamReaderStreamWriter來實現對NetworkStream對象的訪問其中StreamReader類中的ReadLine ( )方法就是從NetworkStream對象中讀取一行字符StreamWriter類中的WriteLine ( )方法就是對NetworkStream對象中寫入一行字符串從而實現在網絡上面傳輸字符串下面是具體的實現代碼

try
{
//如果返回值是true則產生的套節字已經接受來自遠方的連接請求
if ( socketForClientConnected )
{
ListBoxItemsAdd ( 已經和客戶端成功連接! ) ;
while ( true )
{
//創建networkStream對象通過網絡套節字來接受和發送數據
networkStream = new NetworkStream ( socketForClient ) ;
//從當前數據流中讀取一行字符返回值是字符串
streamReader = new StreamReader ( networkStream ) ;
string msg = streamReaderReadLine ( ) ;
ListBoxItemsAdd ( 收到客戶端信息 + msg ) ;
streamWriter = new StreamWriter ( networkStream ) ;
if ( textBoxText != )
{
ListBoxItemsAdd ( 往客戶端反饋信息 + textBoxText ) ;
//往當前的數據流中寫入一行字符串
streamWriterWriteLine ( textBoxText ) ;
//刷新當前數據流中的數據
streamWriterFlush ( ) ;
}
}
}
}
catch ( Exception ey )
{
MessageBoxShow ( eyToString ( ) ) ;
}


  (最後別忘了要關閉所以流停止偵聽網絡關閉套節字具體如下

//關閉線程和流
networkStreamClose ( ) ;
streamReaderClose ( ) ;
streamWriterClose ( ) ;
_threadAbort ( ) ;
tcpListenerStop ( ) ;
socketForClientShutdown ( SocketShutdownBoth ) ;
socketForClientClose ( ) ;

  三.C#網絡編程服務器端程序的部分源代碼(servercs)

  由於在此次程序中我們采用的結構是異步阻塞方式所以在實際的程序中為了不影響服務器端程序的運行速度我們在程序中設計了一個線程使得對網絡請求偵聽接受和發送數據都在線程中處理請在下面的代碼中注意這一點下面是servercs的完整代碼

using System ;
using SystemDrawing ;
using SystemCollections ;
using SystemComponentModel ;
using SystemWindowsForms ;
using SystemData ;
using SystemNetSockets ;
using SystemIO ;
using SystemThreading ;
using SystemNet ;
//導入程序中使用到的名字空間
public class Form : Form
{
private ListBox ListBox ;
private Button button ;
private Label label ;
private TextBox textBox ;
private Button button ;
private Socket socketForClient ;
private NetworkStream networkStream ;
private TcpListener tcpListener ;
private StreamWriter streamWriter ;
private StreamReader streamReader ;
private Thread _thread ;
private SystemComponentModelContainer components = null ;
public Form ( )
{
InitializeComponent ( ) ;
}
//清除程序中使用的各種資源
protected override void Dispose ( bool disposing )
{
if ( disposing )
{
if ( components != null )
{
componentsDispose ( ) ;
}
}
baseDispose ( disposing ) ;
}
private void InitializeComponent ( )
{
label = new Label ( ) ;
button = new Button ( ) ;
button = new Button ( ) ;
ListBox = new ListBox ( ) ;
textBox = new TextBox ( ) ;
SuspendLayout ( ) ;
labelLocation = new Point ( ) ;
labelName = label ;
labelSize = new Size ( ) ;
labelTabIndex = ;
labelText = 往客戶端反饋信息 ;
//同樣的方式設置其他控件這裡略去

thisControlsAdd ( button ) ;
thisControlsAdd ( textBox ) ;
thisControlsAdd ( label ) ;
thisControlsAdd ( button ) ;
thisControlsAdd ( ListBox ) ;
thisMaximizeBox = false ;
thisMinimizeBox = false ;
thisName = Form ;
thisText = C#的網絡編程服務器端! ;
thisClosed += new SystemEventHandler ( thisForm_Closed ) ;
thisResumeLayout ( false ) ;

}
private void Listen ( )
{
//創建一個tcpListener對象此對象主要是對給定端口進行偵聽
tcpListener = new TcpListener ( ) ;
//開始偵聽
tcpListenerStart ( ) ;
//返回可以用以處理連接的Socket實例
socketForClient = tcpListenerAcceptSocket ( ) ;
try
{
//如果返回值是true則產生的套節字已經接受來自遠方的連接請求
if ( socketForClientConnected )
{
ListBoxItemsAdd ( 已經和客戶端成功連接! ) ;
while ( true )
{
//創建networkStream對象通過網絡套節字來接受和發送數據
networkStream = new NetworkStream ( socketForClient ) ;
//從當前數據流中讀取一行字符返回值是字符串
streamReader = new StreamReader ( networkStream ) ;
string msg = streamReaderReadLine ( ) ;
ListBoxItemsAdd ( 收到客戶端信息 + msg ) ;
streamWriter = new StreamWriter ( networkStream ) ;
if ( textBoxText != )
{
ListBoxItemsAdd ( 往客戶端反饋信息 + textBoxText ) ;
//往當前的數據流中寫入一行字符串
streamWriterWriteLine ( textBoxText ) ;
//刷新當前數據流中的數據
streamWriterFlush ( ) ;
}
}
}
}
catch ( Exception ey )
{
MessageBoxShow ( eyToString ( ) ) ;
}
}
static void Main ( )
{
ApplicationRun ( new Form ( ) ) ;
}

private void button_Click ( object sender SystemEventArgs e )
{
ListBoxItems Add ( 服務已經啟動! ) ;
_thread = new Thread ( new ThreadStart ( Listen ) ) ;
_threadStart ( ) ;

}

private void button_Click ( object sender SystemEventArgs e )
{
//關閉線程和流
networkStreamClose ( ) ;
streamReaderClose ( ) ;
streamWriterClose ( ) ;
_threadAbort ( ) ;
tcpListenerStop ( ) ;
socketForClientShutdown ( SocketShutdownBoth ) ;
socketForClientClose ( ) ;
}
private void Form_Closed ( object sender SystemEventArgs e )
{
//關閉線程和流
networkStreamClose ( ) ;
streamReaderClose ( ) ;
streamWriterClose ( ) ;
_threadAbort ( ) ;
tcpListenerStop ( ) ;
socketForClientShutdown ( SocketShutdownBoth ) ;
socketForClientClose ( ) ;
}
}

  四.客戶端程序設計的關鍵步驟以及解決辦法 

  (連接到服務器端的指定端口

  我們采用的本地機既做服務器也做客戶機你可以通過修改IP地址來確定自己想要連接的服務器我們在連接的時候采用了TcpClient此類是在較高的抽象級別(高於Socket類)上面提供TCP服務下面代碼就是連接到本地機(端口為並獲取響應流

//連接到服務器端口在這裡是選用本地機器作為服務器你可以通過修改IP地址來改變服務器
try
{
myclient = new TcpClient ( localhost ) ;
}
catch
{
MessageBoxShow ( 沒有連接到服務器! ) ;
return ;
}
//創建networkStream對象通過網絡套節字來接受和發送數據
networkStream = myclientGetStream ( ) ;
streamReader = new StreamReader ( networkStream ) ;
streamWriter = new StreamWriter ( networkStream ) ;

  (實現接受和發送數據

  在接受和發送數據上面我們依然采用了NetworkStream因為對他進行操作比較簡單具體實現發送和接受還是通過命名空間SystemIOStreamReader類ReadLine ( )方法和StreamWriter類的WriteLine ( )方法具體的實現方法如下

if ( textBoxText == )
{
MessageBoxShow ( 請確定文本框為非空! ) ;
textBoxFocus ( ) ;
return ;
}
try
{
string s ;
//往當前的數據流中寫入一行字符串
streamWriterWriteLine ( textBoxText ) ;
//刷新當前數據流中的數據
streamWriterFlush ( ) ;
//從當前數據流中讀取一行字符返回值是字符串
s = streamReaderReadLine ( ) ;
ListBoxItemsAdd ( 讀取服務器端發送內容 + s ) ;
}
catch ( Exception ee )
{
MessageBoxShow ( 從服務器端讀取數據出現錯誤類型為 + eeToString ( ) ) ;
}


  (最後一步和服務器端是一樣的就是要關閉程序中創建的流具體如下

streamReaderClose ( ) ;
streamWriterClose ( ) ;
networkStreamClose ( ) ;

  五.客戶端的部分代碼

  由於在客戶端不需要偵聽網絡所以在調用上面沒有程序阻塞情況所以在下面的代碼中我們沒有使用到線程這是和服務器端程序的一個區別的地方總結上面的這些關鍵步驟可以得到一個用C#網絡編程 完整的客戶端程序(clientcs)具體如下

using System ;
using SystemDrawing ;
using SystemCollections ;
using SystemComponentModel ;
using SystemWindowsForms ;
using SystemData ;
using SystemNetSockets ;
using SystemIO ;
using SystemThreading ;
//導入程序中使用到的名字空間
public class Form : Form
{
private ListBox ListBox ;
private Label label ;
private TextBox textBox ;
private Button button ;
private NetworkStream networkStream ;
private StreamReader streamReader ;
private StreamWriter streamWriter ;
TcpClient myclient ;
private Label label ;

private SystemComponentModelContainer components = null ;

public Form ( )
{
InitializeComponent ( ) ;
}
//清除程序中使用的各種資源
protected override void Dispose ( bool disposing )
{
if ( disposing )
{
if ( components != null )
{
componentsDispose ( ) ;
}
}
baseDispose ( disposing ) ;
}
private void InitializeComponent ( )
{
label = new Label ( ) ;
button = new Button ( ) ;
ListBox = new ListBox ( ) ;
textBox = new TextBox ( ) ;
label = new Label ( ) ;
SuspendLayout ( ) ;
labelLocation = new Point ( ) ;
labelName = label ;
labelSize = new Size ( ) ;
labelTabIndex = ;
labelText = 信息 ;
//同樣方法設置其他控件
AutoScaleBaseSize = new Size ( ) ;
ClientSize = new Size ( ) ;
thisControlsAdd ( button ) ;
thisControlsAdd ( textBox ) ;
thisControlsAdd ( label ) ;
thisControlsAdd ( label ) ;
thisControlsAdd ( ListBox ) ;
thisMaximizeBox = false ;
thisMinimizeBox = false ;
thisName = Form ;
thisText = C#的網絡編程客戶器端! ;
thisClosed += new SystemEventHandler ( thisForm_Closed ) ;
thisResumeLayout ( false ) ;
//連接到服務器端口在這裡是選用本地機器作為服務器你可以通過修改IP地址來改變服務器
try
{
myclient = new TcpClient ( localhost ) ;
}
catch
{
MessageBoxShow ( 沒有連接到服務器! ) ;
return ;
}
//創建networkStream對象通過網絡套節字來接受和發送數據
networkStream = myclientGetStream ( ) ;
streamReader = new StreamReader ( networkStream ) ;
streamWriter = new StreamWriter ( networkStream ) ;
}
static void Main ( )
{
ApplicationRun ( new Form ( ) ) ;
}

private void button_Click ( object sender SystemEventArgs e )
{

if ( textBoxText == )
{
MessageBoxShow ( 請確定文本框為非空! ) ;
textBoxFocus ( ) ;
return ;
}
try
{
string s ;
//往當前的數據流中寫入一行字符串
streamWriterWriteLine ( textBoxText ) ;
//刷新當前數據流中的數據
streamWriterFlush ( ) ;
//從當前數據流中讀取一行字符返回值是字符串
s = streamReaderReadLine ( ) ;
ListBoxItemsAdd ( 讀取服務器端發送內容 + s ) ;
}
catch ( Exception ee )
{
MessageBoxShow ( 從服務器端讀取數據出現錯誤類型為 + eeToString ( ) ) ;
}
}

private void Form_Closed ( object sender SystemEventArgs e )
{
streamReaderClose ( ) ;
streamWriterClose ( ) ;
networkStreamClose ( ) ;

}
}

  六.總結

  雖然在Net FrameWrok SDK 中只為網絡編程提供了二個命名空間但這二個命名空間中的內容卻是十分豐富的C#利用這二個命名空間既可以實現同步和異步也可以實現阻塞和非阻塞本文通過用C#編寫一個網絡上信息傳輸的程序展現了其豐富的內容由於篇幅所限更深更強大的功能還需要讀者去實踐探索


From:http://tw.wingwit.com/Article/program/net/201311/14603.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.