NeoZephyr / Post-It-Note

0 stars 0 forks source link

TCP 握手、参数 #22

Open NeoZephyr opened 2 years ago

NeoZephyr commented 2 years ago

net.ipv4.tcp_tw_reuse net.ipv4.ip_local_port_range

connect 与 bind 端口选择区别

NeoZephyr commented 2 years ago

image

NeoZephyr commented 2 years ago
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>

#define MAXLINE 4096

int main(int argc, char** argv)
{
    int sorkfd, n;
    char recvline[4096], sendline(4096];
    struct sockaddr_in servaddr:
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(6666);
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);

    for (n = 0; n < 100; n++) {
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
            printf("create socket error: %s(errno: %d) \n"
            strerror(errno), errno);
            return 0:
        }

        // 客户端不停的向服务端发起新连接,成功之后继续发,没成功会阻塞在这里
        if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
        {
            printf("connect error: %s(errno: %d) \n", strerror(errno), errno);
            return 0;
        }

        printf("connected to server: %d\n", n);
        close(sockfd);
    }
}
NeoZephyr commented 2 years ago
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>

#define MAXLINE 4096

int main(int argc, char* argv[])
{
    int listenfd, connfd;
    struct sockaddr_in servaddr;
    char buff[4096];
    int n;

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(6666);

    if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
    {
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 1;
    }

    if (listen(listenfd, 10) == -1)
    {
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno):
        return 2;
    }

    if ((connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1)
    {
        printf("accept socket error: %s(errno: %d)", strerror(errno), errno);
        return 3;
    }

    printf("accepet a socket \n");

    // 服务端仅 accept 一次,之后就不再 accept,此时全连接队列会被堆满
    sleep(1000);
    close(connfd);
    close(listenfd);
    return 0;
}
NeoZephyr commented 2 years ago

在 Linux中,一般情况下都是内核代理三次握手的,也就是说,当 client端调用 connect() 之后内核负责发送SYN,接收SYN-ACK,发送ACK。然后 connect() 系统调用才会返回,客户端侧握手成功

服务端的 Linux 内核会在收到SYN之后负责回复SYN-ACK再等待ACK之后才会让 accept() 返回,从而完成服务端侧握手。于是Linux内核就需要引入半连接队列(用于存放收到SYN,但还没收到ACK的连接)和全连接队列(用于存放已经完成3次握手,但是应用层代码还没有完成 accept() 的连接)两个概念,用于存放在握手中的连接

NeoZephyr commented 2 years ago

3.10 内核在全连接队列满的情况下,会先回复SYN-ACK,然后移进全连接队列时才发现满了于是丢弃连接,这样从客户端看来TCP连接成功了

在 4.9 内核中,对于全连接队列满的处理,就不一样,connect() 系统调用不会成功,一直阻塞,也就是说能够避免幽灵连接的产生