自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

動(dòng)手學(xué)習(xí)TCP系列之TCP連接建立與終止

網(wǎng)絡(luò) 網(wǎng)絡(luò)管理
TCP是一個(gè)面向連接的協(xié)議,任何一方在發(fā)送數(shù)據(jù)之前,都必須先在雙方之間建立一條連接。所以,本文就主要看看TCP連接的建立和終止。

TCP是一個(gè)面向連接的協(xié)議,任何一方在發(fā)送數(shù)據(jù)之前,都必須先在雙方之間建立一條連接。所以,本文就主要看看TCP連接的建立和終止。

在開(kāi)始介紹TCP連接之前,先來(lái)看看TCP數(shù)據(jù)包的首部,首部里面有很多重要的字段,在我們實(shí)現(xiàn)程序的時(shí)候需要進(jìn)行設(shè)置。

TCP的首部

在OSI七層模型中,上層的數(shù)據(jù)包都會(huì)作為下層數(shù)據(jù)包的數(shù)據(jù)部分(payload)。

也就是說(shuō),當(dāng)構(gòu)造TCP數(shù)據(jù)包的時(shí)候,會(huì)把應(yīng)用層的數(shù)據(jù)包作為T(mén)CP包的數(shù)據(jù)部分,然后加上TCP頭構(gòu)成TCP數(shù)據(jù)包;同樣,當(dāng)構(gòu)造IP數(shù)據(jù)包的時(shí)候,整個(gè)TCP包就會(huì)被當(dāng)作數(shù)據(jù)部分,然后加上IP頭構(gòu)成IP數(shù)據(jù)包。

 

TCP頭的數(shù)據(jù)格式如下,在不包括可選字段的情況下,一般TCP頭會(huì)占用20個(gè)字節(jié)。

 

在TCP首部中,有幾個(gè)字段是需要關(guān)注一下:

在TCP首部中沒(méi)有源和目標(biāo)的IP、MAC地址(IP和MAC地址分別是網(wǎng)絡(luò)層和鏈路層首部的信息),只有源和目標(biāo)的端口

Sequence Number是包的序號(hào),網(wǎng)絡(luò)層(IP層)的傳輸是不可靠的,可能產(chǎn)生包亂序,所以這個(gè)需要可以解決網(wǎng)絡(luò)包亂序的問(wèn)題

Acknowledgement Number用來(lái)確認(rèn)收到數(shù)據(jù)包的確認(rèn)序號(hào),為T(mén)CP的傳輸提供了可靠性保證

TCP Flags包括了8個(gè)bit,通過(guò)對(duì)這些bit的設(shè)置,可以代表不同類型的TCP數(shù)據(jù)包

下面就看看TCP連接的建立和終止。

TCP連接建立

TCP連接建立的過(guò)程被稱為三次握手過(guò)程:

連接建立發(fā)起端發(fā)送[SYN]包,該端將主動(dòng)打開(kāi)(active open)

接收端將發(fā)送[SYN, ACK]包,該端將被動(dòng)打開(kāi)(passive open),ACK標(biāo)志表示對(duì)收到的[SYN]包的確認(rèn)

連接建立發(fā)起端發(fā)送[ACK]包確認(rèn)[SYN, ACK]包

 

Initial Sequence Number

連接建立過(guò)程中,一個(gè)重要的工作就是初始化Sequence Number,通信的雙方在建立連接的過(guò)程中互相通知對(duì)方自己的初始Sequence Number(ISN:Initial Sequence Number)。ISN不是固定的,ISN跟時(shí)鐘綁定,根據(jù)特定的間隔自增,直到超過(guò)2^32,又從0開(kāi)始。

SYN全稱就是Synchronize Sequence Number,通過(guò)seq序號(hào),TCP就可以保證數(shù)據(jù)包的順序;通過(guò)ack序號(hào),TCP就有了可靠性。

連接建立注意點(diǎn)

在建立TCP連接的過(guò)程中,有以下兩點(diǎn)需要注意一下:

[SYN]標(biāo)志的數(shù)據(jù)包會(huì)使用消耗一個(gè)序號(hào),所以對(duì)端的確認(rèn)號(hào)(ack)是當(dāng)前序號(hào)(seq)加一

當(dāng)被動(dòng)打開(kāi)端發(fā)送[ACK]確認(rèn)包的時(shí)候,同時(shí)設(shè)置了[SYN]標(biāo)志,所以TCP連接建立的過(guò)程只需要三次握手,而不是四次

TCP連接終止

TCP連接終止的過(guò)程被稱為四次揮手過(guò)程,以下圖為例:

連接終止端(client)發(fā)送[FIN, ACK] 包,關(guān)閉client到server方向的數(shù)據(jù)發(fā)送通路

server端發(fā)送[ACK]包來(lái)確認(rèn)來(lái)自client的[FIN, ACK] 包

server端發(fā)送[FIN, ACK] 包,關(guān)閉server到client方向的數(shù)據(jù)發(fā)送通路

client端發(fā)送[ACK]包來(lái)確認(rèn)來(lái)自server的[FIN, ACK] 包,到此TCP連接關(guān)閉

 

#p#

連接終止注意點(diǎn)

在建立TCP連接的過(guò)程中,有以下兩點(diǎn)需要注意一下:

[FIN]標(biāo)志的數(shù)據(jù)包會(huì)使用消耗一個(gè)序號(hào),所以對(duì)端的確認(rèn)號(hào)(ack)是當(dāng)前序號(hào)(seq)加一

與建立連接時(shí)的三次 握手不同,終止連接需要四次揮手

因?yàn)門(mén)CP連接是全雙工的,每個(gè)方向都必須單獨(dú)進(jìn)行關(guān)閉。當(dāng)一方完成它的數(shù)據(jù)發(fā)送任務(wù)后就能發(fā)送一個(gè)FIN來(lái)終止這個(gè)方向的連接。收到一個(gè) FIN只意味著這一方向上沒(méi)有數(shù)據(jù)流動(dòng),但是TCP連接在收到一個(gè)FIN后仍能發(fā)送數(shù)據(jù)

TCP連接實(shí)驗(yàn)

好了,了解了TCP連接建立和終止的基本知識(shí)后,就可以通過(guò)Pcap.Net來(lái)進(jìn)行TCP連接建立和終止的實(shí)驗(yàn)了。

建立連接代碼的基本流程如下:

client程序使用一個(gè)初始的seq序號(hào)(100),然后生成并發(fā)送一個(gè)帶[SYN]標(biāo)志的TCP包

client將期待來(lái)自服務(wù)端的[SYN, ACK]包

當(dāng)收到[SYN, ACK]包之后,client需要生成并發(fā)送一個(gè)[ACK]包進(jìn)行確認(rèn),這個(gè)[ACK]包的ack號(hào)是[SYN, ACK]包seq號(hào)加一

終止連接代碼的基本流程如下:

client程序delay 10秒鐘,然后發(fā)送[FIN, ACK]包關(guān)閉client到server的通路,繼續(xù)使用全局的seq號(hào)

client將期待來(lái)自服務(wù)端的[ACK]包,以及[FIN, ACK]包

***client發(fā)送[ACK]包,seq號(hào)需要加一,因?yàn)閇FIN]標(biāo)志的包將消耗一個(gè)序號(hào),TCP連接終止完成

主程序如下,發(fā)送TCP連接建立和終止請(qǐng)求,每個(gè)請(qǐng)求發(fā)送后都用PacketHandler處理收到的包:

communicator.SendPacket(Utils.BuildTcpPacket(endPointInfo, TcpControlBits.Synchronize, null));
PacketHandler(communicator, endPointInfo);
// delay 10 secs, then client to send Fin
Thread.Sleep(10000);
communicator.SendPacket(Utils.BuildTcpPacket(endPointInfo, TcpControlBits.Fin | TcpControlBits.Acknowledgment));
PacketHandler(communicator, endPointInfo);

程序的主要邏輯在PacketHandler中,這個(gè)函數(shù)根據(jù)收到的不同TCP包的類型完成不同的邏輯,產(chǎn)生并發(fā)送不同類型的包。

例如,當(dāng)PacketHandler接收到來(lái)自服務(wù)端的[SYN, ACK]包后,處理函數(shù)就會(huì)生成并發(fā)送一個(gè)[ACK]確認(rèn)包。也就是說(shuō),PacketHandler的邏輯就是實(shí)現(xiàn)了TCP連接建立和終止的邏輯。

private static void PacketHandler(PacketCommunicator communicator, EndPointInfo endPointInfo)
{
Packet packet = null;
bool running = true;
do
{
PacketCommunicatorReceiveResult result = communicator.ReceivePacket(out packet);
switch (result)
{
case PacketCommunicatorReceiveResult.Timeout:
// Timeout elapsed
continue;
case PacketCommunicatorReceiveResult.Ok:
bool isRecvedPacket = (packet.Ethernet.IpV4.Destination.ToString() == endPointInfo.SourceIp) ? true : false;
if (isRecvedPacket)
{
switch (packet.Ethernet.IpV4.Tcp.ControlBits)
{
case (TcpControlBits.Synchronize | TcpControlBits.Acknowledgment):
Utils.PacketInfoPrinter(packet);
Packet ack4SynAck = Utils.BuildTcpResponsePacket(packet, TcpControlBits.Acknowledgment);
communicator.SendPacket(ack4SynAck);
break;
case (TcpControlBits.Fin | TcpControlBits.Acknowledgment):
Utils.PacketInfoPrinter(packet);
Packet ack4FinAck = Utils.BuildTcpResponsePacket(packet, TcpControlBits.Acknowledgment);
communicator.SendPacket(ack4FinAck);
break;
case TcpControlBits.Acknowledgment:
Utils.PacketInfoPrinter(packet);
break;
default:
Utils.PacketInfoPrinter(packet);
break;
}
}
else
{
switch (packet.Ethernet.IpV4.Tcp.ControlBits)
{
case (TcpControlBits.Fin | TcpControlBits.Acknowledgment):
Utils.PacketInfoPrinter(packet);
break;
case TcpControlBits.Synchronize:
Utils.PacketInfoPrinter(packet);
break;
case TcpControlBits.Acknowledgment:
Utils.PacketInfoPrinter(packet);
running = false;
break;
default:
Utils.PacketInfoPrinter(packet);
break;
}
}
break;
default:
throw new InvalidOperationException("The result " + result + " should never be reached here");
}
} while (running);
}

#p#

在PacketHandler函數(shù)中用到了BuildTcpResponsePacket這個(gè)函數(shù),這個(gè)函數(shù)根據(jù)收到的TCP包,來(lái)構(gòu)建response包。

這個(gè)函數(shù)有下面幾個(gè)注意點(diǎn):

該函數(shù)會(huì)根據(jù)收到的包,設(shè)置response包的源和目的地址

該函數(shù)會(huì)接受PacketHandler傳遞來(lái)的TCP flags,并設(shè)置到TCP首部中

該函數(shù)的另一個(gè)重要部分就是會(huì)計(jì)算并設(shè)置TCP首部中的seq好ack號(hào),這一點(diǎn)很重要

public static Packet BuildTcpResponsePacket(Packet packet, TcpControlBits tcpControlBits)
{
EthernetLayer ethernetHeader = new EthernetLayer
{
Source = new MacAddress(packet.Ethernet.Destination.ToString()),
Destination = new MacAddress(packet.Ethernet.Source.ToString()),
EtherType = EthernetType.None, // Will be filled automatically.
};
IpV4Layer ipHeader = new IpV4Layer
{
Source = new IpV4Address(packet.Ethernet.IpV4.Destination.ToString()),
CurrentDestination = new IpV4Address(packet.Ethernet.IpV4.Source.ToString()),
Fragmentation = IpV4Fragmentation.None,
HeaderChecksum = null, // Will be filled automatically.
Identification = 123,
Options = IpV4Options.None,
Protocol = null, // Will be filled automatically.
Ttl = 100,
TypeOfService = 0,
};
TcpLayer tcpHeader = new TcpLayer
{
SourcePort = packet.Ethernet.IpV4.Tcp.DestinationPort,
DestinationPort = packet.Ethernet.IpV4.Tcp.SourcePort,
Checksum = null, // Will be filled automatically.
SequenceNumber = seqNum = packet.Ethernet.IpV4.Tcp.AcknowledgmentNumber,
AcknowledgmentNumber = ackNum = packet.Ethernet.IpV4.Tcp.SequenceNumber + (uint)((packet.Ethernet.IpV4.Tcp.Payload.Length > 0) ? packet.Ethernet.IpV4.Tcp.Payload.Length : 1),
ControlBits = tcpControlBits,
Window = windowSize,
UrgentPointer = 0,
Options = TcpOptions.None,
};
PacketBuilder builder = new PacketBuilder(ethernetHeader, ipHeader, tcpHeader);
return builder.Build(DateTime.Now);
}

運(yùn)行效果

打開(kāi)Wireshark監(jiān)聽(tīng)"VirtualBox Host-Only Network"網(wǎng)卡,并設(shè)置filter為"port 8081"。

然后運(yùn)行程序,通過(guò)console可以看到客戶端發(fā)送的包,以及服務(wù)端返回的包,通過(guò)這些包完成了TCP連接的建立和終止。

 

下面是Wireshark中顯示的結(jié)果,Wireshark比較友好,會(huì)顯示相對(duì)seq號(hào),所以看到的都是從0開(kāi)始編號(hào)。

注意seq號(hào)和ack號(hào)的變化,[SYN]和[FIN]標(biāo)志的TCP包都會(huì)消耗一個(gè)序號(hào)。

 

總結(jié)

本文介紹了TCP首部,通過(guò)設(shè)置TCP首部中的[SYN]標(biāo)志,可以構(gòu)造TCP連接建立請(qǐng)求包;通過(guò)設(shè)置[FIN]標(biāo)志,可以構(gòu)造TCP連接終止請(qǐng)求包。

文中使用Pcap.Net構(gòu)建了一個(gè)簡(jiǎn)單的客戶端,完成了向服務(wù)器建立(三次握手)和終止(四次揮手)連接的過(guò)程。

通過(guò)這個(gè)實(shí)驗(yàn),一定會(huì)對(duì)TCP連接的建立和終止有一個(gè)比較直觀的認(rèn)識(shí)。

責(zé)任編輯:何妍 來(lái)源: 博客園
相關(guān)推薦

2015-10-13 15:09:31

2015-10-08 14:03:01

TCP網(wǎng)絡(luò)協(xié)議

2015-10-14 09:44:55

TCP網(wǎng)絡(luò)協(xié)議數(shù)據(jù)傳輸

2015-10-15 09:38:48

TCP網(wǎng)絡(luò)協(xié)議定時(shí)器

2015-10-12 08:33:06

TCP網(wǎng)絡(luò)協(xié)議服務(wù)端

2015-10-10 09:51:51

TCP網(wǎng)絡(luò)協(xié)議客戶端

2023-03-10 14:50:34

TCP 連接網(wǎng)絡(luò)通信

2019-09-23 08:27:15

TCP長(zhǎng)連接心跳

2015-04-23 18:46:38

TCPTCP協(xié)議

2021-03-17 09:51:31

網(wǎng)絡(luò)編程TCP網(wǎng)絡(luò)協(xié)議

2015-03-25 12:09:18

TCP網(wǎng)絡(luò)協(xié)議TCP建立連接

2019-12-26 09:28:34

TCPPython通信

2011-06-27 10:15:22

Qt 網(wǎng)絡(luò) TCP

2011-06-27 10:28:45

Qt 網(wǎng)絡(luò) TCP

2019-09-02 10:39:15

TCPWindows連接

2010-01-21 11:19:44

TCP Socketlinux

2015-11-09 09:58:56

2010-07-01 16:38:18

Linux TCP I

2010-06-13 15:37:24

TCP協(xié)議

2021-02-18 22:18:50

TCP 服務(wù)器源碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)