生活随笔
收集整理的這篇文章主要介紹了
.net网络编程之一:Socket编程
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在.net下進行網絡編程其實也相對比較簡單,因為在.net類庫已經提供了大量封裝好的類。在.net下網絡編程比較底層的類是System.Net.Sockets.Socket類,這個類提供了豐富的方法和屬性,并且還提供了異步數據傳輸支持。 對Socket類做一個簡單的介紹,它有如下常見方法: public Socket Accept ():為新建連接創建新的 Socket。???? public void Bind (EndPoint localEP):使 Socket 與一個本地終結點相關聯。?? public void Close ():關閉 Socket 連接并釋放所有關聯的資源。注意這個方法有沖載方法。? public void Connect (EndPoint remoteEP):建立與遠程主機的連接。注意這個方法有重載方法。 public void Disconnect (bool reuseSocket):關閉套接字連接并是否允許重用套接字。?? public void Listen (int backlog):將 Socket 置于偵聽狀態。?? public int Receive (byte[] buffer):接收來自綁定的 Socket 的數據。注意這個方法有重載方法。? public int ReceiveFrom (byte[] buffer,ref EndPoint remoteEP):接收數據報并存儲源終結點。注意這個方法有重載方法。 public int Send (byte[] buffer):將數據發送到連接的 Socket。注意這個方法有重載方法。 public void SendFile (string fileName):將文件和可選數據異步發送到連接的 Socket。注意這個方法有重載方法。 public int SendTo (byte[] buffer,EndPoint remoteEP):將數據發送到特定終結點。注意這個方法有重載方法。 public void Shutdown (SocketShutdown how):禁用某 Socket 上的發送和接收。 說明: 因為在網絡傳輸時傳輸的數據都是二進制形式的(表現為字節數組),所以如果要傳輸類似于中文這樣的雙字節字符就需要在傳輸之前用合適的編碼轉換成字節數組,然后接收方按照發送方的編碼將接收到字節數組轉換成字符串。另外,注意接收數據的時候是先聲明了一個字節數組,然后將接收到的數據保存到字節數組中,這個方法有個返回值表示實際接收了多少字節數據。這是因為數組是不可變的,假如聲明了一個1024字節大小的數組,而發送方僅發送1字節數據,而接收方并不直到發送方發送的數據量把整個1024字節當作發送發送的數據的話,最終會發生錯誤。 要實現一個服務器端的面向連接的Socket用于接收客戶端的請求的話,有如下步驟: (1)首先根據IP地址和端口號實例化一個Socket,注意端口要要大于1024并且不要使用特殊端口號,要大于1024的原因是1024以下的端口號已經被指派了,而1433、3306這樣的端口號已經被用作SQL Server和MySQL的默認端口號了,若指定為這些端口號容易發生沖突。 (3)接著調用Bind()方法進行綁定,然后再調用Listen()方法用于監聽,Listen()方法的參數用于指定監聽的隊列大小,也就是最多可容納的等待接受的傳入連接數。 (4)再調用Accept()方法,調用這個方法之后會是程序處于阻塞狀態,直至有客戶端連接為止。當有客戶端連接,這個方法將會返回一個新的Socket,使用這個Socket與客戶端進行通訊。 (5)使用Accept()方法返回的新Socket的Send()方法就可以向客戶端發送數據了,還可以使用這個新Socket的Receive()接收客戶端的數據。 (6)最后終止與客戶端會話時,注意使用ShutDown()方法關閉Socket連接,并且使用Close()方法釋放所占用的資源。
使用Socket類編寫客戶端的Socket程序步驟如下: (1)首先指定遠程主機和端口號實例化Socket類,注意連接的端口號一定要與服務器監聽的端口號一致。 (2)接著調用Connect()方法連接遠程主機。 (3)連接到遠程主機之后就可以調用Send()方法向服務器發送請求了,然后可以調用Receive()方法接收服務器響應數據,注意如果是發送的類似于中文這樣的雙字節字符串的話,還需要按照服務器響應的字符串編碼將字節數組轉換成字符串。 最后終止與客戶端會話時,注意使用ShutDown()方法關閉Socket連接,并且使用Close()方法釋放所占用的資源。 需要特別說明的是上面是建立連接式Socket(如建立TCP協議Socket)的一般步驟,如果是建立非連接式Socket(如UDP協議Socket)就不需要進行監聽端口了,直接使用ReceiveFrom()方法就能接收來自指定主機端口的數據,用SendTo()方法就能向直接主機端口發送數據。 下面一個例子來演示Socket的編程,在這個例子里服務器端可以處理多個客戶端請求并響應,具體的做法是每接收到一個客戶端請求就新建一個線程來與客戶端通訊,主程序依然繼續監聽,在例子中新建的與客戶端通訊的Socket類僅處理客戶端的日期時間請求(為了演示,這里做了簡化),根據客戶端的請求的不同發送日期時間中響應信息到客戶端,客戶端接收到數據之后在控制臺顯示。 服務器端主要由兩個類組成:ServerSocket類和SocketThread類,ServerSocket類用于監聽客戶端連接請求,SocketThread類用于接收日期時間顯示請求并做出應答。 ServerSocket類代碼如下(ServerSocket類和SocketThread類位于同一個類庫項目中):
view plain
using ?System;?? using ?System.Net;?? using ?System.Net.Sockets;?? using ?System.Text;?? ?? namespace ?ServerSocket?? {?? ????///?<summary> ?? ????///?Socket監聽服務器,用于監聽客戶端連接請求 ?? ????///?作者:周公 ?? ????///?編寫時間:2009-03-18???? ?? ????///?</summary> ?? ????public ? class ?ServerSocket:IDisposable?? ????{?? ????????Socket?listener?=?null ;?? ????????///?<summary> ?? ????????///?開始監聽指定端口 ?? ????????///?</summary> ?? ????????public ? void ?StartListening( int ?port)?? ????????{?? ????????????//?Data?buffer?for?incoming?data. ?? ????????????byte []?bytes?=? new ?Byte[1024];?? ?? ????????????//以運行服務器端程序所在的機器為服務器監聽客戶端連接 ?? ????????????IPHostEntry?ipHostInfo?=?Dns.Resolve(Dns.GetHostName());?? ????????????IPAddress?ipAddress?=?ipHostInfo.AddressList[0];?? ????????????IPEndPoint?localEndPoint?=?new ?IPEndPoint(ipAddress,?port);?? ?? ????????????//創建一個TCP/IP?Socket用于監聽客戶端連接 ?? ????????????Socket?listener?=?new ?Socket(AddressFamily.InterNetwork,?? ????????????????SocketType.Stream,?ProtocolType.Tcp);?? ?? ????????????try ?? ????????????{?? ????????????????//先綁定要監聽的主機和端口 ?? ????????????????listener.Bind(localEndPoint);?? ????????????????//再開始監聽,并且指定監聽隊列的最大長度 ?? ????????????????listener.Listen(10);?? ?? ????????????????//開始監聽連接 ?? ????????????????while ?( true )?? ????????????????{?? ????????????????????Console.WriteLine("等待客戶端連接..." );?? ????????????????????//線程將一直阻塞直到有新的客戶端連接 ?? ????????????????????Socket?handler?=?listener.Accept();?? ????????????????????//啟用一個新的線程用于處理客戶端連接 ?? ????????????????????//這樣主線程還可以繼續接受客戶端連接 ?? ????????????????????SocketThread?socketThread?=?new ?SocketThread(handler);?? ????????????????}?? ?? ????????????}?? ????????????catch ?(Exception?e)?? ????????????{?? ????????????????Console.WriteLine(e.ToString());?? ????????????}?? ?? ????????}?? ?? ????????public ? static ? int ?Main(String[]?args)?? ????????{?? ????????????ServerSocket?server?=?new ?ServerSocket();?? ????????????server.StartListening(11000);?? ????????????return ?0;?? ????????}?? ? ????????#region?IDisposable?成員 ?? ?? ????????public ? void ?Dispose()?? ????????{?? ????????????if ?(listener?!=? null )?? ????????????{?? ????????????????listener.Shutdown(SocketShutdown.Both);?? ????????????????listener.Close();?? ????????????}?? ????????}?? ? ????????#endregion ?? ????}?? }???
SocketThread類代碼如下:
view plain
using ?System;?? using ?System.Collections.Generic;?? using ?System.Text;?? using ?System.Threading;?? using ?System.Net.Sockets;?? ?? namespace ?ServerSocket?? {?? ????///?<summary> ?? ????///?用于處理客戶端請求的Socket ?? ????///?作者:周公 ?? ????///?編寫時間:2009-03-18 ?? ????///?</summary> ?? ????public ? class ?SocketThread:IDisposable?? ????{?? ????????private ?Socket?socket;?? ????????private ?Thread?thread;?? ????????private ? bool ?isListening?=? false ;?? ????????private ? string ?text;?? ????????///?<summary> ?? ????????///?構造方法 ?? ????????///?</summary> ?? ????????///?<param?name="socket">用于處理客戶端應答的Socket</param> ?? ????????public ?SocketThread(Socket?socket)?? ????????{?? ????????????this .socket?=?socket;?? ????????????isListening?=?true ;?? ????????????thread?=?new ?Thread( new ?ThreadStart(Work));?? ????????????thread.Start();?? ????????}?? ?? ?? ????????public ? void ?Work()?? ????????{?? ????????????byte []?buffer= new ? byte [1024];?? ????????????while ?(isListening)?? ????????????{?? ????????????????int ?receivedLength?=?socket.Receive(buffer);?? ????????????????text=System.Text.Encoding.UTF8.GetString(buffer,0,receivedLength);?? ????????????????//<EOF>是自定義的協議,表示中止消息交流 ?? ????????????????if ?(text.IndexOf( "<EOF>" )?>?-1)?? ????????????????{?? ????????????????????isListening=false ;?? ????????????????????socket.Send(new ? byte []?{?0?});?? ????????????????}?? ????????????????else ?? ????????????????{?? ????????????????????//Console.WriteLine("接收到的數據:"?+?text); ?? ????????????????????//根據客戶端的請求獲取相應的響應信息 ?? ????????????????????string ?message?=?GetMessage(text);?? ????????????????????//將響應信息以字節的方式發送到客戶端 ?? ????????????????????socket.Send(Encoding.UTF8.GetBytes(message));?? ????????????????}?? ????????????}?? ????????}?? ????????private ? string ?GetMessage( string ?request)?? ????????{?? ????????????string ?message?=? string .Empty;?? ????????????//Console.WriteLine("Message="?+?request); ?? ????????????switch ?(request)?? ????????????{?? ????????????????case ? "date" :?message?=? "服務器日期:" +DateTime.Now.ToString( "yyyy-MM-dd" );? break ;?? ????????????????case ? "time" :?message?= "服務器時間:" +?DateTime.Now.ToString( "HH:mm:ss" );? break ;?? ????????????????case ? "datetime" :?message?=? "服務器日期時間:" ?+?DateTime.Now.ToString( "yyyy-MM-dd?HH:mm:ss" );? break ;?? ????????????????case ? "year" :?message?=? "服務器年份:" ?+?DateTime.Now.Year.ToString();? break ;?? ????????????????case ? "month" :?message?=? "服務器月份:" ?+?DateTime.Now.Month.ToString();? break ;?? ????????????????case ? "day" :?message?=? "這是本月第" ?+?DateTime.Now.Day.ToString()+ "天" ;? break ;?? ????????????????default :?message?=? "不正確的參數" ;? break ;?? ????????????}?? ????????????return ?message;?? ????????}?? ? ????????#region?IDisposable?成員 ?? ?? ????????public ? void ?Dispose()?? ????????{?? ????????????isListening?=?false ;?? ????????????if ?(thread?!=? null )?? ????????????{?? ????????????????if ?(thread.ThreadState?!=?ThreadState.Aborted)?? ????????????????{?? ????????????????????thread.Abort();?? ????????????????}?? ????????????????thread?=?null ;?? ????????????}?? ????????????if ?(socket?!=? null )?? ????????????{?? ????????????????socket.Shutdown(SocketShutdown.Both);?? ????????????????socket.Close();?? ????????????}?? ????????}?? ? ????????#endregion ?? ?? ?????????? ????}?? }??
客戶端請求的Socket代碼如下(與前面兩個類位于不同的控制臺項目中):
view plain
using ?System;?? using ?System.Net;?? using ?System.Net.Sockets;?? using ?System.Text;?? ?? namespace ?ServerSocket?? {?? ????///?<summary> ?? ????///?客戶端請求的Socket包裝類 ?? ????///?作者:周公 ?? ????///?編寫時間:2009-03-18 ?? ????///?</summary> ?? ????public ? class ?ClientSocket:IDisposable?? ????{?? ????????private ?Socket?sender?=? null ;?? ????????private ? bool ?isListening?=? false ;?? ????????//定義用于接收服務器響應的存儲區. ?? ????????private ? byte []?bytes?=? new ? byte [1024];?? ????????//用于終止Soket的消息 ?? ????????private ? string ?shutDownMessage?=? "<EOF>" ;?? ?? ????????public ?ClientSocket()?? ????????{?? ????????????try ?? ????????????{?? ????????????????//設置要連接的主機信息并使用11000作為監聽端口. ?? ????????????????IPHostEntry?ipHostInfo?=?Dns.GetHostEntry(Dns.GetHostName());?? ????????????????IPAddress?ipAddress?=?ipHostInfo.AddressList[0];?? ????????????????IPEndPoint?remoteEP?=?new ?IPEndPoint(ipAddress,?11000);?? ?? ????????????????//?創建一個TCP/IP協議的socket連接 ?? ????????????????sender?=?new ?Socket(AddressFamily.InterNetwork,?? ????????????????????SocketType.Stream,?ProtocolType.Tcp);?? ????????????????sender.Connect(remoteEP);?? ????????????????isListening?=?true ;?? ????????????}?? ????????????catch ?(Exception?ex)?? ????????????{?? ?????????????????? ????????????????Console.WriteLine(ex.ToString());?? ????????????}?? ????????}?? ?? ????????public ? void ?StartClient()?? ????????{?? ????????????//?連接到遠程主機,并捕獲所有信息 ?? ????????????try ?? ????????????{?? ?????????????????? ????????????????Console.WriteLine("連接到主機{0}" ,?? ????????????????????sender.RemoteEndPoint.ToString());?? ????????????????OutParameters();?? ????????????????string ?consoleMessage?=?Console.ReadLine();?? ????????????????while ?(isListening&&! string .IsNullOrEmpty(consoleMessage))?? ????????????????{?? ????????????????????consoleMessage?=?consoleMessage.ToLower();?? ????????????????????if ?(consoleMessage?==? "bye" )?? ????????????????????{?? ????????????????????????SendShutDownMessage();?? ????????????????????}?? ????????????????????else ?? ????????????????????{?? ????????????????????????string ?resultMessage=SendMessage(consoleMessage);?? ????????????????????????Console.WriteLine(resultMessage);?? ????????????????????????OutParameters();?? ????????????????????????consoleMessage?=?Console.ReadLine();?? ????????????????????????//Console.WriteLine("consoleMessage="?+?consoleMessage); ?? ????????????????????}?? ????????????????}?? ?? ????????????}?? ????????????catch ?(ArgumentNullException?ane)?? ????????????{?? ????????????????Console.WriteLine("參數異常?:?{0}" ,?ane.ToString());?? ????????????}?? ????????????catch ?(SocketException?se)?? ????????????{?? ????????????????Console.WriteLine("出現Socket異常:?{0}" ,?se.ToString());?? ????????????}?? ????????????catch ?(Exception?e)?? ????????????{?? ????????????????Console.WriteLine("出現了異常?:?{0}" ,?e.ToString());?? ????????????}?? ?? ????????}?? ????????///?<summary> ?? ????????///?向遠程主機發送信息 ?? ????????///?</summary> ?? ????????///?<param?name="message">要發送的信息</param> ?? ????????public ? string ?SendMessage( string ?message)?? ????????{?? ????????????byte []?buffer?=?Encoding.UTF8.GetBytes(message);?? ????????????sender.Send(buffer);?? ????????????int ?count=sender.Receive(bytes);?? ????????????return ?Encoding.UTF8.GetString(bytes,?0,?count);?? ????????}?? ????????///?<summary> ?? ????????///?向服務器發送關閉Socket信息,并中止與服務器的連接 ?? ????????///?</summary> ?? ????????public ? void ?SendShutDownMessage()?? ????????{?? ????????????SendMessage(shutDownMessage);?? ????????????Console.WriteLine("已經關閉與服務器的連接" );?? ????????????isListening?=?false ;?? ????????????Environment.Exit(0);?? ????????}?? ?? ????????private ? void ?OutParameters()?? ????????{?? ????????????Console.WriteLine("參數說明:" );?? ????????????Console.WriteLine("獲取服務器日期:date" );?? ????????????Console.WriteLine("獲取服務器時間:time" );?? ????????????Console.WriteLine("獲取服務器日期時間:datetime" );?? ????????????Console.WriteLine("獲取服務器年份:year" );?? ????????????Console.WriteLine("獲取服務器月份:month" );?? ????????????Console.WriteLine("獲取服務器天數:day" );?? ????????????Console.WriteLine("關閉連接:bye" );?? ????????????Console.WriteLine("請輸入你要進行的操作:" );?? ????????}?? ?? ????????public ? static ? int ?Main(String[]?args)?? ????????{?? ????????????ClientSocket?client?=?new ?ClientSocket();?? ????????????client.StartClient();?? ????????????return ?0;?? ????????}?? ? ????????#region?IDisposable?成員 ?? ?? ????????public ? void ?Dispose()?? ????????{?? ????????????isListening?=?false ;?? ????????????if ?(sender?!=? null )?? ????????????{?? ????????????????sender.Shutdown(SocketShutdown.Both);?? ????????????????sender.Close();?? ????????????}?? ????????}?? ? ????????#endregion ?? ????}?? }??
上面的三個類位于兩個控制臺項目中ServerSocket類和SocketThread類位于同一個類庫項目中,其中ClientSocket類位于另一個控制臺項目中,編譯之后會生成兩個exe文件,運行的時候首先運行服務器端exe程序以便監聽,再運行客戶端exe程序。這個程序運行的效果如下:
注意,在終止程序的時候首先在客戶端向服務器發送一個自定義的字符串“<EOF>”,在客戶端使用命令行參數“bye”就是發送這個字符串的,這樣就會正確終止服務器端響應Socket及正確關閉客戶端請求的Socket,否則有可能拋出異常。 以上僅僅是演示了如何使用Socket進行編程,如果要傳輸文件的話,可能需要更多類似于關閉Socket的自定義字符串用以控制了。另外使用Socket來傳輸數據是比較高效的,但是傳輸控制相對來說要麻煩一點,針對網絡中一些特殊場合的數據傳輸,可以使用特定的傳輸協議,在這個系列的后續文章中會繼續介紹網絡編程的有關知識。程序代碼稍后整理之后提供下載。
目前有關在.net下進行網絡編程的文章較少,而偶爾有需要進行網絡編程的項目需求,所以打算研究一下.net下的網絡編程,抱著交流技術的態度,整理成文章與大家分享。不過在網絡編程方面,本人經驗幾乎沒有經驗,如果大家發現有不當之處,望不吝賜教。
此文中的源代碼可以到:http://download.csdn.net/source/1117938下載。
下一篇將會介紹TCP編程。
轉載于:https://www.cnblogs.com/heavyhe/archive/2011/09/05/4547338.html
《新程序員》:云原生和全面數字化實踐 50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔 為你收集整理的.net网络编程之一:Socket编程 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。