C# Socket的TCP通訊


Socket的TCP通訊

一、 socket的通訊原理

服務器端的步驟如下。

(1)建立服務器端的Socket,開始偵聽整個網絡中的連接請求。

(2)當檢測到來自客戶端的連接請求時,向客戶端發送收到連接請求的信息,並建立與客戶端之間的連接。

(3)當完成通信后,服務器關閉與客戶端的Socket連接。

客戶端的步驟如下。

(1)建立客戶端的Socket,確定要連接的服務器的主機名和端口。

(2)發送連接請求到服務器,並等待服務器的回饋信息。

(3)連接成功后,與服務器進行數據的交互。

(4)數據處理完畢后,關閉自身的Socket連接。

二、 socket的通訊方式

socket通訊方式有兩種:同步和異步

同步工作方式:

用TCP協議進行編程時程序執行到發送、接收和監聽語句的時候,在未完成工作前不再繼續往下執行,即處於阻塞狀態,直到該語句完成某個工作后才繼續執行下一條語句。

異步工作方式

程序執行到發送、接收和監聽語句的時候,不論工作是否完成,都會繼續往下執行。

三、 socket的C#實現

  1. 1.      同步: 

服務端客戶端通信

在與服務端的連接建立以后,我們就可以通過此連接來發送和接收數據。端口與端口之間以流(Stream)的形式傳輸數據,因為幾乎任何對象都可以保存到流中,所以實際上可以在客戶端與服務端之間傳輸任何類型的數據。對客戶端來說,往流中寫入數據,即為向服務器傳送數據;從流中讀取數據,即為從服務端接收數據。對服務端來說,往流中寫入數據,即為向客戶端發送數據;從流中讀取數據,即為從客戶端接收數據。

 

服務端: 

(1)服務端對端口進行偵聽:

服務器端建立一個socket,設置好本機的ip和監聽的端口與socket進行綁定,開始監聽連接請求,當接收到連接請求后,發送確認,同客戶端建立連接,開始與客戶端進行通信。

TcpListener listener =new TcpListener(new IPEndPoint(IPAddress.Parse(ip), port));//ip為服務器IP地址,port為監聽的端口

Listener.Start();//開啟監聽

 

(2)檢測來自客戶端的連接請求

TcpClient remoteClient = listener.AcceptTcpClient();

//接收客戶端  這里體現了同步的含義,如果客戶端對該服務端發起連接的時候,程序在這里就會等待(阻塞),直到有客戶端的連接請求為止

(3)建立和連接的客戶端的數據流(傳輸數據)

NetworkStream streamToClient = remoteClient.GetStream();

 

該數據流只要是用來接收和發送數據,同步也分多客戶端和單個客戶端,如果分的詳細一點的話,還有客戶端的一條以及多條數據的情況,如果是單個客戶端的多條數據的話,連接客戶端之后,在建立數據流的前面添加一個循環就可以了,如果是多個客戶端的話,在(2)前面加個循環就可以了。為了接收數據的效率,建議不管是同步還是異步,服務端都做成線程,詳細見Demo

(4)接收客戶端發送過來的數據(用緩存來接收)

byte [] buffer = new  byte [BufferSize];  // BufferSize為緩存的大小
 
  int  bytesRead;
 
  try
 
  {
 
     lock  (streamToClient) //為了保證數據的完整性以及安全性  鎖定數據流
 
      {
 
          bytesRead = streamToClient.Read(buffer, 0, BufferSize);
 
}

(5)向連接的客戶端發送數據

lock  (streamToClient)
 
                       {
 
                       streamToClient.Write(buffer, 0, buffer.Length); //buffer為發送的字符數組                  
 
}

      (6)釋放數據流和TcpClient(以便下次的數據以及客戶端的收發)

streamToClient.Dispose(); //釋放數據流中的數據
 
               remoteClient.Close(); //釋放TcpClient實例

客戶端 

(1)   連接服務器

TcpClient tcp = new  TcpClient();
 
 
 
tcp.Connect(IP,Port); //根據服務器的IP地址和偵聽的端口連接
 
if  (tcp.Connected)
 
{
 
//連接成功的消息機制  詳細見DEMO
 
ShowGetData( "成功連接上了服務器:" , this .strIP.Text.ToString());
 
  }

這里需要注意的是,不管是使用有參數的構造函數與服務器連接,或者是通過Connect()方法與服務器建立連接,都是同步方法(或者說是阻塞的,英文叫block)。它的意思是說,客戶端在與服務端連接成功、從而方法返回,或者是服務端不存、從而拋出異常之前,是無法繼續進行后繼操作的。這里還有一個名為BeginConnect()的方法,用於實施異步的連接,這樣程序不會被阻塞,可以立即執行后面的操作,這是因為可能由於網絡擁塞等問題,連接需要較長時間才能完成。網絡編程中有非常多的異步操作,凡事都是由簡入難,關於異步操作,我們后面再討論,現在只看同步操作。

(2)   建立連接服務端的數據流

NetworkStream streamToServer = tcp.GetStream();

(3)   接收和發送數據

//發送字符串
 
         byte [] buffer = Encoding.Unicode.GetBytes(msg); //msg為發送的字符串  
 
         try
 
           {
 
              lock  (streamToServer)
 
             {
 
             streamToServer.Write(buffer, 0, buffer.Length);     // 發往服務器
 
              }
 
 
 
           //接收字符串
 
                buffer = new  byte [BufferSize];
 
                lock  (streamToServer)
 
             {
 
                bytesRead = streamToServer.Read(buffer, 0, BufferSize);
 
             }
 
}
  1. 2.      異步 

        相對於同步,異步中的連接,接收和發送數據的方法都不一樣,都有一個回調函數,就是即使不能連接或者接收不到數據,程序還是會一直執行下去,如果連接上了或者接到數據,程序會回到這個回調函數的地方重新往下執行。詳細見下面:

服務器

1、 開啟偵聽接口

private  TcpListener listener;               //監聽類
 
listener = new  TcpListener( new  IPEndPoint(IPAddress.Parse(ip), port));
 
listener.Start(); //開啟偵聽,對連接的客戶端的數目沒有限制
 
或者
 
listener.Start( int  i); // 開啟偵聽,最多只能連接i個客戶端數目

2、 接收客戶端

                     listener.BeginAcceptSocket(clientConnect, listener); //異步接受客戶端的連接請求  clientConnect為連接的回調函數
 
 
/// <summary>
 
         /// 接收回調函數
 
         /// </summary>
 
         /// <param name="ar"></param>
 
         private  void  clientConnect(IAsyncResult ar)
 
         {
 
             try
 
             {
 
                 TcpListener listener = (TcpListener)ar.AsyncState;
 
                 //接受客戶的連接,得到連接的Socket
 
                 Socket client = listener.EndAcceptSocket(ar);
 
 
 
             }
 
             catch  { }
 
         }

3、 接收客戶端發送的數據

/// <summary>
 
         /// 異步接收數據
 
         /// </summary>
 
         private  void  receiveData(Socket client)
 
         {
 
                 // 調用異步方法 BeginReceive 來告知 socket 如何接收數據
 
                 IAsyncResult iar = client.BeginReceive(buffer, 0, BagSize, SocketFlags.None, out  errorCode, receiveCallback, buffer);
             }
      }
 
 
 
         /// <summary>
 
         /// 接收數據回調函數
 
         /// </summary>
 
         /// <param name="ar"></param>
 
         private  void  receiveCallback(IAsyncResult ar)
 
         {         
 
                 //接收到的數據長度.
 
                 int  receLen = 0;
 
                 try
 
                 {
 
                     receLen = client.EndReceive(ar, out  errorCode);               
 
if  (receLen > 0)
 
                     {
 
                         OnReceiveData(client); //接收到數據之后的處理函數
 
                     }
 
                 }
 
                 catch  {     }
             }
 
             else  { }
 
         }

 

4、接收成功之后,回發數據給客戶端

/// <summary>
 
         /// 異步發送報文
 
         /// </summary>
 
         /// <param name="data"></param>
 
         private  void  OnReceiveData (Socket socket)
 
         {
 
string  strLogin = “succeed recived”;
 
byte [] data = Encoding.ASCII.GetBytes(strLogin);
 
          socket.BeginSend(data, 0, data.Length, SocketFlags.None, out  errorCode, sendCallBack, socket); //異步發送數據
 
             }
 
             else
 
             { }
 
         }
 
/// <summary>
 
         /// 異步發送回調事件
 
         /// </summary>
 
         /// <param name="ar"></param>
 
         private  void  sendCallBack(IAsyncResult ar)
 
         {
 
socket.EndSend(ar, out  errorCode);
 
           }

客戶端 

1、連接服務器

private  TcpClient tcpcz = null
 
          tcpcz = new  TcpClient()
 
     tcpcz.BeginConnect(ipaddress, Convert.ToInt32(port), new  AsyncCallback(ConnectCallback), tcpcz); //根據服務器的IP地址和端口號 異步連接服務器
 
 
         /// <summary>
 
         /// 異步連接的回調函數
 
         /// </summary>
 
         /// <param name="ar"></param>
 
         private  void  ConnectCallback(IAsyncResult ar)
 
         {
 
             TcpClient t = (TcpClient)ar.AsyncState;
 
             try
 
             {
 
                 if  (t.Connected)
 
                 {
 
                     t.EndConnect(ar); //函數運行到這里就說明連接成功
 
                 }
 
                 else
 
                 {
 
                 }
 
             }
 
             catch  () {    }
         }

2、發送和接收字符串

              

NetworkStream stream = tcp.GetStream(); //創建於服務器連接的數據流
 
 
 
                   //發送字符串
 
                   string  strLogin = “ this  is  socket example”;
 
              byte [] data = Encoding.ASCII.GetBytes(strLogin);
 
           stream.BeginWrite(data, 0, data.Length, new  AsyncCallback(SendCallback),stream); //異步發送數據
 
                   //接收字符串
 
                 byte [] result = new  byte [tcp.Available]; // tcp.Available為接受的字符串大小
                   try
                   {
 
                       stream.BeginRead(result, 0, result.Length, new  AsyncCallback(ReadCallback), stream); //異步接受服務器回報的字符串
 
                   }
                   catch  { }
                   string  strResponse = Encoding.ASCII.GetString(result).Trim(); //從服務器接受到的字符串
 
               }
 
           }
           catch  ()
           {
           }
       }

以上是這一段時間對socket的一些心得,還在不斷學習中,如果上面的講解有什么不到位的或者錯誤的,可以交流一下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM