• Linux高性能服务器编程——ch5笔记


    第5章 Linux网络编程基础API

    5.1 socket地址API

    主机字节序(小端字节序):整数的高位字节存储在内存的高地址处,而低位字节则存储在内存的低地址处。
    网络字节序(大端字节序):相反。

    void byteorder()
    {
        union{
            short value;
            char union_bytes[sizeof(short)];
        }test;
    
        test.value = 0x0102;
    
        if ((test.union_bytes[0] == 1) && (test.union_bytes[1] == 2)) {
            printf("big endian\n");
        } else if ((test.union_bytes[0] == 2) && (test.union_bytes[1] == 1)) {
            printf("little endian\n");
        } else {
            printf("unknown...\n");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    主机字节序和网络字节序之间的转换:发送端总是把要发送的数据转化成大端字节序数据后发送。(函数:htonl、htons、ntohl、ntohs)host to network long(short)

    #include 
    //是内存对齐的
    struct sockaddr_storage
    {
    	sa_family_t sa_family;  //地址族类型的变量
    	unsigned long int __ss_align;  //内存对齐
    	char __ss_padding[128-sizeof(__ss_align)];  //socket地址值
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    #include 
    struct sockaddr_un
    {
    	sa_family_t sin_family;  //地址族:AF_UNIX
        char sun_path[108];  //文件路径名
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    struct sockaddr_in
    {
    	sa_family_t sin_family;  //地址族:AF_INET
    	u_int16_t sin_port;  //端口号,网络字节序
        struct in_addr sin_addr;  //IPv4地址结构体
    };
    struct in_addr
    {
    	u_int32_t s_addr;  //IPv4地址,网路字节序
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    struct sockaddr_in6
    {
    	sa_family_t sin6_family;  //地址族:AF_INET6
    	u_int16_t sin6_port;  //端口号,网络字节序
    	u_int32_t sin6_flowinfo;  //流信息,设置为0
        struct in6_addr sin6_addr;  //IPv6地址结构体
    	u_int32_t sin6_scope_id;  //scope ID
    };
    struct in6_addr
    {
    	unsigned char sa_addr[16];  //IPv6地址,网路字节序
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    函数:inet_addr(点分十进制v4地址转化为网络字节序整数表示的v4地址)、inet_aton(与上一个一样,但结果转储在参数inp指向的地址中)、inet_ntoa(网络字节序表示的v4转化为点分十进制,不可重入)、inet_pton(适用v4和v6,网络字节序转储在参数dst指向的内存中)、inet_ntop(适用v4和v6,相反)

    5.2 创建socket

    socket就是可读、可写、可控制、可关闭的文件描述符(int)。

    #include 
    #include 
    int socket(int domain, int type, int protocol);
    /*
    ** domain:底层协议族
    ** type:服务类型,SOCK_STREAM(TCP流服务)、SOCK_UGRAM(UDP数据报服务)
    ** protocol:0,表示默认协议
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    5.3 命名socket

    命名:将一个socket与socket地址绑定。bind函数。
    服务器中需要,客户端不需要,而是采用匿名,即使用OS自动分配的socket地址。

    5.4 监听socket

    listen函数:创建一个监听队列以存放待处理的客户连接。客户端被动连接。

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    static bool stop = false;
    
    /* SIGTERM信号的处理函数,触发时结束主程序中的循环 */
    static void handle_term(int sig)
    {
        stop = true;
    }
    
    int main(int argc, char **argv)
    {
        signal(SIGTERM, handle_term);
    
        if (argc < 4) {
            printf("usage: %s ip_address port_number backlog\n",
                    basename(argv[0]));
    
            return 1;
        }
    
        const char *ip = argv[1];  //IP地址
        int port = atoi(argv[2]);  //端口号
        int backlog = atoi(argv[3]);  //backlog值,内核监听队列的最大长度
    
        int sock = socket(PF_INET, SOCK_STREAM, 0);
        assert(sock >= 0);
    
        /* 创建一个IPv4 socket地址 */
        struct sockaddr_in address;
        bzero(&address, sizeof(address));
        address.sin_family = AF_INET;
        inet_pton(AF_INET, ip, &address.sin_addr);
        address.sin_port = htons(port);
    
        int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));
        assert(ret != -1);
    
        ret = listen(sock, backlog);
        assert(ret != -1);
    
        /* 循环等待连接, 查到有SIGTERM信号将它中断 */
        while(!stop)
        {
            sleep(1);
        }
    
        /* 关闭socket */
        close(sock);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    监听队列中完整连接的上限通常比 backlog 值略大。

    5.5 接受连接

    accept函数。

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main(int argc, char *argv[])
    {
        if (argc < 2){
            printf("usage: %s ip_address port_number!\n", basename(argv[0]));
            return 1;
        }
    
        const char *ip = argv[1];
        int port = atoi(argv[2]);
    
        int sock = socket(PF_INET, SOCK_STREAM, 0);
        assert(sock >= 0);
    
        struct sockaddr_in address;
        bzero(&address, sizeof(address));
        address.sin_family = AF_INET;
        inet_pton(AF_INET, ip, &address.sin_addr);
        address.sin_port = htons(port);
    
        int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));
        assert(ret != -1);
    
        ret = listen(sock, 5);
        assert(ret != -1);
    
        /* 暂停20秒以等待客户端连接和相关操作(掉线或者退出)完成 */
        sleep(20);
    
        struct sockaddr_in client;
        socklen_t client_addrlength = sizeof(client);
        int connfd = accept(sock, (struct sockaddr *)&client, &client_addrlength);
        if (connfd < 0) {
            printf("errno is: %d\n", errno);
        } else {
            /* 接受连接成功则打印客户端的IP地址和端口号 */
            char remote[INET_ADDRSTRLEN];
            printf("connected with ip: %s and port: %d\n", inet_ntop(AF_INET,
                        &client.sin_addr, remote, INET_ADDRSTRLEN), ntohs(client.sin_port));
            close(connfd);
        }
    
        close(sock);
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    accept只是从监听队列中取出连接,而不论连接处于何种状态 (如上面的ESTABLISHED 状态和 CLOSE_WAIT 状态),更不关心任何网络状况的变化。

    5.6 发起连接

    客户端通过connect函数主动与服务器建立连接。

    5.7 关闭连接

    关闭该连接对应的socket。
    close函数:将参数fd的引用计数减1,为0才是真正关闭。多进程程序中,一次fork默认使父进程中打开的socket的引用计数加1。读写同时关闭。
    shutdown函数:分别关闭socket上的读或写,或者都关闭。

    5.8 数据读写

    TCP数据读写:recv(可能要多次调用才能读取完整数据)、send(写入数据)

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main(int argc, char *argv[])
    {
        if (argc <= 2) {
            printf("usage: %s ip_address port_number\n", basename(argv[0]));
            return 1;
        }
    
        const char *ip = argv[1];
        int port = atoi(argv[2]);
    
        struct sockaddr_in server_address;
        bzero(&server_address, sizeof(server_address));
        server_address.sin_family = AF_INET;
        inet_pton(AF_INET, ip, &server_address.sin_addr);
        server_address.sin_port = htons(port);
    
        int sockfd = socket(PF_INET, SOCK_STREAM, 0);
        assert(sockfd >= 0);
    
        if (connect(sockfd, (struct sockaddr *)&server_address,
                    sizeof(server_address)) > 0) {
            printf("connection failed\n");
        } else {
            const char *oob_data = "abc";
            const char *normal_data = "123";
            send(sockfd, normal_data, strlen(normal_data), 0);
            send(sockfd, oob_data, strlen(oob_data), MSG_OOB);  //flags=MSG_OOB 带外数据
            send(sockfd, normal_data, strlen(normal_data), 0);
    
        }
    
        close(sockfd);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define BUF_SIZE 1024
    
    int main(int argc, char *argv[])
    {
        if (argc <= 2) {
            printf("usage: %s ip_address port_number\n", basename(argv[0]));
            return 1;
        }
    
        const char *ip = argv[1];
        int port = atoi(argv[2]);
    
        struct sockaddr_in address;
        bzero(&address, sizeof(address));
        address.sin_family = AF_INET;
        inet_pton(AF_INET, ip, &address.sin_addr);
        address.sin_port = htons(port);
    
        int sock = socket(PF_INET, SOCK_STREAM, 0);
        assert(sock);
    
        int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));
        assert(ret != -1);
    
        ret = listen(sock, 5);
        assert(ret != -1);
    
        struct sockaddr_in client;
        socklen_t client_addrlength = sizeof(client);
        int connfd = accept(sock, (struct sockaddr *)&client, &client_addrlength);
        if (connfd < 0) {
            printf("errno is : %d\n", errno);
        } else {
            char buffer[BUF_SIZE];
    
            memset(buffer, '\0', BUF_SIZE);
            ret = recv(connfd, buffer, BUF_SIZE - 1, 0);
            printf("got %d bytes of normal data '%s'\n", ret, buffer);
    
            memset(buffer, '\0', BUF_SIZE);
            ret = recv(connfd, buffer, BUF_SIZE - 1, MSG_OOB);  //flags=MSG_OOB 带外数据
            printf("got %d bytes of oob data '%s'\n", ret, buffer);
    
            memset(buffer, '\0', BUF_SIZE);
            ret = recv(connfd, buffer, BUF_SIZE - 1, 0);
            printf("got %d bytes of normal data '%s'\n", ret, buffer);
    
            memset(buffer, '\0', BUF_SIZE);
            ret = recv(connfd, buffer, BUF_SIZE - 1, 0);
            printf("got %d bytes of normal data '%s'\n", ret, buffer);
    
            close(connfd);
        }
    
        close(sock);
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    UDP数据读写:recvfrom(UDP通信没有连接的概念,每次读取数据都要获取发送端的socket地址)、sendto(写入数据)。
    通用数据读写函数:recvmsg(数据将被读取并存放在msg_iovlen块分散的内存,称为分散读)、sendmsg(msg_iovlen块分散内存中的数据将被一并发送,称为集中写)。

    5.9 带外标记

    内核检测到TCP紧急标志,然后通知应用程序带外数据到达的两种常见方式是:I/O复用产生的异常事件和SIGURG信号。应用程序通过sockatmark函数得到带外数据在数据流中的具体位置。

    5.10 地址信息函数

    getsockname:获取sockfd对应的本端socket地址,存储与参数指定的内存中。
    getpeername:获取远端socket地址。

    5.11 socket选项

    读取和设置socket文件描述符属性:getsockopt、setsockopt。
    对服务器而言,部分socket选项(在TCP同步报文段中设置)只能在调用listen(listen监听队列中的连接至少是SYN_RCVD状态,三次握手前两步至少已经完成,同步报文段已发出)之前设置才有效。
    对客户端而言,部分socket选项应该在connect之前设置。

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main(int argc, char *argv[])
    {
        if (argc <= 2) {
            printf("usage: %s ip_address port_number\n", basename(argv[0]));
            return 1;
        }
    
        const char *ip = argv[1];
        int port = atoi(argv[2]);
    
        int sock = socket(PF_INET, SOCK_STREAM, 0);
        assert(sock);
        int reuse = 1;
        //即使 sock 处于 TIME_WAIT 状态,与之绑定的 socket 地址也可以立即被重用
        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
    
        struct sockaddr_in address;
        bzero(&address, sizeof(address));
        address.sin_family = AF_INET;
        inet_pton(AF_INET, ip, &address.sin_addr);
        address.sin_port = htons(port);
    
        int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));
        assert(ret != -1);
    
        ret = listen(sock, 5);
        assert(ret != -1);
    
        struct sockaddr_in client;
        socklen_t client_addrlength = sizeof(client);
        int connfd = accept(sock, (struct sockaddr *)&client, &client_addrlength);
        if (connfd < 0) {
            printf("errno is %d\n", errno);
        } else {
            char remote[INET_ADDRSTRLEN];
            printf("connected with ip: %s and port: %d\n",
                    inet_ntop(AF_INET, &client.sin_addr, remote, INET_ADDRSTRLEN), ntohs(client.sin_port));
            close(connfd);
        }
    
        close(sock);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define BUFFER_SIZE 512
    
    int main(int argc, char *argv[])
    {
        if (argc <= 2) {
            printf("usage: %s ip_address port_number send_buffer_size\n",
                    basename(argv[0]));
            return 1;
        }
    
        const char *ip = argv[1];
        int port = atoi(argv[2]);
    
        struct sockaddr_in server_address;
        bzero(&server_address, sizeof(server_address));
        server_address.sin_family = AF_INET;
        inet_pton(AF_INET, ip, &server_address.sin_addr);
        server_address.sin_port = htons(port);
    
        int sock = socket(PF_INET, SOCK_STREAM, 0);
        assert(sock >= 0);
    
        int sendbuf = atoi(argv[3]);
        int len = sizeof(sendbuf);
    
        /* 先设置TCP发送缓冲区的大小,然后立即读取之 */
        setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, sizeof(sendbuf));
        getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, (socklen_t *)&len);
        printf("the tcp send buffer size after setting is %d\n", sendbuf);
    
        if(connect(sock, (struct sockaddr *)&server_address, sizeof(
                        server_address)) != -1)
        {
            char buffer[BUFFER_SIZE];
            memset(buffer, 'a', BUFFER_SIZE);
            send(sock, buffer, BUFFER_SIZE, 0);
        }
    
        close(sock);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define BUFFER_SIZE 1024
    
    int main(int argc, char *argv[])
    {
        if (argc <= 2) {
            printf("usage: %s ip_address port_number recv_buffer_size\n",
                    basename(argv[0]));
            return 1;
        }
    
        const char *ip = argv[1];
        int port = atoi(argv[2]);
    
        struct sockaddr_in address;
        bzero(&address, sizeof(address));
        address.sin_family = AF_INET;
        inet_pton(AF_INET, ip, &address.sin_addr);
        address.sin_port = htons(port);
    
        int sock = socket(PF_INET, SOCK_STREAM, 0);
        assert(sock >- 0);
    
        int recvbuf = atoi(argv[3]);
        int len = sizeof(recvbuf);
    
        /* 先设置TCP接受缓冲区的大小,然后立即读取之 */
        setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf));
        getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, (socklen_t *)&len);
        printf("the tcp receive buffer size after setting is %d\n", recvbuf);
    
        int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));
        assert(ret != -1);
    
        ret = listen(sock, 5);
        assert(ret != -1);
    
        struct sockaddr_in client;
        socklen_t client_addrlength = sizeof(client);
        int connfd = accept(sock, (struct sockaddr *)&client, &client_addrlength);
        if (connfd < 0) {
            printf("errno is %d\n", errno);
        } else {
            char buffer[BUFFER_SIZE];
            memset(buffer, '\0', BUFFER_SIZE);
            while(recv(connfd, buffer, BUFFER_SIZE - 1, 0) > 0) {}
            close(connfd);
        }
    
        close(sock);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    setsockopt设置缓冲区大小时,系统会将其值加倍,且TCP接收缓冲区最小值为256字节,发送缓冲区最小值为2048字节。
    SO_RCVLOWAT 和 SO_SNDLOWAT 选项分别表示TCP接收缓冲区和发送缓冲区的低水位标记。它们一般被I/O复用系统调用,来判断socket是否可读或可写。默认1字节。
    SO_LINGER 选项用于控制 close 系统调用在关闭TCP连接时的行为。默认情况下,当我们使用 close 来关闭一个socket时,close 将立即返回,TCP模块负责把该 socket对应的TCP发送缓冲区中残留的数据发送给对方。

    5.12 网络信息API

    socket地址由IP地址和端口号组成,不便于扩展,可以用主机名代替IP地址,用服务名称代替端口号。
    gethostbyname:根据主机名称获取主机完整信息。
    gethostbyaddr:根据IP地址获取主机完整信息。
    getservbyname:根据名称获取某个服务的完整信息。
    getservbyport:根据端口号获取某个服务的完整信息。
    四个函数均不可重入,即非线程安全。尾部加_r是可重入版本。

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main(int argc, char *argv[])
    {
        assert(argc == 2);
        char *host = argv[1];
    
        /* 获取目标主机地址信息 */
        struct hostent *hostinfo = gethostbyname(host);
        assert(hostinfo);
    
        /* 获取daytime服务信息 */
        struct servent *servinfo = getservbyname("daytime", "tcp");
        assert(servinfo);
        printf("daytime port is :%d\n", ntohs(servinfo->s_port));
    
        struct sockaddr_in address;
        address.sin_family = AF_INET;
        address.sin_port = servinfo->s_port;
        /* 注意下面的代码,因为h_addr_list本身使用网络字节序的地址列表,所以使用其中的IP
         * 地址时,无需对目标IP地址转换字节序 */
        address.sin_addr = *(struct in_addr *)*hostinfo->h_addr_list;
    
        int sockfd = socket(PF_INET, SOCK_STREAM, 0);
        int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
        assert(result != -1);
    
        char buffer[128];
        result = read(sockfd, buffer, sizeof(buffer));
        assert(result > 0);
        buffer[result] = '\0';
        printf("the day time is : %s\n", buffer);
    
        close(sockfd);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    getaddrinfo:通过主机名获得IP地址(内部使用的是 gethostbyname 函数),也能通过服务名获得端口号(内部使用的是 getservbyname 函数)。它是否可重人取决于其内部调用的函数是否是它们的可重入版本。将隐式地分配堆内存,因为参数result指针(struct addrinfo*类型,指向链表,存储结果)原本是没有指向一块合法内存的,调用结束后,必须使用 freeaddrinfo 函数来释放这块内存。
    getnameinfo:通过socket地址同时获得以字符串表示的主机名(内部使用的是gethostbyaddr 函数)和服务名(内部使用的是 getservbyport 函数),它是否可重入取决于其内部函数是否是它们的可重入版本。

  • 相关阅读:
    十、ThreadLocal
    Android TV 桌面图标闪
    C++基础
    JAVA继承
    内核动态打印
    信息化发展47
    如何写好软件任务书
    国产MCU芯片(2):东软MCU概览
    [vulnhub] Matrix:1
    C++ 线段树的实现总结
  • 原文地址:https://blog.csdn.net/qq_43680965/article/details/133942837