当前位置:网站首页>TCP waves twice, have you seen it? What about four handshakes?
TCP waves twice, have you seen it? What about four handshakes?
2022-07-04 19:37:00 【Linux server development】
We all know ,TCP It is a connection oriented 、 reliable 、 Transport layer communication protocol based on byte stream .
What's mentioned here " Connection oriented ", It means the need for Establishing a connection , Use connections , Release the connection .
Establishing a connection is what we know TCP Three handshakes .
Instead of using connections , Is sent through a 、 A form of confirmation , Data transfer .
There is also the release of connections , That's what we're used to TCP Four waves .
TCP You should know more about the four waves , But have you seen three waves ? There are still two waves ?
Have seen ? What about the four handshakes ?
Today's topic , I don't want to be just curious , I don't want to engage in cold knowledge .
Let's start with four waves , Get some practical knowledge .
TCP Four waves
In a brief review TCP Four waves .
Under normal circumstances . As long as the data transmission is over , Whether it's the client or the server , Can take the initiative to wave four times , Release the connection .
Just like the picture , hypothesis , The four waves were initiated by the client , Then it is the active party . The server passively receives the wave request from the client , Called the passive party .
Client and server , In limine , It's all in ESTABLISHED state .
First wave : In general , The active party performs close() or shutdown() Method , Will send a FIN Message comes out , Express " I don't send data anymore ".
Second wave : After receiving the request from the active party FIN After the message , The passive side immediately responds to a ACK, intend " I received your FIN 了 , I know you don't send data anymore ".
The above mentioned is that the active party no longer sends data . But if at this time , The passive party still has data to send , Then keep sending . Be careful , Although between the second and third wave , The passive side can send data to the active side , But it is not certain whether the active party can receive it normally , This will be said later .
Third wave : After the passive side senses the second wave , Will do a series of finishing work , Finally, a close(), At this time, there will be a third wave FIN-ACK.
Fourth wave : The active party returns a ACK, It means received .
The first wave and the third wave , It's all triggered by our initiative in the application ( For example, call close() Method ), That is what we need to pay attention to when writing code .
Second and fourth wave , The kernel protocol stack automatically helps us complete , We can't touch this place when we write code , So you don't need to care too much .
In addition, whether active or passive , Each side sent out a FIN And a ACK . Also received a FIN And a ACK . Let's pay attention to this , I'll mention later .
FIN Be sure to execute the program close() or shutdown() Can it be sent out ?
not always . In general , Through to socket perform close() or shutdown() Method will emit FIN. But actually , As long as the application exits , Whether it is active exit , Or passive exit ( For some inexplicable reason kill 了 ), Will send out FIN.
FIN Refer to " I no longer send data ", therefore shutdown() Turn off reading and don't send to each other FIN, Turn off writing before sending FIN.
If on the machine FIN-WAIT-2 There are many states , What is it for?
According to the four waves above , It can be seen that ,FIN-WAIT-2 It's the status of the active side .
Programs in this state , Waiting for the third wave FIN. The third wave needs to be executed by the passive party in the code close() issue .
So when the machine FIN-WAIT-2 There are many states , Well, generally speaking , There will be a lot of... On another machine CLOSE_WAIT. There are a lot of CLOSE_WAIT The machine that you're using , Why are you reluctant to call close() Close the connection .
therefore , If on the machine FIN-WAIT-2 There are many states , Generally, it is because the opposite end does not execute close() Method send out the third wave .
The active party is close Data received later , What will happen to
An article I wrote before 《 Code execution send After success , Is the data sent out ?》 in , From the perspective of source code , In general , The program takes the initiative to execute close() When ;
If the current connection corresponds to socket The receive buffer of has data , Will send RST.
If there is data in the send buffer , That will wait for sending , Send the first wave again FIN.
As we all know ,TCP It's full duplex communication , It means sending data at the same time , It can also receive data .
Close() The meaning is , At this time, the functions of sending and receiving messages should be turned off at the same time .
in other words , Although theoretically , Between the second and third wave , The passive side can transmit data to the active side .
But if The four waves of the active side are through close() The trigger , The active party will not receive this message . And there will be another RST. Just end the connection .
【 Article Welfare 】 In addition, Xiaobian also sorted out some C++ Back-end development interview questions , Teaching video , Back end learning roadmap for free , You can add what you need : Click to join the learning exchange group ~ Group file sharing
Xiaobian strongly recommends C++ Back end development free learning address :C/C++Linux Server development senior architect /C++ Background development architect https://ke.qq.com/course/417774?flowToken=1013189
Between the second and third wave , Can't transfer data ?
Neither . Mentioned earlier Close() The meaning is , Turn off the function of sending and receiving messages at the same time .
If you can only turn off sending messages , Do not turn off the function of receiving messages , Then you can continue to receive messages . such half-close The function of , By calling shutdown() Method can do .
int shutdown(int sock, int howto);
among howto The mode is disconnected . There are the following values :
SHUT_RD: Turn off reading . At this time, the application layer should no longer try to receive data , In the kernel protocol stack, even if the receive buffer receives data, it will be discarded .
SHUT_WR: Turn off write . If there is still data in the send buffer , The data is passed to the target host .
SHUT_RDWR: Turn off reading and writing . amount to close() 了 .
How do you know the opposite end socket Yes close still shutdown
No matter what the active shutdown party calls close() still shutdown(), For the passive side , Only one received FIN.
The passive closing party is confused ," How do I know if the other party wants me to continue sending data ?"
Actually , There's no need to get tangled up , Send it when it's time .
Between the second wave and the third wave , If the passive shutdown party wants to send data , So at the code level , It's the execution of send() Method .
int send( SOCKET s,const char* buf,int len,int flags);
send() The data will be copied to the local send buffer . If there is no problem with the send buffer , Can be copied in , So normally ,send() Generally, it will return success .
![tcp_sendmsg Logic ](https://cdn.jsdelivr.net/gh/xiaobaiTech/image/tcp_sendmsg Logic .png)
Then the passive side kernel protocol stack will send the data to the active shutdown side .
If the last active shutdown party called shutdown(socket_fd, SHUT_WR). At this time , The active shutdown party will no longer send messages , But it can receive messages from the passive side , As usual , All's well that ends well .
If the last active shutdown party called close(). The active party will directly discard the data received from the passive party , Then go back to one RST.
For the second case .
The passive side kernel protocol stack received RST, Will close the connection . But the kernel connection is closed , The application layer doesn't know ( Unless notified ).
At this point, the next operations of the passive application layer , Nothing more than reading or writing .
If it's reading , Will return RST The error of , That's what we're used to Connection reset by peer.
If it is written , Then the program will produce SIGPIPE The signal , Application layer code can capture and process signals , If you don't deal with , By default, the process will terminate , Abnormal exit .
To sum up , When the passive closing side recv() return EOF when , Explain that the active party passes close() or shutdown(fd, SHUT_WR) Launched the first wave .
If the passive party executes twice send().
for the first time send(), It usually returns successfully .
The second time send() when . If the initiative is through shutdown(fd, SHUT_WR) The first wave initiated , At this time send() Will still succeed . If the active party passes close() The first wave initiated , Then there will be SIGPIPE The signal , The process terminates by default , Abnormal exit . If you don't want to quit abnormally , Remember to capture and process this signal .
If the passive side doesn't wave for the third time , What will happen?
Third wave , It is triggered by the passive party , For example, call close().
If it's due to a code error or some other reason , The passive side just doesn't perform the third wave .
Now , The active party will use... When waving for the first time close() still shutdown(fd, SHUT_WR) , There are different behaviors .
If it is shutdown(fd, SHUT_WR) , It indicates that the active party actually only turns off write , But you can also read , At this time, it will always be in FIN-WAIT-2, Die waiting for the third wave of the passive side .
If it is close(), It means that the active party's reading and writing are turned off , At this time, you will be in FIN-WAIT-2 A span , This time is by net.ipv4.tcp_fin_timeout control , It's usually 60s, This value coincides with 2MSL equally . After that time , The state does not become `TIME-WAIT`, It's directly becoming `CLOSED`.
# cat /proc/sys/net/ipv4/tcp_fin_timeout 60
TCP Three waves
After four waves , Is it possible to wave three times ?
it is possible that .
We know ,TCP Wave your hand four times , Between the second and third wave , It is possible to have data transmission . The purpose of the third wave is to tell the active party ," The passive party has no data to send ".
therefore , After the first wave , If the passive party has no data, send it to the active party . It is possible to combine the second and third waves . So there are three waves .
If you have data to send , Can't it be three waves
The situation mentioned above is that there is no data to send , If second 、 There is data to send between the third wave , Can't it become three waves ?
Not at all .TCP There is also a feature called delayed acknowledgement . It can be simply understood as : The receiver does not need to reply immediately after receiving the data ACK Confirmation package .
On this basis , Not every time you send a packet, you can receive one ACK Confirmation package , Because the receiver can merge confirmation .
And this merger is confirmed , Put it in your hand four times , You can wave your hand a second time 、 Third wave , And the data transmission between them are combined to send . So there are three waves .
TCP Two waves
Mentioned in the previous four waves , When it was closed, both sides sent a FIN And received a ACK.
Under normal circumstances TCP Both ends of the connection , It's different IP+ Port process .
But if TCP Both ends of the connection ,IP+ When the port is the same , So when you close the connection , It's also done. One end sends out a FIN, Also received a ACK, It's just that these two ends are actually the same socket .
And this kind of two ends IP+ The ports are all the same , It's called TCP Self join .
Yes , You read that right , I didn't type wrong, either . The same socket You can really connect yourself , Form a connection .
One socket Can establish a connection ?
It's mentioned above that , Same client socket, Initiate a connection request to yourself . The connection can be established successfully . Such a connection , It's called TCP Self join .
Let's try to reproduce .
Note that I did the experiment on the following system . stay mac Most of them can't be reproduced .
# cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
adopt nc The command can simply create a TCP Self join
# nc -p 6666 127.0.0.1 6666
above -p You can specify the source port number . That is, a port number is specified as 6666 To connect to 127.0.0.1:6666 .
# netstat -nt | grep 6666
tcp 0 0 127.0.0.1:6666 127.0.0.1:6666 ESTABLISHED
The whole process , There is no server involved . You can grab a bag and have a look .
You can see , same socket, When you connect yourself , The handshake is three times . Waving is twice .
In the picture above , Both sides are the same client , Drawing it into two is to facilitate everyone to understand the transfer of state .
We can compare the handshake state of self connection with that under normal conditions TCP Three handshakes .
Look at the state diagram of self connection , Let's look at the following questions .
【 Article Welfare 】 In addition, Xiaobian also sorted out some C++ Back-end development interview questions , Teaching video , Back end learning roadmap for free , You can add what you need : Click to join the learning exchange group ~ Group file sharing
Xiaobian strongly recommends C++ Back end development free learning address :C/C++Linux Server development senior architect /C++ Background development architect https://ke.qq.com/course/417774?flowToken=1013189
After the first handshake from one end , If you receive the first handshake SYN package ,TCP How the connection state will change ?
After the first handshake , The connection state becomes SYN_SENT state . If you receive the first handshake at this time SYN package , Then the connection status will change from SYN_SENT The state becomes SYN_RCVD.
// net/ipv4/tcp_input.c
static int tcp_rcv_synsent_state_process()
{
// SYN_SENT State, , received SYN package
if (th->syn) {
// The state is set to SYN_RCVD
tcp_set_state(sk, TCP_SYN_RECV);
}
}
After a second handshake from one end , If you receive a second handshake SYN+ACK package ,TCP How the connection state will change ?
Second, after shaking hands , The connection state changes to SYN_RCVD 了 , At this point, if you receive a second handshake SYN+ACK package . The connection status will change to ESTABLISHED.
// net/ipv4/tcp_input.c
int tcp_rcv_state_process()
{
// Omit a lot of logic from the front , If you can get here, you think there must be ACK
if (true) {
// Judge this ack Is it legal
int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH | FLAG_UPDATE_TS_RECENT) > 0;
switch (sk->sk_state) {
case TCP_SYN_RECV:
if (acceptable) {
// Status from SYN_RCVD To ESTABLISHED
tcp_set_state(sk, TCP_ESTABLISHED);
}
}
}
}
After the first wave at one end , Received the bag waving for the first time ,TCP How the connection state will change ?
After the first wave , The state at one end will become FIN-WAIT-1. Under normal circumstances , Waiting for the second wave ACK. But in fact, I waited A first wave FIN package , At this time, the connection state will change to CLOSING.
// net/
static void tcp_fin(struct sock *sk)
{
switch (sk->sk_state) {
case TCP_FIN_WAIT1:
tcp_send_ack(sk);
// FIN-WAIT-1 State, , received FIN, To CLOSING
tcp_set_state(sk, TCP_CLOSING);
break;
}
}
This can be said to be a hidden plot .
CLOSING see little of , Except when the self connection is closed , It usually appears in TCP When both ends close the connection at the same time .
be in CLOSING When in state , Just get another ACK, You can get into TIME-WAIT state , And then wait for 2MSL, The connection is completely disconnected . This is a little different from the normal four waves . You can slide to the beginning of the article TCP Wave four times and compare .
Code recurrence from connection
Maybe people will doubt , Is this nc The software itself bug.
Then we can try strace Look what's done inside it .
# strace nc -p 6666 127.0.0.1 6666
// ...
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(6666), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
connect(3, {sa_family=AF_INET, sin_port=htons(6666), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)
// ...
Nothing more than to create a client socket Handle , Then execute... On this handle bind, The port number that binds it is 6666, And then 127.0.0.1:6666 launch connect Method .
We can try to use C Language to reproduce again .
The following code , Only for reproducing problems . Skipping directly does not affect reading at all .
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
int main()
{
int lfd, cfd;
struct sockaddr_in serv_addr, clie_addr;
socklen_t clie_addr_len;
char buf[BUFSIZ];
int n = 0, i = 0, ret = 0 ;
printf("This is a client \n");
/*Step 1: Create client side socket The descriptor cfd*/
cfd = socket(AF_INET, SOCK_STREAM, 0);
if(cfd == -1)
{
perror("socket error");
exit(1);
}
int flag=1,len=sizeof(int);
if( setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1)
{
perror("setsockopt");
exit(1);
}
bzero(&clie_addr, sizeof(clie_addr));
clie_addr.sin_family = AF_INET;
clie_addr.sin_port = htons(6666);
inet_pton(AF_INET,"127.0.0.1", &clie_addr.sin_addr.s_addr);
/*Step 2: Client side usage bind Binding client's IP And port */
ret = bind(cfd, (struct sockaddr* )&clie_addr, sizeof(clie_addr));
if(ret != 0)
{
perror("bind error");
exit(2);
}
/*Step 3: connect Link server side IP And port number */
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(6666);
inet_pton(AF_INET,"127.0.0.1", &serv_addr.sin_addr.s_addr);
ret = connect(cfd,(struct sockaddr *)&serv_addr, sizeof(serv_addr));
if(ret != 0)
{
perror("connect error");
exit(3);
}
/*Step 4: Write data to the server */
while(1)
{
fgets(buf, sizeof(buf), stdin);
write(cfd, buf, strlen(buf));
n = read(cfd, buf, sizeof(buf));
write(STDOUT_FILENO, buf, n);// Write on the screen
}
/*Step 5: close socket The descriptor */
close(cfd);
return 0;
}
Save as client.c file , Then execute the following command , You will find that the connection is successful .
# gcc client.c -o client && ./client
This is a client
# netstat -nt | grep 6666
tcp 0 0 127.0.0.1:6666 127.0.0.1:6666 ESTABLISHED
explain , This is not nc Of bug. in fact , This is also a case allowed by the kernel .
Self connected solutions
Self connection is generally less common , But it's not difficult to solve .
The solution is simple , As long as you can ensure that the ports of the client and server are inconsistent .
in fact , When we write code, we usually don't specify the port of the client , The system will randomly assign a range of ports to the client . And this range , You can query through the following command
# cat /proc/sys/net/ipv4/ip_local_port_range
32768 60999
That is, as long as our server port is not 32768-60999 In this range , For example, set to 8888. You can avoid this problem .
Another solution , You can refer to golang Implementation of standard network library , After the connection is established, judge IP Whether the and port are consistent , If you encounter self connection , Then disconnect and try again .
Four handshakes
aforementioned TCP Self connection is a scenario where the client connects itself . Can different clients be interconnected ?
The answer is yes , There is a situation called TCP Open at the same time .
You can compare ,TCP At the same time, open the state change when shaking hands , Follow TCP Self connection is very much like .
such as SYN_SENT State, , I got another one SYN, In fact, it is equivalent to self connection , After the first handshake , Received the first handshake request again . The result is to become SYN_RCVD.
stay SYN_RCVD Received in status SYN+ACK, It's equivalent to self connection , After the second handshake , Received a second handshake request , The result is to become ESTABLISHED. Their source code is actually the same piece of logic .
Reappear TCP Open at the same time
Under two consoles , Execute the following two lines respectively .
The meaning of the above two commands is also relatively simple , Two clients request each other to connect to each other's port number , If it fails, keep trying again .
What you see after execution is , It will fail madly at first , retry . After a while , Connection established .
# netstat -an | grep 2223
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:2224 127.0.0.1:2223 ESTABLISHED
tcp 0 0 127.0.0.1:2223 127.0.0.1:2224 ESTABLISHED
The following results are obtained during packet capture .
You can see , It takes four interactions to establish the connection . So it can be said that this is through " Four handshakes " Established connection .
And more importantly , There are only two clients involved , There is no server .
See here , I wonder if everyone is like me , A new wave of cognition , Yes socket With a new understanding .
In the old idea , Establishing a connection , There must be a client and a server , And the server has to execute a listen() And a accept(). But in fact , None of this is necessary .
So next time , The interviewer asked you " No, listen(), TCP Can you establish a connection ?", I think you should know how to answer .
But here's the problem , There are only two clients , No, listen() , Why can we establish TCP Connect ?
If you're interested , We'll have a chance to fill this hole again .
summary
Four waves , Whether the program is actively executed close(), Or the process was killed , It's possible to wave for the first time FIN package . If on the machine FIN-WAIT-2 There are many states , Generally, it is because the opposite end does not execute close() Method send out the third wave .
Close() It will turn off the function of sending and receiving messages at the same time .shutdown() Can turn off sending or receiving messages alone .
second 、 Third wave , It's possible to get together . So four waves become three waves .
The same socket I even myself , Will produce TCP Self join , The self connected wave is two waves .
No, listen, A connection can also be established between two clients . This situation is called TCP Open at the same time , It is produced by four handshakes .
Reference material
Recommend a zero sound education C/C++ Free open courses developed in the background , Personally, I think the teacher spoke well , Share with you :C/C++ Background development senior architect , The content includes Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK, Streaming media ,CDN,P2P,K8S,Docker,TCP/IP, coroutines ,DPDK Etc , Learn now
original text : See you for a long time !TCP Two waves , Have you ever seen ? What about the four handshakes ?
边栏推荐
- 在线文本行固定长度填充工具
- kotlin 基本数据类型
- . Net ORM framework hisql practice - Chapter 2 - using hisql to realize menu management (add, delete, modify and check)
- Educational Codeforces Round 22 E. Army Creation
- Add namespace declaration
- Shell programming core technology "three"
- 添加命名空间声明
- 与二值化阈值处理相关的OpenCV函数、方法汇总,便于对比和拿来使用
- 欧拉函数
- Pythagorean number law (any three numbers can meet the conditions of Pythagorean theorem)
猜你喜欢
随机推荐
Niuke Xiaobai month race 7 e applese's super ability
求2的n次方
abc229 总结(区间最长连续字符 图的联通分量计数)
The explain statement in MySQL queries whether SQL is indexed, and several types in extra collate and summarize
PointNeXt:通过改进的模型训练和缩放策略审视PointNet++
Shell programming core technology "three"
kotlin 基本数据类型
Cbcgpprogressdlgctrl progress bar used by BCG
1011 World Cup betting (20 points) (pat a)
【问题】druid报异常sql injection violation, part alway true condition not allow 解决方案
There are multiple divs in the large div, which are displayed on the same line. After overflow, scroll bars are generated without line breaks
1002. A+b for Polynomials (25) (PAT class a)
“只跑一趟”,小区装维任务主动推荐探索
Online data migration scheme encountered in the project 1 - general idea sorting and technical sorting
Niuke Xiaobai month race 7 F question
爬虫(6) - 网页数据解析(2) | BeautifulSoup4在爬虫中的使用
Matrix flip (array simulation)
矩阵翻转(数组模拟)
大div中有多个div,这些div在同一行显示,溢出后产生滚动条而不换行
"Only one trip", active recommendation and exploration of community installation and maintenance tasks