• Linux c编程之TCP通信


    一、说明

    TCP(Transmission Control Protocol),由RFC 793定义,中文名为传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。
    TCP是常用的网络传输协议之一,该协议是面向连接、可靠传输的字节流协议。在Linux C网络程序中广泛使用,如http/https、ftp等。
    TCP通信分为客户端和服务端,传输数据前需要客户端向服务端发起建立连接,数据传输完成后,双方可以断开连接。

    二、常用API介绍

    2.1 socket()

       #include 
       #include 
    
       int socket(int domain, int type, int protocol);
    
    • 1
    • 2
    • 3
    • 4

    作用: 用来创建一个通信的终端实例
    参数说明:
      domain: 协议族,用AF_INET表示IPv4
      type: 传输方式,常用的有以下两种
        SOCK_STREAM: TCP
        SOCK_DGRAM: UDP
      protol: 特殊协议,实际应用中都是写为0
    返回值:
      成功时返回一个socket文件描述符,失败时返回-1,errno会被设置,可以通过errno值获取错误码

    2.2 bind()

       #include 
       #include 
    
       int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    作用:绑定网络地址(IP/PORT)到socket
    参数说明:
      sockfd: socket()返回的描述符
      addr: 绑定的地址
      addrlen: 绑定的地址结构长度
    返回值:
      成功时返回0,失败时返回-1,errno会被设置,可以通过errno值获取错误码

    2.3 connect()

       #include       
       #include 
    
       int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    作用:连接到服务端
    参数说明:
      sockfd: socket()返回的描述符
      addr: 服务端的地址
      addrlen: 服务端的地址结构长度
    返回值:
      成功时返回0,失败时返回-1,errno会被设置,可以通过errno值获取错误码

    2.4 listen()

       #include 
       #include 
    
       int listen(int sockfd, int backlog);
    
    • 1
    • 2
    • 3
    • 4

    作用:监听客户端的连接请求
    参数说明:
      sockfd: socket()返回的描述符
      backlog: 待处理的客户端连接请求的队列最大长度,如果待处理的队列满了,则收到客户端请求会返回
    返回值:
      成功时返回0,失败时返回-1,errno会被设置,可以通过errno值获取错误码

    2.5 accept()

       #include 
       #include 
    
       int accept(int sockfd, struct sockaddr *addr,
                   socklen_t *addrlen);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    作用:接受客户端的连接请求
    参数说明:
      sockfd: socket()返回的描述符
      addr: 服务端的地址
      addrlen: 服务端的地址结构长度
    返回值:
      成功时返回对应客户端连接的socket描述符,失败时返回-1,errno会被设置,可以通过errno值获取错误码

    2.6 read()和write()

    #include 
    ssize_t write(int fd, const void *buf, size_t count);
    
    #include 
    ssize_t read(int fd, void *buf, size_t count);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    作用:
      可以通过系统的write/read函数发送/接收网络数据,其中fd对应socket文件描述符
    参数说明:
      fd: socket()返回的网络描述符
      buf:接收或发送缓冲区
      count:发送的字节数或接收的缓冲区大小
    返回值:
      大于0时,表示读取或写入的字节数; -1表示错误

    2.7 close()

    #include 
    int close(int fd);
    
    • 1
    • 2

    作用:
      关闭一个描述符(文件/socket)

    三、实际应用分析

    3.1 基本TCP通信

    server_tcp.c:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SERVER_PORT 9999
    
    int server_tcp()
    {
        int ret = 0;
        int socket_fd = -1;
        int client_fd = -1;
        int addr_len = 0;    
        unsigned int value = 1;
        char buf[1024] = {0};
        int read_len = 0;
        struct sockaddr_in server_addr;
        struct sockaddr_in client_addr;
    
        socket_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (socket_fd < 0) {
            printf("%s: socket failed\n", __FUNCTION__);
            goto on_error;
        }
    
        if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,
                (void *)&value, sizeof(value)) < 0) {
            printf("%s: Fail to setsockopt\n", __FUNCTION__);
            goto on_error;
        }
    
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVER_PORT);
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        addr_len = sizeof(server_addr);
        ret = bind(socket_fd, (const struct sockaddr *)&server_addr, addr_len);
        if (ret < 0) {
            printf("%s: bind failed\n", __FUNCTION__);
            goto on_error;
        }
    
        ret = listen(socket_fd, 5);
        if (ret < 0) {
            printf("%s: listen failed\n", __FUNCTION__);
            goto on_error;
        }
    
        client_fd = accept(socket_fd
                , (struct sockaddr *)&client_addr, &addr_len);
        if (client_fd < 0) {
            printf("%s: invalid client fd[%d]\n", __FUNCTION__, client_fd);
            return -1;
        }
        printf("client fd[%d] connect\n", client_fd);
    
        while (1) {
            read_len = read(client_fd, buf, 1024);
            if (read_len > 0) {
                printf("recv data[%s] from client\n", buf);
                write(client_fd, buf, read_len);
                printf("send data[%s] to client\n", buf);
            } else if (read_len < 0) {
                printf("errno:%d\n", errno);
                break;
            } else if (0 == read_len) {
                printf("client fd[%d] disconnect\n", client_fd);
                break;
            }
        }
    
        close(socket_fd);
        close(client_fd);
    
        return 0;
    
    on_error:
    
        close(socket_fd);
        close(client_fd);
    
        return -1;
    }
    
    int main(int argc, char *argv[])
    {
        int ret = -1;
    
        ret = server_tcp();
        if (ret < 0) {
            printf("%s: server tcp failed\n", __FUNCTION__);
            return -1;
        }
    
        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
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101

    client_tcp.c:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SERVER_IP "127.0.0.1"
    #define SERVER_PORT 9999
    
    int client_tcp(char *server_ip, int server_port, char *data)
    {
        int ret = 0;
        int socket_fd = -1;
        int addr_len = 0;
        char buf[1024] = {0};
        struct sockaddr_in server_addr;
    
        socket_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (socket_fd < 0) {
            printf("%s: socket failed\n", __FUNCTION__);
            return 0;
        }
    
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(server_port);
        server_addr.sin_addr.s_addr = inet_addr(server_ip);
    
        addr_len = sizeof(server_addr);
        ret = connect(socket_fd, (struct sockaddr *)&server_addr, addr_len);
        if (ret < 0) {
            printf("%s: connect failed, errno:%d, %s\n", __FUNCTION__, errno, strerror(errno));
            goto on_error;
        }
    
        ret = write(socket_fd, data, strlen(data));
        printf("send data[%s] to server\n", data);
    
        ret = read(socket_fd, buf, sizeof(buf));
        if (ret > 0) {
            printf("recv data[%s] from server\n", buf);
        } else if (ret < 0) {
            printf("%s: errno:%d\n", __FUNCTION__, errno);
        } else if (0 == ret) {
            printf("server fd[%d] disconnect\n", socket_fd);
        }
    
        close(socket_fd);
    
        return 0;
    
    on_error:
    
        close(socket_fd);
    
        return -1;
    }
    
    int main(int argc, char *argv[])
    {
        if (argc < 4) {
            printf("usage: %s 127.0.0.1 9999 hello\n", argv[0]);
            return -1;
        }
    
        client_tcp(argv[1], atoi(argv[2]), argv[3]);
    
        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
    • 70
    • 71
    • 72

    Makefile:

    all:
    	gcc -o server server_tcp.c
    	gcc -o client client_tcp.c
    clean:
    	-@rm server client
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    编译:
    make
    运行:

    $ ./server
    client fd[4] connect
    recv data[helloworld] from client
    send data[helloworld] to client
    client fd[4] disconnect
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    $ ./client 127.0.0.1 9999 helloworld
    send data[helloworld] to server
    recv data[helloworld] from server
    
    • 1
    • 2
    • 3

    服务端不启动时,客户端连接失败,如下:

    $ ./client 127.0.0.1 9999 helloworld
    client_tcp: connect failed, errno:111, Connection refused
    
    • 1
    • 2

    服务端启动时,可以看见监听的端口,如下:

    $ netstat -antp | grep server
    tcp        0      0 0.0.0.0:9999            0.0.0.0:*               LISTEN      36467/server
    
    • 1
    • 2

    已经有服务端启动时,再启动服务端bind()接口会失败:

    $ ./server
    bind failed, errno:98, Address already in use
    
    • 1
    • 2

    3.2 服务端支持IO复用

    为了使服务端能够多次处理连接,使用epoll函数管理socket描述符。
    源码:
    server_tcp.c:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SERVER_PORT 9999
    
    
    int g_listen_fd = -1;
    int g_epoll_fd = -1;
    
    int server_tcp()
    {
        int ret = 0;
        long socket_fd = -1;
        int addr_len = 0;    
        unsigned int value = 1;
        struct epoll_event event;
        struct sockaddr_in server_addr;
    
        socket_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (socket_fd < 0) {
            printf("%s: socket failed\n", __FUNCTION__);
            goto on_error;
        }
    
        if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,
                (void *)&value, sizeof(value)) < 0) {
            printf("%s: Fail to setsockopt\n", __FUNCTION__);
            goto on_error;
        }
    
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVER_PORT);
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        addr_len = sizeof(server_addr);
        ret = bind(socket_fd, (const struct sockaddr *)&server_addr, addr_len);
        if (ret < 0) {
            printf("%s: bind failed\n", __FUNCTION__);
            goto on_error;
        }
    
        ret = listen(socket_fd, 5);
        if (ret < 0) {
            printf("%s: listen failed\n", __FUNCTION__);
            goto on_error;
        }
    
        event.events =  EPOLLIN;
        event.data.fd = socket_fd;
        ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, socket_fd, &event);
        if(ret < 0) {
            printf("epoll_ctl failure:%s\n", strerror(errno));
            close(g_epoll_fd);
            return -1;
        }
    
        g_listen_fd = socket_fd;
    
        return 0;
    
    on_error:
    
        close(socket_fd);
    
        return -1;
    }
    
    int main(int argc, char *argv[])
    {
        int ret = -1;
        int num = 0;
        int i = 0;
        int client_fd = 0;
        int addr_len = 0;
        int read_len = 0;
        char buf[1024] = {0};
        struct epoll_event event;
        struct sockaddr_in client_addr;
        struct epoll_event events_array[10];
    
        g_epoll_fd = epoll_create(10);
        if (g_epoll_fd < 0) {
            printf("epoll_create failure:%s\n", strerror(errno));
            return -1;
        }
    
        ret = server_tcp();
        if (ret < 0) {
            printf("%s: server tcp failed\n", __FUNCTION__);
            return -1;
        }
    
        while (1) {
            num = epoll_wait(g_epoll_fd, events_array, 10, -1);
            if (num < 0) {
                printf("epoll_wait failure:%s\n", strerror(errno));
                close(g_epoll_fd);
                break;
            } else if (num == 0) {
                printf("eopll_wait timeout!\n");
                continue;
            }
    
            for (i = 0; i < num; i++) {
                if(events_array[i].events == EPOLLIN) {
                    if (events_array[i].data.fd == g_listen_fd) {
                        client_fd = accept(events_array[i].data.fd,
                                (struct sockaddr *)&client_addr, &addr_len);
                        if (client_fd < 0) {
                            printf("%s: invalid client fd[%d]\n", __FUNCTION__, client_fd);
                            return -1;
                        }
    
                        printf("client %s:%d fd[%d] connect\n",
                                        inet_ntoa(client_addr.sin_addr),
                                        ntohs(client_addr.sin_port), client_fd);
    
                        event.events =  EPOLLIN;
                        event.data.fd = client_fd;
                        ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
                        if(ret < 0) {
                            printf("epoll_ctl failure:%s\n", strerror(errno));
                            return -1;
                        }
                    } else {
                        memset(buf, 0, sizeof(buf));
                        read_len = read(events_array[i].data.fd, buf, 1024);
                        if (read_len > 0) {
                            printf("recv data[%s] from client fd[%d]\n",
                                    buf, events_array[i].data.fd);
                        } else if (read_len < 0) {
                            epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, events_array[i].data.fd, NULL);
                            close(events_array[i].data.fd);
                        } else if (0 == read_len) {
                            printf("client fd[%d] disconnect\n",
                                    events_array[i].data.fd);
                            epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, events_array[i].data.fd, NULL);
                            close(events_array[i].data.fd);
                        }
                    }
                }
    
            }
        }
    
        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
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154

    client_tcp.c:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int client_tcp(char *server_ip, int server_port, char *data)
    {
        int ret = 0;
        int socket_fd = -1;
        int addr_len = 0;
        struct sockaddr_in server_addr;
    
        socket_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (socket_fd < 0) {
            printf("%s: socket failed\n", __FUNCTION__);
            return 0;
        }
    
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(server_port);
        server_addr.sin_addr.s_addr = inet_addr(server_ip);
    
        addr_len = sizeof(server_addr);
        ret = connect(socket_fd, (struct sockaddr *)&server_addr, addr_len);
        if (ret < 0) {
            printf("%s: connect failed\n", __FUNCTION__);
            goto on_error;
        }
    
        ret = write(socket_fd, data, strlen(data));
    
        close(socket_fd);
    
        return 0;
    
    on_error:
    
        close(socket_fd);
    
        return -1;
    }
    
    int main(int argc, char *argv[])
    {
        if (argc < 4) {
            printf("usage: %s 127.0.0.1 9999 hello\n", argv[0]);
            return -1;
        }
    
        client_tcp(argv[1], atoi(argv[2]), argv[3]);
    
        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

    Makefile:

    all:
    	gcc -o server server_tcp.c
    	gcc -o client client_tcp.c
    clean:
    	-@rm server client
    
    • 1
    • 2
    • 3
    • 4
    • 5

    客户端端口变化问题

    $  ./server
    client 127.0.0.1:50280 fd[5] connect
    recv data[hello] from client fd[5]
    client fd[5] disconnect
    client 127.0.0.1:50281 fd[5] connect
    recv data[hello] from client fd[5]
    client fd[5] disconnect
    client 127.0.0.1:50282 fd[5] connect
    recv data[hello] from client fd[5]
    client fd[5] disconnect
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    客户端端口变化是由于没有绑定指定的端口,系统每次随机分配了一个端口。

    memset(&client_addr, 0, sizeof(client_addr));                                                                                        
        client_addr.sin_family = AF_INET;
        client_addr.sin_port = htons(10000);
        client_addr.sin_addr.s_addr = inet_addr(127.0.0.1);
        addr_len = sizeof(client_addr);
    
        ret = bind(socket_fd, (const struct sockaddr *)&client_addr, addr_len);
        if (ret < 0) {
            printf("%s: bind failed\n", __FUNCTION__);
            close(socket_fd);
            return 0;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    测试:
    多次启动客户端,均为同一个端口10000

    $ ./server
    client 127.0.0.1:10000 fd[5] connect
    recv data[hello] from client fd[5]
    client fd[5] disconnect
    client 127.0.0.1:10000 fd[5] connect
    recv data[hello] from client fd[5]
    client fd[5] disconnect
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ###3.3 绑定失败问题

    $ ./client 127.0.0.1 9999 hello
    $ ./client 127.0.0.1 9999 hello
    client_tcp: bind failed, errno:98, Address already in use
    
    $ netstat -antp | grep 10000
    tcp        0      0 127.0.0.1:10000         127.0.0.1:9999          TIME_WAIT
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    原因是之前的连接还没有完全释放,可以在调用bind()接口前将socket设置为地址可重复使用,对客户端、服务端同样适用。

    socket_fd = socket(AF_INET, SOCK_STREAM, 0); 
        if (socket_fd < 0) {
            printf("%s: socket failed\n", __FUNCTION__);
            return 0;
        }   
    
        unsigned int value = 1;
        if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,
                (void *)&value, sizeof(value)) < 0) {
            printf("%s: Fail to setsockopt\n", __FUNCTION__);
            goto on_error;
        }   
    
        memset(&client_addr, 0, sizeof(client_addr));
        client_addr.sin_family = AF_INET;
        client_addr.sin_port = htons(CLINET_PORT);
        client_addr.sin_addr.s_addr = inet_addr(CLINET_IP);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    测试:

    $ ./client 127.0.0.1 9999 hello
    $ ./client 127.0.0.1 9999 hello
    $ ./client 127.0.0.1 9999 hello
    
    • 1
    • 2
    • 3

    这时就没有绑定问题了

    3.4 服务端监听地址问题

    1. server监听在本地回环地址127.0.0.1
    $ ./server 127.0.0.1 9999
    client 127.0.0.1:10000 fd[5] connect
    recv data[hello] from client fd[5]
    client fd[5] disconnect
    
    • 1
    • 2
    • 3
    • 4

    在同一台机器,运行client可以访问

    $ ./client 127.0.0.1 9999 hello
    
    • 1

    但是在另外一台机器,是连接不上的,因为127.0.0.1是本地回环地址,只能在同一台机器内使用,如下:

    $ ./client 127.0.0.1 9999 hello
    client_tcp: connect failed
    
    • 1
    • 2

    因此,server端需要绑定其它机器可以访问的地址。比如server 端所在机器是192.168.0.29,可以绑定在192.168.0.29。
    测试结果:

    $ ./server 192.168.0.29 9999
    client 192.168.0.132:10000 fd[5] connect
    recv data[hello] from client fd[5]
    client fd[5] disconnect
    
    • 1
    • 2
    • 3
    • 4
    $ ./client 192.168.0.29 9999 hello
    
    • 1

    同时,同一台机器也可以通过127.0.0.1连接

    $ ./server 192.168.0.29 9999
    client 127.0.0.1:10000 fd[5] connect
    recv data[hello] from client fd[5]
    client fd[5] disconnect
    
    • 1
    • 2
    • 3
    • 4
    $ ./client 192.168.0.29 9999 hello
    
    • 1

    但是在实际环境中,比如云服务器,主机上一般都有多块网上、多个IP,如本地回环地址127.0.0.1、局域网IP(也称私网地址)、公网IP等,如下某主机IP地址情况:
    本地回环地址:127.0.0.1
    私有IP:192.168.0.5, 192.168.1.5
    公网地址:36.87.80.2,36.78.90.3

    如果服务端监听在192.168.0.5地址,客户端192.168.0.x可以连接,但客户端192.168.1.x连接不上;如果服务端监听在192.168.1.5地址,客户端192.168.1.x可以连接,但客户端192.168.0.x连接不上。
    有一种方法是同时监听需要监听的地址,常用于公网服务器。另一种方法是直接监听在0.0.0.0地址,即表示监听主机的所有IP地址,这也是非常常用的方法。

    四、关键说明

    • TCP是常用的网络层传输协议,最经典的应用场景是HTTP协议
    • TCP通信有重传机制,是可靠传输
  • 相关阅读:
    基于STM32设计的智能化钻杆系统(华为云IOT)
    SourceTree使用技巧
    第2-4-2章 规则引擎Drools入门案例-业务规则管理系统-组件化-中台
    详解Linux的系统调用fork()函数
    详解RISC v中断
    基于YOLO算法的单目相机2D测量(工件尺寸和物体尺寸)三
    vue3.0 组件传参
    十大排序算法之——归并排序算法(Java实现)及思路讲解
    阿里巴巴内部:2022年全技术栈(架构篇+算法篇+大数据)你值得拥有
    Unity --- 反向动力学(IK)
  • 原文地址:https://blog.csdn.net/szkbsgy/article/details/128139054