C语言socket编程实例

文章目录

      • 一、实验说明
      • 二、面向连接的流式套接字 C/S 例子
      • 三、非阻塞的多人聊天服务器端例子
      • 四、简单的 IPv6 UDP socket编程
      • 四、参考书籍和??

一、实验说明

  • 服务端:树莓派(可以使用putty、xshell、vnc远程操作树莓派,这里使用vnc)
  • 客户端:Ubuntu
  • 所用语言:C语言
  • 树莓派和Ubuntu应处于同一个局域网下(可以用手机热点连接)

二、面向连接的流式套接字 C/S 例子

  1. 树莓派下,新建一个 Server1.c,命令 nano Server1.c,然后写入如下内容

    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <arpa/inet.h>
    #include <sys/wait.h>
    #include <signal.h>
    #define PORT "9090" // the port users will be connecting to
    #define BACKLOG 10
    // how many pending connections queue will hold
    void sigchld_handler(int s)
    {<!-- -->
        while(waitpid(-1, NULL, WNOHANG) > 0);
    }
    // get sockaddr, IPv4 or IPv6:
    void *get_in_addr(struct sockaddr *sa)
    {<!-- -->
        if (sa->sa_family == AF_INET) {<!-- -->
            return &(((struct sockaddr_in*)sa)->sin_addr);
        }
        return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }
    int main(void)
    {<!-- -->
        int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
        struct addrinfo hints, *servinfo, *p;
        struct sockaddr_storage their_addr; // connector's address information
        socklen_t sin_size;
        struct sigaction sa;
        int yes=1;
        char s[INET6_ADDRSTRLEN];
        int rv;
        memset(&hints, 0, sizeof hints);
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_flags = AI_PASSIVE; // use my IP
        if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {<!-- -->
            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
            return 1;
        }
    // loop through all the results and bind to the first we can
        for(p = servinfo; p != NULL; p = p->ai_next) {<!-- -->
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
        p->ai_protocol)) == -1) {<!-- -->
            perror("server: socket");
            continue;
        }
        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
        sizeof(int)) == -1) {<!-- -->
            perror("setsockopt");
            exit(1);
        }
        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {<!-- -->
            close(sockfd);
            perror("server: bind");
            continue;
        }
        break;
    }
        if (p == NULL) {<!-- -->
            fprintf(stderr, "server: failed to bind\n");
            return 2;
        }
        freeaddrinfo(servinfo); // all done with this structure
        if (listen(sockfd, BACKLOG) == -1) {<!-- -->
            perror("listen");
            exit(1);
        }
        sa.sa_handler = sigchld_handler; // reap all dead processes
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_RESTART;
        if (sigaction(SIGCHLD, &sa, NULL) == -1) {<!-- -->
            perror("sigaction");
            exit(1);
        }
        printf("server: waiting for connections...\n");
        while(1) {<!-- --> // main accept() loop
            sin_size = sizeof their_addr;
            new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
            if (new_fd == -1) {<!-- -->
                perror("accept");
                continue;
            }
            inet_ntop(their_addr.ss_family,
            get_in_addr((struct sockaddr *)&their_addr),
            s, sizeof s);
            printf("server: got connection from %s\n", s);
            if (!fork()) {<!-- --> // this is the child process
                close(sockfd); // child doesn't need the listener
                if (send(new_fd, "Hello, world!", 13, 0) == -1)
                perror("send");
                close(new_fd);
                exit(0);
            }
            close(new_fd); // parent doesn't need this
        }
        return 0;
    }
  2. 然后编译并运行

    1
    2
    gcc Server1.c -o Server1
    ./Server1

  3. Ubuntu系统下,新建一个Client1.c文件,命令gedit Client1.c,然后写入如下内容

    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>

    #define PORT "9090"  //the port client will be connecting to
    #define MAXDATASIZE 100  //max number of bytes we can get at once

    //get sockaddr, IPv4 or IPv6
    void *get_in_addr(struct sockaddr *sa)
    {<!-- -->
        if(sa->sa_family == AF_INET)
        {<!-- -->
            return &(((struct sockaddr_in*)sa)->sin_addr);
        }
        return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }

    int main(int argc, char *argv[])
    {<!-- -->
        int sockfd, numbytes;
        char buf[MAXDATASIZE];
        struct addrinfo hints, *servinfo, *p;
        int rv;
        char s[INET6_ADDRSTRLEN];
        if(argc != 2)
        {<!-- -->
            fprintf(stderr, "usage:client hostname\n");
            exit(1);
        }
        memset(&hints, 0, sizeof hints);
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        if((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0)
        {<!-- -->
            fprintf(stderr, "getaddrinfo:%s\n",gai_strerror(rv));
            return 1;
        }
        // loop through all the results and connect to the first we can
        for(p = servinfo; p != NULL; p = p->ai_next)
        {<!-- -->
            if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
            {<!-- -->
                perror("client:socket");
                continue;
            }
            if(connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
            {<!-- -->
                close(sockfd);
                perror("client:connect");
                continue;
            }
            break;
        }
        if(p == NULL)
        {<!-- -->
            fprintf(stderr, "client:failed to connect\n");
            return 2;
        }
        inet_ntop(p->ai_family, get_in_addr((struct sockaddr*)p->ai_addr), s, sizeof s);
        printf("client:connecting to %s\n",s);
        freeaddrinfo(servinfo);// all done with this structure
        if((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1)
        {<!-- -->
            perror("recv");
            exit(1);
        }
        buf[numbytes] = '\0';
        printf("client:received %s\n",buf);
        close(sockfd);
        return 0;
    }
  4. 然后编译并运行

    1
    2
    gcc Client1.c -o Client1
    ./Client1 192.168.43.161

    注意:这里后面跟的IP是自己服务端的IP,即,自己树莓派的IP

  5. 运行结果如下

三、非阻塞的多人聊天服务器端例子

  1. 树莓派下,新建一个Server2.c文件,命令nano Server2.c,然后写入如下内容

    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
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>

    #define PORT "9090"  //port we're listening on

    //get sockaddr,IPv4 or IPv6:
    void *get_in_addr(struct sockaddr *sa)
    {<!-- -->
        if(sa->sa_family == AF_INET){<!-- -->
            return &(((struct sockaddr_in*)sa)->sin_addr);
        }
        return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }

    int main(void)
    {<!-- -->
        fd_set master;
        // master file descriptor list
        fd_set read_fds; // temp file descriptor list for select()
        int fdmax;
        // maximum file descriptor number
        int listener;
        // listening socket descriptor
        int newfd;
        // newly accept()ed socket descriptor
        struct sockaddr_storage remoteaddr; // client address
        socklen_t addrlen;
        char buf[256];
        // buffer for client data
        int nbytes;
        char remoteIP[INET6_ADDRSTRLEN];
        int yes=1;
        // for setsockopt() SO_REUSEADDR, below
        int i, j, rv;
        struct addrinfo hints, *ai, *p;
        FD_ZERO(&master);
        // clear the master and temp sets
        FD_ZERO(&read_fds);
        // get us a socket and bind it
        memset(&hints, 0, sizeof hints);
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_flags = AI_PASSIVE;
        if((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0)
        {<!-- -->
            fprintf(stderr, "selectserver:%s\n", gai_strerror(rv));
            exit(1);
        }

        for(p = ai; p != NULL; p = p->ai_next)
        {<!-- -->
            listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
            if(listener < 0)
            {<!-- -->
                continue;
            }
            // lose the pesky "address already in use" error message
            setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
            if(bind(listener, p->ai_addr, p->ai_addrlen) < 0)
            {<!-- -->
                close(listener);
                continue;
            }
            break;
        }
        // if we got here, it means we didn't get bound
        if(p == NULL)
        {<!-- -->
            fprintf(stderr, "selectserver:failed to bind\n");
            exit(2);
        }

        freeaddrinfo(ai);  // all done with this
        // listen
        if(listen(listener, 10) == -1)
        {<!-- -->
            perror("listen");
            exit(3);
        }
        // add the listener to the master set
        FD_SET(listener, &master);
        // keep track of the biggest file descriptor
        fdmax = listener;  // so far, it's this one
        // main loop
        for(;;)
        {<!-- -->
            read_fds = master; // copy it
            if(select(fdmax + 1, &read_fds, NULL, NULL, NULL) == -1)
            {<!-- -->
                perror("select");
                exit(4);
            }
            // run through the existing connections looking for data to read
            for(i = 0; i <= fdmax; i++)
            {<!-- -->
                if(FD_ISSET(i, &read_fds))// we got one!!
                {<!-- -->
                    if(i == listener)
                    {<!-- -->
                        // handle new connections
                        addrlen = sizeof remoteaddr;
                        newfd = accept(listener, (struct sockaddr *)&remoteaddr, &addrlen);

                        if(newfd == -1)
                        {<!-- -->
                            perror("accept");
                        }
                        else
                        {<!-- -->
                            FD_SET(newfd, &master); // add to master set
                            if(newfd > fdmax)
                            {<!-- -->
                                // keep track of the max
                                fdmax = newfd;
                            }
                            printf("selectserver: new connection from %s on "
                            "socket %d\n",
                            inet_ntop(remoteaddr.ss_family,
                            get_in_addr((struct sockaddr*)&remoteaddr),
                            remoteIP, INET6_ADDRSTRLEN),
                            newfd);
                        }
                    }
                    else
                    {<!-- -->
                        // handle data from a client
                        if((nbytes = recv(i, buf, sizeof buf, 0)) <= 0)
                        {<!-- -->
                            // got error or connection closed by client
                            if(nbytes == 0)
                            {<!-- -->
                                // connection closed
                                printf("selectserver:socket %d hung up\n", i);
                            }
                            else
                            {<!-- -->
                                perror("recv");
                            }
                            close(i);// bye!
                            FD_CLR(i, &master);// remove from master set
                        }
                        else
                        {<!-- -->
                            // we got some data from a client
                            for(j =0; j <= fdmax; j++)
                            {<!-- -->
                                // send to everyone!
                                if(FD_ISSET(j, &master))
                                {<!-- -->
                                     // except the listener and ourselves
                                    if(j != listener && j != i)
                                    {<!-- -->
                                        if(send(j, buf, nbytes, 0) == -1)
                                        {<!-- -->
                                            perror("send");
                                        }
                                    }
                                }
                            }
                        }
                    }  //END handle from client
                }  //END got new incoming connection
            }  //END looping through file descriptors
        }  //END for(;;)--and you thought it would never end!
        return 0;
    }

  2. 编译并运行

    1
    2
    gcc Server2.c -o Server2
    ./Server2
  3. Ubuntu下新建一个Client2.c文件,命令gedit Client2.c,然后写入如下内容

    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>

    #define PORT "9090"  //the port client will be connecting to
    #define MAXDATASIZE 100  //max number of bytes we can get at once

    int sockfd, numbytes;
    char buf[MAXDATASIZE];

    //get sockaddr, IPv4 or IPv6
    void *get_in_addr(struct sockaddr *sa)
    {<!-- -->
        if(sa->sa_family == AF_INET)
        {<!-- -->
            return &(((struct sockaddr_in*)sa)->sin_addr);
        }
        return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }

    void *recvMag()
    {<!-- -->
        while(1)
        {<!-- -->
            if((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1)
        {<!-- -->
            perror("recv");
            exit(1);
        }
        if(numbytes == 1)
            continue;
        buf[numbytes] = '\0';
        printf("\nreceived:%s\n",buf);
        }
    }

    int main(int argc, char *argv[])
    {<!-- -->
        //int sockfd, numbytes;
        //char buf[MAXDATASIZE];
        struct addrinfo hints, *servinfo, *p;
        int rv;
        char s[INET6_ADDRSTRLEN];
        pthread_t t1;
        char mag[MAXDATASIZE];
       
        if(argc != 2)
        {<!-- -->
            fprintf(stderr, "usage:client hostname\n");
            exit(1);
        }
        memset(&hints, 0, sizeof hints);
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        if((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0)
        {<!-- -->
            fprintf(stderr, "getaddrinfo:%s\n",gai_strerror(rv));
            return 1;
        }
        // loop through all the results and connect to the first we can
        for(p = servinfo; p != NULL; p = p->ai_next)
        {<!-- -->
            if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
            {<!-- -->
                perror("client:socket");
                continue;
            }
            if(connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
            {<!-- -->
                close(sockfd);
                perror("client:connect");
                continue;
            }
            break;
        }
        if(p == NULL)
        {<!-- -->
            fprintf(stderr, "client:failed to connect\n");
            return 2;
        }
        inet_ntop(p->ai_family, get_in_addr((struct sockaddr*)p->ai_addr), s, sizeof s);
        printf("client:connecting to %s\n",s);
        freeaddrinfo(servinfo);// all done with this structure
       
        int err = pthread_create(&t1, NULL, recvMag, NULL);
        if(err != 0)
        {<!-- -->
            printf("receive failed");
            exit(1);
        }
       
        while(1)
        {<!-- -->
            scanf("%s", mag);
            if(send(sockfd, mag, sizeof mag, 0) == -1)
            {<!-- -->
                printf("send failed!\n");
            }
        }
        return 0;
    }

    注意:因为是一个多人聊天程序,所以这里至少应该有两个客户端,本人是重新再开个Ubuntu的虚拟机,重复客户端的操作

  4. 编译并运行

    1
    2
    gcc -pthread Client2.c -o Client2
    ./Client2 192.168.43.161
  5. 运行结果如下

    ① 客户端

    ② 服务端

四、简单的 IPv6 UDP socket编程

  1. 树莓派Ubuntu下查看自己的IPv6地址地址,ifconfig,然后再看双方能不能ping

  2. 树莓派下新建一个Server4.c文件,命令nano Server4.c,然后写入如下内容

    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <arpa/inet.h>
     
    int main(int argc, char **argv)
    {<!-- -->
        struct sockaddr_in6 s_addr;
        struct sockaddr_in6 c_addr;
        int sock;
        socklen_t addr_len;
        int len;
        char buff[128];
        char buf_ip[128];
     
        if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {<!-- -->
            perror("socket");
            exit(errno);
        } else
            printf("create socket.\n\r");
     
        memset(&s_addr, 0, sizeof(struct sockaddr_in6));
        s_addr.sin6_family = AF_INET6;
     
        if (argv[2])
            s_addr.sin6_port = htons(atoi(argv[2]));
        else
            s_addr.sin6_port = htons(4444);
     
        if (argv[1])
            inet_pton(AF_INET6, argv[1], &s_addr.sin6_addr);
        else
            s_addr.sin6_addr = in6addr_any;
     
        if ((bind(sock, (struct sockaddr *) &s_addr, sizeof(s_addr))) == -1) {<!-- -->
            perror("bind");
            exit(errno);
        } else
            printf("bind address to socket.\n\r");
     
        addr_len = sizeof(c_addr);
        while (1) {<!-- -->
            len = recvfrom(sock, buff, sizeof(buff) - 1, 0,
                           (struct sockaddr *) &c_addr, &addr_len);
            if (len < 0) {<!-- -->
                perror("recvfrom");
                exit(errno);
            }
     
            buff[len] = '\0';
            printf("receive from %s: buffer:%s\n\r",
                    inet_ntop(AF_INET6, &c_addr.sin6_addr, buf_ip, sizeof(buf_ip)),
                    buff);
        }
        return 0;
    }
  3. 编译运行

    1
    2
    gcc Server4.c -o server4
    ./server4 2409:8960:1e48:f1a:b6a4:81d5:7021:4873 9090
  4. Ubuntu下,新建一个Client4.c文件,然后写入如下内容

    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 <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <arpa/inet.h>
     
    int main(int argc, char **argv)
    {<!-- -->
        struct sockaddr_in6 s_addr;
        int sock;
        int addr_len;
        int len;
        char buff[128];
     
        if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {<!-- -->
            perror("socket");
            exit(errno);
        } else
            printf("create socket.\n\r");
     
        s_addr.sin6_family = AF_INET6;
        if (argv[2])
            s_addr.sin6_port = htons(atoi(argv[2]));
        else
            s_addr.sin6_port = htons(4444);
     
        if (argv[1])
            inet_pton(AF_INET6, argv[1], &s_addr.sin6_addr);
        else {<!-- -->
            printf("usage:./command ip port\n");
            exit(0);
        }
     
        addr_len = sizeof(s_addr);
        strcpy(buff, "hello i'm here");
        len = sendto(sock, buff, strlen(buff), 0,
                     (struct sockaddr *) &s_addr, addr_len);
        if (len < 0) {<!-- -->
            printf("\n\rsend error.\n\r");
            return 3;
        }
     
        printf("send success.\n\r");
        return 0;
    }
  5. 编译运行

    1
    2
    gcc Client4.c -o Client4
    ./Client4 2409:8960:1e48:f1a:b6a4:81d5:7021:4873 9090
  6. 运行结果如下

    ① 客户端

    ② 服务端

  7. wireshark抓包

四、参考书籍和??

  • Ubuntu 与树莓派之间的两个 socket 应用实例:C/S 和多人聊天
  • 《网络编程技术》
  • 简单的IPv6 UDP/TCP socket编程 – 两台Linux实现简单的ipv6通信