C# 的TCPClient异步连接与异步读数据
Socket的TCP通訊
一、?socket的通訊原理
服務器端的步驟如下。
(1)建立服務器端的Socket,開始偵聽整個網絡中的連接請求。
(2)當檢測到來自客戶端的連接請求時,向客戶端發送收到連接請求的信息,并建立與客戶端之間的連接。
(3)當完成通信后,服務器關閉與客戶端的Socket連接。
客戶端的步驟如下。
(1)建立客戶端的Socket,確定要連接的服務器的主機名和端口。
(2)發送連接請求到服務器,并等待服務器的回饋信息。
(3)連接成功后,與服務器進行數據的交互。
(4)數據處理完畢后,關閉自身的Socket連接。
二、?socket的通訊方式
socket通訊方式有兩種:同步和異步
同步工作方式:
用TCP協議進行編程時程序執行到發送、接收和監聽語句的時候,在未完成工作前不再繼續往下執行,即處于阻塞狀態,直到該語句完成某個工作后才繼續執行下一條語句。
異步工作方式
程序執行到發送、接收和監聽語句的時候,不論工作是否完成,都會繼續往下執行。
三、?socket的C#實現
服務端客戶端通信
在與服務端的連接建立以后,我們就可以通過此連接來發送和接收數據。端口與端口之間以流(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、 開啟偵聽接口
| 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的一些心得,還在不斷學習中,如果上面的講解有什么不到位的或者錯誤的,可以交流一下。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的C# 的TCPClient异步连接与异步读数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 发动机的工作原理,你知道吗?
- 下一篇: 广州学车科目三路考操作步骤要领