C# Socket程式設計 同步以及非同步通訊
套接字簡介:套接字最早是Unix的,window是借鑑過來的。TCP/IP協議族提供三種套接字:流式、資料報式、原始套接字。其中原始套接字允許對底層協議直接訪問,一般用於檢驗新協議或者新裝置問題,很少使用。
套接字程式設計原理:延續檔案作用思想,開啟-讀寫-關閉的模式。
C/S程式設計模式如下:
Ø 伺服器端:
開啟通訊通道,告訴本地機器,願意在該通道上接受客戶請求——監聽,等待客戶請求——接受請求,建立專用連結進行讀寫——處理完畢,關閉專用連結——關閉通訊通道(當然其中監聽到關閉專用連結可以重複迴圈)
Ø 客戶端:開啟通訊通道,連線伺服器——資料互動——關閉通道。
Socket通訊方式:
Ø 同步:客戶端在傳送請求之後必須等到伺服器迴應之後才可以傳送下一條請求。序列執行
Ø 非同步:客戶端請求之後,不必等到伺服器迴應之後就可以傳送下一條請求。並行執行
套接字模式:
Ø 阻塞:執行此套接字呼叫時,所有呼叫函式只有在得到返回結果之後才會返回。在呼叫結果返回之前,當前程序會被掛起。即此套接字一直被阻塞在網路呼叫上。
Ø 非阻塞:執行此套接字呼叫時,呼叫函式即使得不到得到返回結果也會返回。
套接字工作步驟:
Ø 伺服器監聽:監聽時伺服器端套接字並不定位具體客戶端套接字,而是處於等待連結的狀態,實時監控網路狀態
Ø 客戶端連結:客戶端發出連結請求,要連線的目標是伺服器端的套接字。為此客戶端套接字必須描述伺服器端套接字的伺服器地址與埠號。
Ø 連結確認:是指伺服器端套接字監聽到客戶端套接字的連結請求時,它響應客戶端連結請求,建立一個新的執行緒,把伺服器端套接字的描述傳送給客戶端,一旦客戶端確認此描述,則連結建立好。而伺服器端的套接字繼續處於監聽狀態,繼續接受其他客戶端套接字請求。
在TCP/IP網路中,IP網路互動分類兩大類:面向連線的互動與面向無連線的互動。
Socket建構函式:public socket(AddressFamily 定址型別, SocketType 套接字型別, ProtocolType 協議型別)。但需要注意的是套接字型別與協議型別並不是可以隨便組合。
SocketType |
ProtocolType |
描述 |
||||||
Stream |
Tcp |
面向連線 |
||||||
Dgram |
Udp |
面向無連線 |
||||||
Raw |
Icmp |
網際訊息控制 |
||||||
Raw |
Raw |
基礎傳輸協議 |
||||||
Socket類的公共屬性: |
||||||||
屬性名 |
描述 | |||||||
AddressFamily |
獲取Socket的地址族 |
|||||||
Available |
獲取已經從網路接收且可供讀取的資料量 |
|||||||
Blocking |
獲取或設定一個值,只是socket是否處於阻塞模式 |
|||||||
Connected |
獲取一個值,指示當前連線狀態 |
|||||||
Handle |
獲取socket的作業系統控制代碼 |
|||||||
LocalEndPoint |
獲取本地終端EndPoint |
|||||||
RemoteEndPoint |
獲取遠端終端EndPoint |
|||||||
ProtocolType |
獲取協議型別 |
|||||||
SocketType |
獲取SocketType型別 |
|||||||
Socket常用方法: |
||||||||
Bind(EndPoint) |
伺服器端套接字需要繫結到特定的終端,客戶端也可以先繫結再請求連線 |
|||||||
Listen(int) |
監聽埠,其中parameters表示最大監聽數 |
|||||||
Accept() |
接受客戶端連結,並返回一個新的連結,用於處理同客戶端的通訊問題 |
|||||||
Send() |
傳送資料 |
|||||||
Send(byte[]) |
簡單傳送資料 |
|||||||
Send(byte[],SocketFlag) |
使用指定的SocketFlag傳送資料 |
|||||||
Send(byte[], int, SocketFlag) |
使用指定的SocketFlag傳送指定長度資料 |
|||||||
Send(byte[], int, int, SocketFlag) |
使用指定的SocketFlag,將指定位元組數的資料傳送到已連線的socket(從指定偏移量開始) |
|||||||
Receive() |
接受資料 |
|||||||
Receive(byte[]) |
簡單接受資料 |
|||||||
Receive (byte[],SocketFlag) |
使用指定的SocketFlag接受資料 |
|||||||
Receive (byte[], int, SocketFlag) |
使用指定的SocketFlag接受指定長度資料 |
|||||||
Receive (byte[], int, int, SocketFlag) |
使用指定的SocketFlag,從繫結的套接字接收指定位元組數的資料,並存到指定偏移量位置的緩衝區 |
|||||||
Connect(EndPoint) |
連線遠端伺服器 |
|||||||
ShutDown(SocketShutDown) |
禁用套接字,其中SocketShutDown為列舉,Send禁止傳送,Receive為禁止接受,Both為兩者都禁止 |
|||||||
Close() |
關閉套接字,釋放資源 |
|||||||
非同步通訊方法: |
||||||||
BeginAccept(AsynscCallBack,object) |
開始一個一步操作接受一個連線嘗試。引數:一個委託。一個物件。物件包含此請求的狀態資訊。其中回撥方法中必須使用EndAccept方法。應用程式呼叫BegineAccept方法後,系統會使用單獨的執行緒執行指定的回撥方法並在EndAccept上一直處於阻塞狀態,直至監測到掛起的連結。EndAccept會返回新的socket物件。供你來同遠端主機資料互動。不能使用返回的這個socket接受佇列中的任何附加連線。呼叫BeginAccept當希望原始執行緒阻塞的時候,請呼叫WaitHandle.WaitOne方法。當需要原始執行緒繼續執行時請在回撥方法中使用ManualResetEvent的set方法 |
|||||||
BeginConnect(EndPoint, AsyncCallBack, Object) |
回撥方法中必須使用EndConnect()方法。Object中儲存了連線的詳細資訊。 |
|||||||
BeginSend(byte[], SocketFlag, AsyncCallBack, Object) |
||||||||
BegineReceive(byte[], SocketFlag, AsyncCallBack, Object) |
||||||||
BegineDisconnect(bool, AsyncCallBack, Object) |
給出同步通訊與非同步通訊的示例:
同步通訊: |
預定義結構體,同步通訊沒有多執行緒非同步委託回撥,所以無需預定義結構體 |
客戶端Client: |
classProgram { staticvoid Main() { try{ int port = 2000; string host = "127.0.0.1"; IPAddress ip = IPAddress.Parse(host); IPEndPoint ipe = newIPEndPoint(ip, port);//把ip和埠轉化為IPEndPoint例項 Socket c = newSocket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);//建立一個Socket Console.WriteLine("Conneting..."); c.Connect(ipe);//連線到伺服器 string sendStr = "hello!This is a socket test"; byte[] bs = Encoding.ASCII.GetBytes(sendStr); Console.WriteLine("Send Message"); c.Send(bs, bs.Length, 0);//傳送測試資訊 string recvStr = ""; byte[] recvBytes = newbyte[1024]; int bytes; bytes = c.Receive(recvBytes, recvBytes.Length, 0);//從伺服器端接受返回資訊 recvStr += Encoding.ASCII.GetString(recvBytes, 0, bytes); Console.WriteLine("Client Get Message:{0}", recvStr);//顯示伺服器返回資訊 c.Close(); } catch (ArgumentNullException e){ Console.WriteLine("ArgumentNullException: {0}", e); } catch (SocketException e){ Console.WriteLine("SocketException: {0}", e); } Console.WriteLine("Press Enter to Exit"); Console.ReadLine(); } } |
伺服器端: |
classProgram { staticvoid Main() { try{ int port = 2000; string host = "127.0.0.1"; IPAddress ip = IPAddress.Parse(host); IPEndPoint ipe = newIPEndPoint(ip, port); Socket s = newSocket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);//建立一個Socket類 s.Bind(ipe);//繫結2000埠 s.Listen(0);//開始監聽 Console.WriteLine("Wait for connect"); Socket temp = s.Accept();//為新建連線建立新的Socket。 Console.WriteLine("Get a connect"); string recvStr = ""; byte[] recvBytes = newbyte[1024]; int bytes; bytes = temp.Receive(recvBytes, recvBytes.Length, 0);//從客戶端接受資訊 recvStr += Encoding.ASCII.GetString(recvBytes, 0, bytes); Console.WriteLine("Server Get Message:{0}", recvStr);//把客戶端傳來的資訊顯示出來 string sendStr = "Ok!Client Send Message Sucessful!"; byte[] bs = Encoding.ASCII.GetBytes(sendStr); temp.Send(bs, bs.Length, 0);//返回客戶端成功資訊 temp.Close(); s.Close(); } catch (ArgumentNullException e){ Console.WriteLine("ArgumentNullException: {0}", e);} catch (SocketException e){ Console.WriteLine("SocketException: {0}", e);} Console.WriteLine("Press Enter to Exit"); Console.ReadLine(); } } |
非同步通訊: |
客戶端Client: |
預定義結構體,用於非同步委託之間的傳遞。使用者根據自己需要定製即可 |
publicclassStateObject { // Client socket. publicSocket workSocket = null; // Size of receive buffer. publicconstint BufferSize = 256; // Receive buffer. publicbyte[] buffer = newbyte[BufferSize]; // Received data string. publicStringBuilder sb = newStringBuilder(); } |
正文: |
publicclassAsynchronousClient { // The port number for the remote device. privateconstint port = 11000; // ManualResetEvent instances signal completion. privatestaticManualResetEvent connectDone = newManualResetEvent(false); privatestaticManualResetEvent sendDone = newManualResetEvent(false); privatestaticManualResetEvent receiveDone = newManualResetEvent(false); // The response from the remote device. privatestaticString response = String.Empty; privatestaticvoid StartClient(){ // Connect to a remote device. try{ // Establish the remote endpoint for the socket. // The name of the remote device is "host.contoso.com". IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com"); IPAddress ipAddress = ipHostInfo.AddressList[0]; IPEndPoint remoteEP = newIPEndPoint(ipAddress, port); // Create a TCP/IP socket. Socket client = newSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // Connect to the remote endpoint. client.BeginConnect(remoteEP, newAsyncCallback(ConnectCallback), client); connectDone.WaitOne(); // Send test data to the remote device. Send(client, "This is a test<EOF>"); sendDone.WaitOne(); // Receive the response from the remote device. Receive(client); receiveDone.WaitOne(); // Write the response to the console. Console.WriteLine("Response received : {0}", response); // Release the socket. client.Shutdown(SocketShutdown.Both); client.Close(); } catch (Exception e){ Console.WriteLine(e.ToString());} } privatestaticvoid ConnectCallback(IAsyncResult ar) { try{ // Retrieve the socket from the state object. Socket client = (Socket)ar.AsyncState; // Complete the connection. client.EndConnect(ar); Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint.ToString()); // Signal that the connection has been made. connectDone.Set(); } catch (Exception e){ Console.WriteLine(e.ToString());} } privatestaticvoid Receive(Socket client) { try{ // Create the state object. StateObject state = newStateObject(); state.workSocket = client; // Begin receiving the data from the remote device. client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, newAsyncCallback(ReceiveCallback), state); } catch (Exception e){ Console.WriteLine(e.ToString());} } privatestaticvoid ReceiveCallback(IAsyncResult ar) { try{ // Retrieve the state object and the client socket // from the asynchronous state object. StateObject state = (StateObject)ar.AsyncState; Socket client = state.workSocket; // Read data from the remote device. int bytesRead = client.EndReceive(ar); if (bytesRead > 0){ // There might be more data, so store the data received so far. state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead)); // Get the rest of the data. client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, newAsyncCallback(ReceiveCallback), state); } else{ // All the data has arrived; put it in response. if (state.sb.Length > 1) { response = state.sb.ToString(); } // Signal that all bytes have been received. receiveDone.Set(); } } catch (Exception e){ Console.WriteLine(e.ToString());} } privatestaticvoid Send(Socket client, String data) { // Convert the string data to byte data using ASCII encoding. byte[] byteData = Encoding.ASCII.GetBytes(data); // Begin sending the data to the remote device. client.BeginSend(byteData, 0, byteData.Length, 0, newAsyncCallback(SendCallback), client); } privatestaticvoid SendCallback(IAsyncResult ar) { try{ // Retrieve the socket from the state object. Socket client = (Socket)ar.AsyncState; // Complete sending the data to the remote device. int bytesSent = client.EndSend(ar); Console.WriteLine("Sent {0} bytes to server.", bytesSent); // Signal that all bytes have been sent. sendDone.Set(); } catch (Exception e){ Console.WriteLine(e.ToString());} } publicstaticint Main(String[] args) { StartClient(); return 0; } } |
伺服器端Server: |
預定義結構體,用於非同步委託之間的傳遞。同客戶端的一致。不再贅述 |
正文: |
// State object for reading client data asynchronously publicclassAsynchronousSocketListener { // Thread signal. publicstaticManualResetEvent allDone = newManualResetEvent(false); public AsynchronousSocketListener(){} publicstaticvoid StartListening() { // Data buffer for incoming data. byte[] bytes = newByte[1024]; // Establish the local endpoint for the socket. // The DNS name of the computer // running the listener is "host.contoso.com". //IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName()); IPHostEntry ipHostInfo = Dns.Resolve("127.0.0.1"); IPAddress ipAddress = ipHostInfo.AddressList[0]; IPEndPoint localEndPoint = newIPEndPoint(ipAddress, 11000); // Create a TCP/IP socket. Socket listener = newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); // Bind the socket to the local endpoint and listen for incoming connections. try{ listener.Bind(localEndPoint); listener.Listen(100); while (true){ // Set the event to nonsignaled state. allDone.Reset(); // Start an asynchronous socket to listen for connections. Console.WriteLine("Waiting for a connection..."); listener.BeginAccept(newAsyncCallback(AcceptCallback),listener); // Wait until a connection is made before continuing. allDone.WaitOne(); } } catch (Exception e){ Console.WriteLine(e.ToString());} Console.WriteLine("\nPress ENTER to continue..."); Console.Read(); } publicstaticvoid AcceptCallback(IAsyncResult ar) { // Signal the main thread to continue. allDone.Set(); // Get the socket that handles the client request. Socket listener = (Socket)ar.AsyncState; Socket handler = listener.EndAccept(ar); // Create the state object. StateObject state = newStateObject(); state.workSocket = handler; handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,newAsyncCallback(ReadCallback), state); } publicstaticvoid ReadCallback(IAsyncResult ar) { String content = String.Empty; // Retrieve the state object and the handler socket // from the asynchronous state object. StateObject state = (StateObject)ar.AsyncState; Socket handler = state.workSocket; // Read data from the client socket. int bytesRead = handler.EndReceive(ar); if (bytesRead > 0) { // There might be more data, so store the data received so far. state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead)); // Check for end-of-file tag. If it is not there, read // more data. content = state.sb.ToString(); if (content.IndexOf("<EOF>") > -1){ // All the data has been read from the // client. Display it on the console. Console.WriteLine("Read {0} bytes from socket. \n Data : {1}", content.Length, content); // Echo the data back to the client. Send(handler, "Server return :" + content); } else{ // Not all data received. Get more. handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, newAsyncCallback(ReadCallback), state); } } } privatestaticvoid Send(Socket handler, String data){ // Convert the string data to byte data using ASCII encoding. byte[] byteData = Encoding.ASCII.GetBytes(data); // Begin sending the data to the remote device. handler.BeginSend(byteData, 0, byteData.Length, 0, newAsyncCallback(SendCallback), handler); } privatestatic |