What happens if the server does not listen and the client initiates a connection establishment?

Hello everyone, I am Xiaolin.

In the morning, when I saw a reader talking about the three sides of the byte, I asked this question:

picture

From this reader's point of view, the client would not be able to ping the server if the server did not call listen. Obviously, it was a mistake.

The protocol used by ping is ICMP, which belongs to the network layer, and the interviewer asks questions about the transport layer.

To solve this problem, if the server only binds the IP address and port without calling listen, and then the client initiates the establishment of a TCP connection to the server, what will happen at this time?

do an experiment

This problem can be found out by doing an experiment.

I use the following program as an example, which binds the IP address + port without calling listen.

/*******服务器程序  TCPServer.c ************/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

int main(int argc, char *argv[])
{
    
    
    int sockfd, ret;
    struct sockaddr_in server_addr;

    /* 服务器端创建 tcp socket 描述符 */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
    {
    
    
        fprintf(stderr, "Socket error:%s\n\a", strerror(errno));
        exit(1);
    }

    /* 服务器端填充 sockaddr 结构 */
    bzero(&server_addr, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(8888);
  
  /* 绑定 ip + 端口 */
    ret = bind(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr));
    if(ret < 0)
    {
    
    
        fprintf(stderr, "Bind error:%s\n\a", strerror(errno));
        exit(1);
    }
  
  //没有调用 listen
    
    sleep(1000);
    close(sockfd);
    return 0;
}

Then, I use a browser to access this address: http://121.43.173.240:8888/

picture

Failed to connect to server.

At the same time, I also used the packet capture tool to capture this process.

picture

It can be seen that after the client sends a SYN message to the server, the server returns a RST message.

Therefore, there is an answer to this question. If the server only binds the IP address and port, but does not call listen, then the client initiates a connection establishment to the server, and the server will return a RST message.

Source code analysis

Next, take everyone to analyze the source code.

The entry function of the Linux kernel for processing received TCP messages is tcp_v4_rcv. After receiving a TCP message, it will call the __inet_lookup_skb function to find the socket to which the TCP message belongs.

int tcp_v4_rcv(struct sk_buff *skb)
{
 ...
  
 sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
 if (!sk)
  goto no_tcp_socket;
 ...
}

The __inet_lookup_skb function first looks for the socket in the connection establishment state (__inet_lookup_established), and only looks for the listening socket (__inet_lookup_listener) if there is no hit.

picture

The implementation of the function of looking up the listening socket (__inet_lookup_listener) is to calculate a hash value according to the destination address and destination port, and then find the socket corresponding to the listening port in the hash table.

In this case, the server did not call the listen function, so naturally it cannot find the socket listening to the port.

Therefore, the __inet_lookup_skb function finally cannot find the corresponding socket, so it jumps to no_tcp_socket.

picture

In this error handling, as long as the "checksum" of the received message (skb) is okay, the kernel will call tcp_v4_send_reset to send RST to terminate the connection.

So far, the entire source code process has been parsed.

In fact, many network problems, everyone can do their own experiments to find the answer.

picture

Can a TCP connection be established without listen?

The title question has been answered before, now we look at another similar question.

I read the group news before and saw that some readers were asked such a question when they were interviewing Tencent.

Can a TCP connection be established without using listen?

The answer is yes, the client can connect itself to form a connection (TCP self-connection), or two clients can send requests to each other to establish a connection at the same time (TCP opens at the same time), both of these situations have one thing in common , that is, the connection can be established without the participation of the server, that is, without the listen .

Then there is no listen, why can a connection be established?

We know that when the listen method is executed, a semi-connected queue and a fully connected queue will be created.

During the three-way handshake, the connection information will be temporarily stored in these two queues.

So to form a connection, the premise is that you have a place to store it, so that you can find the corresponding socket according to the IP + port and other information when shaking hands.

So will the client have a semi-connected queue?

Obviously not, because the client does not execute listen, because both the semi-connected queue and the fully connected queue are automatically created by the kernel when the listen method is executed.

But the kernel also has a global hash table, which can be used to store sock connection information.

This global hash table is actually subdivided into ehash, bhash and listen_hash, etc., but because it is too detailed, everyone understands that one global hash is enough.

In the case of TCP self-connection, the client will put its own connection information into the global hash table at the end of the connect method, and then send the information. When the message returns to the TCP transport layer through the loopback address, According to the IP + port information, the information will be taken out from the global hash again. So the handshake packets come and go, and finally the connection is successfully established .

The situation of TCP opening at the same time is similar, except that one client becomes two clients.

do an experiment

The client self-connection code, the TCP socket can connect the address and port of its own bind:

#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#define LOCAL_IP_ADDR		(0x7F000001) // IP 127.0.0.1
#define LOCAL_TCP_PORT		(34567) // 端口

int main(void)
{
    
    
	struct sockaddr_in local, peer;
	int ret;
	char buf[128];
	int sock = socket(AF_INET, SOCK_STREAM, 0);

	memset(&local, 0, sizeof(local));
	memset(&peer, 0, sizeof(peer));

	local.sin_family = AF_INET;
	local.sin_port = htons(LOCAL_TCP_PORT);
	local.sin_addr.s_addr = htonl(LOCAL_IP_ADDR);

	peer = local;	

    int flag = 1;
    ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
    if (ret == -1) {
    
    
        printf("Fail to setsocket SO_REUSEADDR: %s\n", strerror(errno));
        exit(1);
    }

	ret = bind(sock, (const struct sockaddr *)&local, sizeof(local));
	if (ret) {
    
    
		printf("Fail to bind: %s\n", strerror(errno));
		exit(1);
	}
	
	ret = connect(sock, (const struct sockaddr *)&peer, sizeof(peer));
	if (ret) {
    
    
		printf("Fail to connect myself: %s\n", strerror(errno));
		exit(1);
	}
	
	printf("Connect to myself successfully\n");

    //发送数据
	strcpy(buf, "Hello, myself~");
	send(sock, buf, strlen(buf), 0);

	memset(buf, 0, sizeof(buf));
	
	//接收数据
	recv(sock, buf, sizeof(buf), 0);
	printf("Recv the msg: %s\n", buf);

    sleep(1000);
	close(sock);
	return 0;
}

Compile and run:

Use the netstat command to order the client to self-connect the TCP connection:

insert image description here
From the screenshot, we can see that the TCP socket has successfully "connected" itself, and sent and received data packets. The output of netstat proves that the addresses and ports of both ends of the TCP are exactly the same.

Guess you like

Origin blog.csdn.net/qq_34827674/article/details/126194533