日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 综合教程 >内容正文

综合教程

C#版清晰易懂TCP通信原理解析(附demo)

發(fā)布時(shí)間:2023/12/13 综合教程 39 生活家
生活随笔 收集整理的這篇文章主要介紹了 C#版清晰易懂TCP通信原理解析(附demo) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

【轉(zhuǎn)】 C#版清晰易懂TCP通信原理解析(附demo)

(點(diǎn)擊上方藍(lán)字,可快速關(guān)注我們)

來(lái)源:周見(jiàn)智

cnblogs.com/xiaozhi_5638/p/4244797.html

對(duì).NET中網(wǎng)絡(luò)編程寫(xiě)得比較多,主要原因有兩個(gè),一是我公司做的項(xiàng)目多數(shù)跟通信這個(gè)有關(guān);二是研究Socket通信工作模式有益于對(duì)軟件架構(gòu)設(shè)計(jì)的理解,因?yàn)樗锩娴教幎际褂玫搅恕氨谩苯Y(jié)構(gòu),而這個(gè)結(jié)構(gòu)幾乎是所有框架、大型模塊所必需具備的。另外,工作之余寫(xiě)的一本書(shū)(即將要出版)中有一章專(zhuān)門(mén)講到了“泵”結(jié)構(gòu)在軟件系統(tǒng)中的作用。

這次寫(xiě)這篇文章主要是看了網(wǎng)上一個(gè)人提的有關(guān)TCP編程的問(wèn)題,所以就再次整理了一下這方面的知識(shí),并且做了一個(gè)“簡(jiǎn)易通信庫(kù)”發(fā)出來(lái)給大家看看,代碼很簡(jiǎn)單,功能也不是特別全,但是具備很好的擴(kuò)展性,基本上可以用來(lái)說(shuō)明.NET中TCP通信的工作模式。

TCP與UDP通信的特點(diǎn)

關(guān)于對(duì)這兩者的比較,網(wǎng)上一搜一大片,講得也比較清楚。TCP通信就像打電話,雙方通信之前需要建立連接、雙方就位后方可開(kāi)始會(huì)話;而UDP通信就像發(fā)短信,一方給另一方發(fā)送數(shù)據(jù)前,并不需要對(duì)方就位。

上面兩幅圖顯示了TCP與UDP通信過(guò)程建立的區(qū)別。

除了它們通信過(guò)程建立的不同之外,兩者還有以下區(qū)別:

TCP通信特點(diǎn)

1)可靠性;

通信雙方均就位,一方發(fā)送數(shù)據(jù),另一方收到后會(huì)做出回應(yīng),如果超時(shí)未發(fā)送成功,會(huì)自動(dòng)重發(fā),數(shù)據(jù)不會(huì)丟失。

2)順序性;

既然數(shù)據(jù)是按順序走在建立的一條隧道中,那么數(shù)據(jù)遵循“先走先達(dá)到”的規(guī)則,并且隧道中的數(shù)據(jù)以“流”的形式傳輸,發(fā)送方發(fā)送的前后兩次數(shù)據(jù)之間沒(méi)有邊界,需要接收方自己根據(jù)事先規(guī)定好的“協(xié)議”去判斷數(shù)據(jù)邊界。

3)高損耗。

“高損耗”包括機(jī)器性能損耗高、寬帶流量損耗高。因?yàn)橥ㄐ烹p方時(shí)刻需要維持著連接的存在,這必然會(huì)損耗通信雙方主機(jī)性能,要想維持隧道的通暢,通信雙方必須不斷地發(fā)送檢測(cè)包和應(yīng)答包,同時(shí),它還支持?jǐn)?shù)據(jù)重發(fā)等數(shù)據(jù)糾錯(cuò)功能,這些都將導(dǎo)致網(wǎng)絡(luò)流量的增加。

UDP通信特點(diǎn)

1)不可靠性;

既然無(wú)連接,發(fā)送方只管發(fā)送數(shù)據(jù),而不管對(duì)方是否能夠正確地接收到數(shù)據(jù),更不負(fù)責(zé)數(shù)據(jù)超時(shí)重發(fā)等功能。

2)無(wú)序性;

數(shù)據(jù)以“數(shù)據(jù)報(bào)”的形式發(fā)送,可以把“數(shù)據(jù)報(bào)”看成是一個(gè)“包”。如果把TCP傳輸數(shù)據(jù)比如成“河里的流水”,那么UDP傳輸數(shù)據(jù)就是‘郵局寄信’。發(fā)送方先發(fā)送的數(shù)據(jù)可能后到達(dá),后發(fā)送的數(shù)據(jù)可能先到達(dá),這個(gè)跟短消息類(lèi)似。

3)低損耗。

“低損耗”包括機(jī)器性能損耗低、寬帶流量損耗低。UDP通信不需要維持一個(gè)連接的存在,所以它不需要消耗額外的機(jī)器性能。同時(shí)它也沒(méi)有像TCP通信那樣為了保持隧道的通暢,而必須不停地發(fā)送檢測(cè)包和應(yīng)答包,更不會(huì)進(jìn)行一些數(shù)據(jù)檢測(cè)糾錯(cuò)、重發(fā)等行為。

這次我們只討論TCP通信。

TCP通信中的“沾包”現(xiàn)象

上面提到過(guò),TCP通信中,數(shù)據(jù)是以“流”的形式傳輸?shù)摹G耙淮伟l(fā)送的數(shù)據(jù)和后一次發(fā)送的數(shù)據(jù)之間并沒(méi)有明顯的界限,這就會(huì)出現(xiàn)一個(gè)問(wèn)題:當(dāng)你收到一部分?jǐn)?shù)據(jù)時(shí),你無(wú)法判斷接收到的數(shù)據(jù)是否是完整的?

如上圖,發(fā)送方發(fā)送三次數(shù)據(jù),而接收方可能一共分四次接收。并且每次接收到的數(shù)據(jù)量不確定(雖然每次收到的數(shù)據(jù)不確定,但是將四次接收到的數(shù)據(jù)拼接起來(lái),與發(fā)送時(shí)的一致)。這樣以來(lái),當(dāng)我們每次收到一份數(shù)據(jù)時(shí),我們無(wú)法輕易判斷(幾乎不能)收到的數(shù)據(jù)是否完整(是否可以正確地被處理)。

以上現(xiàn)象我們稱(chēng)之為“沾包”。TCP通信過(guò)程中,要想解決“沾包”問(wèn)題,我們必須人工采取一些措施,比如在發(fā)送數(shù)據(jù)時(shí)遵循一些“規(guī)則”,在接收到數(shù)據(jù)時(shí),再按照相同的“規(guī)則”去解析數(shù)據(jù),最終得到一份完整的數(shù)據(jù),并進(jìn)行正確的處理。沒(méi)錯(cuò),這里說(shuō)的“規(guī)則”便是我們通常聽(tīng)到的“協(xié)議”。

關(guān)于協(xié)議,講到的地方也很多。簡(jiǎn)單的說(shuō),協(xié)議就是一種“數(shù)據(jù)結(jié)構(gòu)”,合作雙方必須同時(shí)按照相同的數(shù)據(jù)結(jié)構(gòu)發(fā)送/接收數(shù)據(jù),比如傳輸層的TCP/UDP協(xié)議,又比如應(yīng)用層的HTTP/FTP等協(xié)議。B/S結(jié)構(gòu)系統(tǒng)使用到的協(xié)議見(jiàn)下圖:

在TCP通信中,在發(fā)送和接收數(shù)據(jù)的時(shí)候,如果我們遵循事先定義的一種“協(xié)議”(屬于一種應(yīng)用層協(xié)議)。比如,在發(fā)送數(shù)據(jù)時(shí),按照“數(shù)據(jù)頭(4Byte)+內(nèi)容長(zhǎng)度(4Byte)+內(nèi)容正文(NByte)+附加信息(8Byte)”這種形式去“格式化”需要發(fā)送的數(shù)據(jù);同理,在接收到數(shù)據(jù)后,按照這種形式去“反格式化”數(shù)據(jù),這樣我們便可以判斷數(shù)據(jù)邊界,輕松得到一條完整數(shù)據(jù)。

自定義應(yīng)用層協(xié)議

是的。我們自己完全可以定義一個(gè)類(lèi)似HTTP這樣的應(yīng)用層協(xié)議,只要你能力足夠強(qiáng),系統(tǒng)足夠大。今天在這里,我只舉個(gè)簡(jiǎn)單的例子,假設(shè)一個(gè)TCP通信系統(tǒng)中,客戶(hù)端連接上服務(wù)器后,客戶(hù)端向服務(wù)器發(fā)送一個(gè)字符串,并發(fā)送一個(gè)字符串轉(zhuǎn)換指令(比如大小寫(xiě)轉(zhuǎn)換、除去特殊字符等指令),服務(wù)器接收到數(shù)據(jù)后,按照對(duì)應(yīng)的指令,將字符串轉(zhuǎn)換后發(fā)送回給客戶(hù)端。那么這里的應(yīng)用層協(xié)議可以這樣設(shè)計(jì):

如上表所示,假設(shè)一共有四種字符串轉(zhuǎn)換請(qǐng)求,那么我們可以按下面圖設(shè)計(jì)應(yīng)用層協(xié)議的數(shù)據(jù)結(jié)構(gòu):

如上圖所示,開(kāi)頭一個(gè)字節(jié)代表字符串轉(zhuǎn)換指令類(lèi)型,后續(xù)四個(gè)字節(jié)存放一個(gè)Int32的整型數(shù)據(jù),表示字符串的長(zhǎng)度(字符串采用Unicode編碼),最后N個(gè)字節(jié)表示字符串內(nèi)容。數(shù)據(jù)發(fā)送方必須按照此協(xié)議格式發(fā)送數(shù)據(jù),數(shù)據(jù)接收方必須按照此協(xié)議格式接收數(shù)據(jù)。

發(fā)送數(shù)據(jù)時(shí)按照協(xié)議格式化數(shù)據(jù)很簡(jiǎn)單,但是,接收數(shù)據(jù)后,按照協(xié)議去解析數(shù)據(jù)該怎樣呢?事實(shí)上,這個(gè)相對(duì)來(lái)講稍微復(fù)雜一點(diǎn)。我們可以將每次接收到的數(shù)據(jù)(字節(jié)流)寫(xiě)入一個(gè)緩沖區(qū),然后判斷緩沖區(qū)中是否存在一條完整的數(shù)據(jù),如果存在,則處理這條完整的數(shù)據(jù);否則,繼續(xù)接收數(shù)據(jù),將接收到的數(shù)據(jù)再次寫(xiě)入緩沖區(qū)...以此循環(huán)。

TCPLibrary通信庫(kù)介紹

其實(shí)我只是將一些代碼單獨(dú)拿出來(lái)生成了一個(gè)dll,這部分代碼可以為我們搭建起TCP通信的框架,包括服務(wù)端偵聽(tīng)、(服務(wù)端/客戶(hù)端)接收數(shù)據(jù)、上下線、消息處理并通知Application以及“沾包”問(wèn)題處理等等。功能并不全面,如果要拿去實(shí)際項(xiàng)目中使用還需要自己完善,文章末會(huì)列出未完成的功能。

TCP通信過(guò)程建立之后,大概結(jié)構(gòu)如下:

整個(gè)通信庫(kù)中,只包含5個(gè)抽象類(lèi),以及5個(gè)默認(rèn)實(shí)現(xiàn)類(lèi)(所以說(shuō)簡(jiǎn)易):

使用該通信庫(kù)的前提是要定義好程序使用到的“協(xié)議”,然后重點(diǎn)實(shí)現(xiàn)ZMessage.RawData屬性和ZDataBuffer.TryReadMessage方法,前者可以按照協(xié)議格式化需要發(fā)送的數(shù)據(jù),后者可以按照協(xié)議解析一條完整的消息。庫(kù)中包含5個(gè)默認(rèn)實(shí)現(xiàn)類(lèi)(以Base開(kāi)頭的),它默認(rèn)使用以下的協(xié)議進(jìn)行通信:

其中,BaseDataBuffer.TryReadMessage方法具體實(shí)現(xiàn)為:

/// <summary>

/// 按照規(guī)定協(xié)議,重寫(xiě)TryReadMessage方法

/// </summary>

/// <returns></returns>

internal override ZMessage TryReadMessage()

{

if (_length >= 8) // 4 + 4 + N

{

using (MemoryStream ms = new MemoryStream(_buffer))

{

BinaryReader br = new BinaryReader(ms);

int msgtype = br.ReadInt32(); //讀取消息類(lèi)型

int msglength = br.ReadInt32(); //讀取消息長(zhǎng)度

if (_length - 8 >= msglength) //如果緩沖區(qū)中存在一條完整消息,則讀取

{

byte[] msgcontent = br.ReadBytes(msglength); //讀取消息內(nèi)容

BaseMessage bm = new BaseMessage(msgtype, msgcontent); //還原成一條完整的消息

Remove(8 + msglength); //注意! 移除已讀數(shù)據(jù)

return bm; //返回讀取到的消息

}

else

{

return null;

}

}

}

else

{

return null;

}

}

BaseMessage.RawData屬性具體的實(shí)現(xiàn)為:

/// <summary>

/// 按照規(guī)定協(xié)議,重寫(xiě)RawData屬性

/// </summary>

public override byte[] RawData

{

get

{

byte[] rawdata = new byte[4 + 4 + MsgContent.Length]; //消息類(lèi)型 + 消息長(zhǎng)度 + 消息內(nèi)容

using (MemoryStream ms = new MemoryStream(rawdata))

{

BinaryWriter bw = new BinaryWriter(ms);

bw.Write(MsgType); //先寫(xiě)入MsgType

bw.Write(MsgContent.Length); //再寫(xiě)入MsgContent的長(zhǎng)度

bw.Write(MsgContent); //最后寫(xiě)入消息內(nèi)容

return rawdata;

}

}

}

可以看到,上面一個(gè)按照協(xié)議格式化數(shù)據(jù),而另一個(gè)按照協(xié)議解析數(shù)據(jù)。它們兩個(gè)完全遵守同一個(gè)協(xié)議。

Demo演示

使用TCPLibrary中的默認(rèn)實(shí)現(xiàn)類(lèi)(以Base開(kāi)頭的類(lèi)型),我做了一個(gè)簡(jiǎn)單的Demo。該Demo可以完成字符串、可序列化對(duì)象(圖片)的發(fā)送與接收。Demo源碼很簡(jiǎn)單:

Server端初始化:

private void Form1_Load(object sender, EventArgs e)

{

_server = new BaseServerSocket();

_server.Connected += new ConnectedEventHandler(_server_Connected);

_server.DisConnected += new DisConnectedEventHandler(_server_DisConnected);

_server.MessageReceived += new MessageReceivedEventHandler(_server_MessageReceived);

_server.StartAccept(9100);

textBox1.AppendText("服¤t務(wù)?器??啟?動(dòng)?¥,ê?監(jiān)¨¤聽(tīng)?y端?口¨2 " + 9000 + "...
");

}

Client端的初始化:

private void Form1_Load(object sender, EventArgs e)

{

_client = new BaseClientSocket();

_client.Connected += new ConnectedEventHandler(_client_Connected);

_client.DisConnected += new DisConnectedEventHandler(_client_DisConnected);

_client.MessageReceived += new MessageReceivedEventHandler(_client_MessageReceived);

_client.Connect("127.0.0.1",9100);

}

可以看到,使用起來(lái)很簡(jiǎn)單。注冊(cè)事件后,既可以開(kāi)始運(yùn)行了。

下面可以看一下Demo截圖:

注意,這個(gè)Demo只是利用庫(kù)中的默認(rèn)實(shí)現(xiàn)類(lèi)來(lái)完成的。你完全可以自己定義一個(gè)協(xié)議,按照你自己的方式發(fā)送數(shù)據(jù),比如“頭(4Byte)+是否加密(1Byte)+發(fā)送方程序版本(8Byte)+消息長(zhǎng)度(4Byte)+消息內(nèi)容(NByte)+附加信息(8Byte)”這種方式發(fā)送數(shù)據(jù)/接收數(shù)據(jù)。只要你正確的實(shí)現(xiàn)了上面強(qiáng)調(diào)的方法和屬性。

源碼下載

下載地址:http://files.cnblogs.com/xiaozhi_5638/TCPDemo.rar

看完本文有收獲?請(qǐng)轉(zhuǎn)發(fā)分享給更多人

關(guān)注「DotNet」,提升.Net技能

紙上得來(lái)終覺(jué)淺,絕知此事要躬行。

總結(jié)

以上是生活随笔為你收集整理的C#版清晰易懂TCP通信原理解析(附demo)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。