Linux网络编程(四 )

前言:前面三篇文章的面试总结

掌握:

第一部分

1、静态库和动态的区别和各自的使用命令,加载失败问题和解决。

2、GDB如何多线程调试,调试命令。

3、虚拟地址空间的定义和含有的内容,文件描述符。

4、API函数,需要会结合man命令学习各个函数,其中重要的有dup,dup2函数,fcntl函数用于修改文件描述符的指令。

第二部分

1、进程的状态的转换,转换的条件等

2、fork函数,父子进程虚拟地址空间,exec函数族去执行一个命令,把当前用户区的内容替换成新的内容。

3、进程退出、孤儿进程、僵尸进程定义,孤儿进程和僵尸进程的弊端,如何解决僵尸进程,采用回收子进程的资源(wait/waitpid函数),waitpid可以设置成非阻塞的。

4、进程间通信的方式和实现代码,方式有匿名管道、有名管道、内存映射、消息队列、信号、共享内存。每种方式的原理或者流程需要掌握。

5、信号方面,掌握定时器、信号捕捉 去捕捉信号做信号处理、SIGCHLD信号 子进程在结束或者状态发生改变的时候会给父进程发送一个SIGCHLD信号。

6、守护进程的定义,守护进程是后台一直运行的后台进程,周期性的去执行某些事情,守护进程的流程步骤。

7、补充:进程的调度(策略和算法)

第三部分

1、掌握API ,创建、终止、连接已终止、分离、取消线程。

2、线程和进程的区别,线程是共享一个虚拟地址空间,进程是fork出来一个新的虚拟地址空间,写时复制,读时共享的特点

3、线程同步的定义,几种方式,互斥锁和读写锁和悲观锁等锁。

4、生产者消费者模型,其中,在保证容器满了,生产者停下来,消费者消费,是通过条件变量或者信号量可以实现。

第四部分

1、了解BS和CS架构模式,IP和端口,网络模型,协议(看计算机网络的书籍)。

2、掌握ISO七层网络模型和TCP/IP四层网络模型(TCP/IP协议的书籍)

3、字节序,IP操作函数,sockaddr数据结构,掌握系统的API的使用,会问到字节序定义(UNIX网络编程的书籍)

4、TCP实现服务器和客户端通信  三次握手,滑动窗口,四次挥手,通信并发,状态转换,半关闭,端口复用,说概念,不涉及具体代码

5、IO多路转接-select、poll、epoll(服务器开发和后台开发80%会问这个问题)

6、面试不常问,简单了解 UDP通信,广播,组播,本地套接字

 

Linux网络编程(四)

(最重要的章节)

网络结构模式

c/s和b/s模式,掌握各自的定义和优缺点

 

 

 

 

 

MAC地址、IP地址、端口

AMC地址

 

 MAC地址是网卡上的唯一标识。

IP地址(重要)

 

 IP地址

 

 IP地址是32位的二进制数。

IP地址编址方式

 

 

 

 

 

 

 

 

 子网掩码

 

 

 

 端口

 

 端口相当于读写缓冲区,端口号相当于进程的编号,通过端口号找到进程。

一个应用程序可以有多个端口,比如QQ可以有通话的端口、视频的端口、聊天打字的端口等。

 

网络模型

OSI模型(七层模型)

 

 

 

 CP/IP四层模型

 

 

 

 

 

 协议

 

 UDP协议

 

 TCP协议

 

 

 

 IP协议

 

 

 

 以太网帧协议

 

 ARP协议

 

 封装

 

 分用

 

 

 

 

 

 网络通信的过程

 

 

问:网络通信刚开始封装的时候是如何知道目地主机的MAC地址和IP地址的啊?

答:ip地址查询dns服务器获得,然后通过ip找到子网,子网进行一个广播找到指定目的主机,目的主机返回mac地址,后面就知道mac地址了。

目的端MAC地址是通过ARP协议获得。

 

ARP请求封装

 ARP报文长度28字节

 

socket介绍

 

 

 

 

 

 

 字节序

 

 

 

 

 

 字节序转换函数

 

 

 

 socket地址

 

 通用socket地址

 

 

 

 专用socket地址

 

 

 

 

 

 

 

 

IP地址转换(字符串ip-整数 ,主机、网络字节序的转换)

 

 

 

 

TCP通信流程

 

 

 

 

 

 

 

 套接字函数

 1 #include <sys/types.h>
 2 #include <sys/socket.h>
 3 #include <arpa/inet.h> // 包含了这个头文件,上面两个就可以省略
 4 int socket(int domain, int type, int protocol);
 5 - 功能:创建一个套接字
 6 - 参数:
 7 - domain: 协议族
 8 AF_INET : ipv4
 9 AF_INET6 : ipv6
10 AF_UNIX, AF_LOCAL : 本地套接字通信(进程间通信)
11 - type: 通信过程中使用的协议类型
12 SOCK_STREAM : 流式协议
13 SOCK_DGRAM : 报式协议
14 - protocol : 具体的一个协议。一般写0
15 - SOCK_STREAM : 流式协议默认使用 TCP
16 - SOCK_DGRAM : 报式协议默认使用 UDP
17 - 返回值:
18 - 成功:返回文件描述符,操作的就是内核缓冲区。
19 - 失败:-1
20 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); // socket命
21 22 - 功能:绑定,将fd 和本地的IP + 端口进行绑定
23 - 参数:
24 - sockfd : 通过socket函数得到的文件描述符
25 - addr : 需要绑定的socket地址,这个地址封装了ip和端口号的信息
26 - addrlen : 第二个参数结构体占的内存大小
27 int listen(int sockfd, int backlog); // /proc/sys/net/core/somaxconn
28 - 功能:监听这个socket上的连接
29 - 参数:
30 - sockfd : 通过socket()函数得到的文件描述符
31 - backlog : 未连接的和已经连接的和的最大值, 5
32 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
33 - 功能:接收客户端连接,默认是一个阻塞的函数,阻塞等待客户端连接
34 - 参数:
35 - sockfd : 用于监听的文件描述符
36 - addr : 传出参数,记录了连接成功后客户端的地址信息(ip,port)
37 - addrlen : 指定第二个参数的对应的内存大小
38 - 返回值:
39 - 成功 :用于通信的文件描述符
40 - -1 : 失败
41 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
42 - 功能: 客户端连接服务器
43 - 参数:
44 - sockfd : 用于通信的文件描述符
45 - addr : 客户端要连接的服务器的地址信息
46 - addrlen : 第二个参数的内存大小
47 - 返回值:成功 0, 失败 -1
48 ssize_t write(int fd, const void *buf, size_t count); // 写数据
49 ssize_t read(int fd, void *buf, size_t count); // 读数据

 

TCP通信实现(服务器端)

 1 // TCP 通信的服务器端
 2 
 3 #include <stdio.h>
 4 #include <arpa/inet.h>
 5 #include <unistd.h>
 6 #include <string.h>
 7 #include <stdlib.h>
 8 
 9 int main() {
10 
11     // 1.创建socket(用于监听的套接字)
12     int lfd = socket(AF_INET, SOCK_STREAM, 0);
13 
14     if(lfd == -1) {
15         perror("socket");
16         exit(-1);
17     }
18 
19     // 2.绑定
20     struct sockaddr_in saddr;
21     saddr.sin_family = AF_INET;
22     // inet_pton(AF_INET, "192.168.193.128", saddr.sin_addr.s_addr);
23     saddr.sin_addr.s_addr = INADDR_ANY;  // 0.0.0.0
24     saddr.sin_port = htons(9999);
25     int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
26 
27     if(ret == -1) {
28         perror("bind");
29         exit(-1);
30     }
31 
32     // 3.监听
33     ret = listen(lfd, 8);
34     if(ret == -1) {
35         perror("listen");
36         exit(-1);
37     }
38 
39     // 4.接收客户端连接
40     struct sockaddr_in clientaddr;
41     int len = sizeof(clientaddr);
42     int cfd = accept(lfd, (struct sockaddr *)&clientaddr, &len);
43     
44     if(cfd == -1) {
45         perror("accept");
46         exit(-1);
47     }
48 
49     // 输出客户端的信息
50     char clientIP[16];
51     inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, clientIP, sizeof(clientIP));
52     unsigned short clientPort = ntohs(clientaddr.sin_port);
53     printf("client ip is %s, port is %d\n", clientIP, clientPort);
54 
55     // 5.通信
56     char recvBuf[1024] = {0};
57     while(1) {
58         
59         // 获取客户端的数据
60         int num = read(cfd, recvBuf, sizeof(recvBuf));
61         if(num == -1) {
62             perror("read");
63             exit(-1);
64         } else if(num > 0) {
65             printf("recv client data : %s\n", recvBuf);
66         } else if(num == 0) {
67             // 表示客户端断开连接
68             printf("clinet closed...");
69             break;
70         }
71 
72         char * data = "hello,i am server";
73         // 给客户端发送数据
74         write(cfd, data, strlen(data));
75     }
76    
77     // 关闭文件描述符
78     close(cfd);
79     close(lfd);
80 
81     return 0;
82 }

 

TCP通信实现(客户端)

代码

 1 // TCP通信的客户端
 2 
 3 #include <stdio.h>
 4 #include <arpa/inet.h>
 5 #include <unistd.h>
 6 #include <string.h>
 7 #include <stdlib.h>
 8 
 9 int main() {
10 
11     // 1.创建套接字
12     int fd = socket(AF_INET, SOCK_STREAM, 0);
13     if(fd == -1) {
14         perror("socket");
15         exit(-1);
16     }
17 
18     // 2.连接服务器端
19     struct sockaddr_in serveraddr;
20     serveraddr.sin_family = AF_INET;
21     inet_pton(AF_INET, "192.168.193.128", &serveraddr.sin_addr.s_addr);
22     serveraddr.sin_port = htons(9999);
23     int ret = connect(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
24 
25     if(ret == -1) {
26         perror("connect");
27         exit(-1);
28     }
29 
30     
31     // 3. 通信
32     char recvBuf[1024] = {0};
33     while(1) {
34 
35         char * data = "hello,i am client";
36         // 给客户端发送数据
37         write(fd, data , strlen(data));
38 
39         sleep(1);
40         
41         int len = read(fd, recvBuf, sizeof(recvBuf));
42         if(len == -1) {
43             perror("read");
44             exit(-1);
45         } else if(len > 0) {
46             printf("recv server data : %s\n", recvBuf);
47         } else if(len == 0) {
48             // 表示服务器端断开连接
49             printf("server closed...");
50             break;
51         }
52 
53     }
54 
55     // 关闭连接
56     close(fd);
57 
58     return 0;
59 }

 

课后练习:回射服务器

 

TCP三次握手

 

 

ack:确认

syn:连接

fin:断开连接

 

 

 

 

 

 

 

 

 

TCP滑动窗口

 

 

注意:窗口理解为缓冲区的大小,当然,窗口不是缓冲区。

 

 

 

 

 

 

 

TCP四次挥手

 

 

 

TCP 通信并发

TCP 状态转换

 

 

 

 

 

 

从程序的角度,可以使用API来控制实现半连接状态:

 

 

 

 

 代码

client.c

 1 // TCP通信的客户端
 2 #include <stdio.h>
 3 #include <arpa/inet.h>
 4 #include <unistd.h>
 5 #include <string.h>
 6 #include <stdlib.h>
 7 
 8 int main() {
 9 
10     // 1.创建套接字
11     int fd = socket(AF_INET, SOCK_STREAM, 0);
12     if(fd == -1) {
13         perror("socket");
14         exit(-1);
15     }
16 
17     // 2.连接服务器端
18     struct sockaddr_in serveraddr;
19     serveraddr.sin_family = AF_INET;
20     inet_pton(AF_INET, "192.168.193.128", &serveraddr.sin_addr.s_addr);
21     serveraddr.sin_port = htons(9999);
22     int ret = connect(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
23 
24     if(ret == -1) {
25         perror("connect");
26         exit(-1);
27     }
28     
29     // 3. 通信
30     char recvBuf[1024];
31     int i = 0;
32     while(1) {
33         
34         sprintf(recvBuf, "data : %d\n", i++);
35         
36         // 给服务器端发送数据
37         write(fd, recvBuf, strlen(recvBuf)+1);
38 
39         int len = read(fd, recvBuf, sizeof(recvBuf));
40         if(len == -1) {
41             perror("read");
42             exit(-1);
43         } else if(len > 0) {
44             printf("recv server : %s\n", recvBuf);
45         } else if(len == 0) {
46             // 表示服务器端断开连接
47             printf("server closed...");
48             break;
49         }
50 
51         sleep(1);
52     }
53 
54     // 关闭连接
55     close(fd);
56 
57     return 0;
58 }

server.c

  1 #include <stdio.h>
  2 #include <arpa/inet.h>
  3 #include <unistd.h>
  4 #include <stdlib.h>
  5 #include <string.h>
  6 #include <signal.h>
  7 #include <wait.h>
  8 #include <errno.h>
  9 
 10 void recyleChild(int arg) {
 11     while(1) {
 12         int ret = waitpid(-1, NULL, WNOHANG);
 13         if(ret == -1) {
 14             // 所有的子进程都回收了
 15             break;
 16         }else if(ret == 0) {
 17             // 还有子进程活着
 18             break;
 19         } else if(ret > 0){
 20             // 被回收了
 21             printf("子进程 %d 被回收了\n", ret);
 22         }
 23     }
 24 }
 25 
 26 int main() {
 27 
 28     struct sigaction act;
 29     act.sa_flags = 0;
 30     sigemptyset(&act.sa_mask);
 31     act.sa_handler = recyleChild;
 32     // 注册信号捕捉
 33     sigaction(SIGCHLD, &act, NULL);
 34     
 35 
 36     // 创建socket
 37     int lfd = socket(PF_INET, SOCK_STREAM, 0);
 38     if(lfd == -1){
 39         perror("socket");
 40         exit(-1);
 41     }
 42 
 43     struct sockaddr_in saddr;
 44     saddr.sin_family = AF_INET;
 45     saddr.sin_port = htons(9999);
 46     saddr.sin_addr.s_addr = INADDR_ANY;
 47 
 48     // 绑定
 49     int ret = bind(lfd,(struct sockaddr *)&saddr, sizeof(saddr));
 50     if(ret == -1) {
 51         perror("bind");
 52         exit(-1);
 53     }
 54 
 55     // 监听
 56     ret = listen(lfd, 128);
 57     if(ret == -1) {
 58         perror("listen");
 59         exit(-1);
 60     }
 61 
 62     // 不断循环等待客户端连接
 63     while(1) {
 64 
 65         struct sockaddr_in cliaddr;
 66         int len = sizeof(cliaddr);
 67         // 接受连接
 68         int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len);
 69         if(cfd == -1) {
 70             if(errno == EINTR) {
 71                 continue;
 72             }
 73             perror("accept");
 74             exit(-1);
 75         }
 76 
 77         // 每一个连接进来,创建一个子进程跟客户端通信
 78         pid_t pid = fork();
 79         if(pid == 0) {
 80             // 子进程
 81             // 获取客户端的信息
 82             char cliIp[16];
 83             inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, cliIp, sizeof(cliIp));
 84             unsigned short cliPort = ntohs(cliaddr.sin_port);
 85             printf("client ip is : %s, prot is %d\n", cliIp, cliPort);
 86 
 87             // 接收客户端发来的数据
 88             char recvBuf[1024];
 89             while(1) {
 90                 int len = read(cfd, &recvBuf, sizeof(recvBuf));
 91 
 92                 if(len == -1) {
 93                     perror("read");
 94                     exit(-1);
 95                 }else if(len > 0) {
 96                     printf("recv client : %s\n", recvBuf);
 97                 } else if(len == 0) {
 98                     printf("client closed....\n");
 99                     break;
100                 }
101                 write(cfd, recvBuf, strlen(recvBuf) + 1);
102             }
103             close(cfd);
104             exit(0);    // 退出当前子进程
105         }
106 
107     }
108     close(lfd);
109     return 0;
110 }

 

多线程实现并发服务器

  1 #include <stdio.h>
  2 #include <arpa/inet.h>
  3 #include <unistd.h>
  4 #include <stdlib.h>
  5 #include <string.h>
  6 #include <pthread.h>
  7 
  8 struct sockInfo {
  9     int fd; // 通信的文件描述符
 10     struct sockaddr_in addr;
 11     pthread_t tid;  // 线程号
 12 };
 13 
 14 struct sockInfo sockinfos[128];
 15 
 16 void * working(void * arg) {
 17     // 子线程和客户端通信   cfd 客户端的信息 线程号
 18     // 获取客户端的信息
 19     struct sockInfo * pinfo = (struct sockInfo *)arg;
 20 
 21     char cliIp[16];
 22     inet_ntop(AF_INET, &pinfo->addr.sin_addr.s_addr, cliIp, sizeof(cliIp));
 23     unsigned short cliPort = ntohs(pinfo->addr.sin_port);
 24     printf("client ip is : %s, prot is %d\n", cliIp, cliPort);
 25 
 26     // 接收客户端发来的数据
 27     char recvBuf[1024];
 28     while(1) {
 29         int len = read(pinfo->fd, &recvBuf, sizeof(recvBuf));
 30 
 31         if(len == -1) {
 32             perror("read");
 33             exit(-1);
 34         }else if(len > 0) {
 35             printf("recv client : %s\n", recvBuf);
 36         } else if(len == 0) {
 37             printf("client closed....\n");
 38             break;
 39         }
 40         write(pinfo->fd, recvBuf, strlen(recvBuf) + 1);
 41     }
 42     close(pinfo->fd);
 43     return NULL;
 44 }
 45 
 46 int main() {
 47 
 48     // 创建socket
 49     int lfd = socket(PF_INET, SOCK_STREAM, 0);
 50     if(lfd == -1){
 51         perror("socket");
 52         exit(-1);
 53     }
 54 
 55     struct sockaddr_in saddr;
 56     saddr.sin_family = AF_INET;
 57     saddr.sin_port = htons(9999);
 58     saddr.sin_addr.s_addr = INADDR_ANY;
 59 
 60     // 绑定
 61     int ret = bind(lfd,(struct sockaddr *)&saddr, sizeof(saddr));
 62     if(ret == -1) {
 63         perror("bind");
 64         exit(-1);
 65     }
 66 
 67     // 监听
 68     ret = listen(lfd, 128);
 69     if(ret == -1) {
 70         perror("listen");
 71         exit(-1);
 72     }
 73 
 74     // 初始化数据
 75     int max = sizeof(sockinfos) / sizeof(sockinfos[0]);
 76     for(int i = 0; i < max; i++) {
 77         bzero(&sockinfos[i], sizeof(sockinfos[i]));
 78         sockinfos[i].fd = -1;
 79         sockinfos[i].tid = -1;
 80     }
 81 
 82     // 循环等待客户端连接,一旦一个客户端连接进来,就创建一个子线程进行通信
 83     while(1) {
 84 
 85         struct sockaddr_in cliaddr;
 86         int len = sizeof(cliaddr);
 87         // 接受连接
 88         int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len);
 89 
 90         struct sockInfo * pinfo;
 91         for(int i = 0; i < max; i++) {
 92             // 从这个数组中找到一个可以用的sockInfo元素
 93             if(sockinfos[i].fd == -1) {
 94                 pinfo = &sockinfos[i];
 95                 break;
 96             }
 97             if(i == max - 1) {
 98                 sleep(1);
 99                 i--;
100             }
101         }
102 
103         pinfo->fd = cfd;
104         memcpy(&pinfo->addr, &cliaddr, len);
105 
106         // 创建子线程
107         pthread_create(&pinfo->tid, NULL, working, pinfo);
108 
109         pthread_detach(pinfo->tid);
110     }
111 
112     close(lfd);
113     return 0;
114 }

 

 半关闭

 

 

 

 

端口复用

 

 

 

 

代码

client.c

 1 #include <stdio.h>
 2 #include <arpa/inet.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <string.h>
 6 
 7 int main() {
 8 
 9     // 创建socket
10     int fd = socket(PF_INET, SOCK_STREAM, 0);
11     if(fd == -1) {
12         perror("socket");
13         return -1;
14     }
15 
16     struct sockaddr_in seraddr;
17     inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);
18     seraddr.sin_family = AF_INET;
19     seraddr.sin_port = htons(9999);
20 
21     // 连接服务器
22     int ret = connect(fd, (struct sockaddr *)&seraddr, sizeof(seraddr));
23 
24     if(ret == -1){
25         perror("connect");
26         return -1;
27     }
28 
29     while(1) {
30         char sendBuf[1024] = {0};
31         fgets(sendBuf, sizeof(sendBuf), stdin);
32 
33         write(fd, sendBuf, strlen(sendBuf) + 1);
34 
35         // 接收
36         int len = read(fd, sendBuf, sizeof(sendBuf));
37         if(len == -1) {
38             perror("read");
39             return -1;
40         }else if(len > 0) {
41             printf("read buf = %s\n", sendBuf);
42         } else {
43             printf("服务器已经断开连接...\n");
44             break;
45         }
46     }
47 
48     close(fd);
49 
50     return 0;
51 }

 

server.c

 1 #include <stdio.h>
 2 #include <ctype.h>
 3 #include <arpa/inet.h>
 4 #include <unistd.h>
 5 #include <stdlib.h>
 6 #include <string.h>
 7 
 8 int main(int argc, char *argv[]) {
 9 
10     // 创建socket
11     int lfd = socket(PF_INET, SOCK_STREAM, 0);
12 
13     if(lfd == -1) {
14         perror("socket");
15         return -1;
16     }
17 
18     struct sockaddr_in saddr;
19     saddr.sin_family = AF_INET;
20     saddr.sin_addr.s_addr = INADDR_ANY;
21     saddr.sin_port = htons(9999);
22     
23     //int optval = 1;
24     //setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
25 
26     int optval = 1;
27     setsockopt(lfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
28 
29     // 绑定
30     int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
31     if(ret == -1) {
32         perror("bind");
33         return -1;
34     }
35 
36     // 监听
37     ret = listen(lfd, 8);
38     if(ret == -1) {
39         perror("listen");
40         return -1;
41     }
42 
43     // 接收客户端连接
44     struct sockaddr_in cliaddr;
45     socklen_t len = sizeof(cliaddr);
46     int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);
47     if(cfd == -1) {
48         perror("accpet");
49         return -1;
50     }
51 
52     // 获取客户端信息
53     char cliIp[16];
54     inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, cliIp, sizeof(cliIp));
55     unsigned short cliPort = ntohs(cliaddr.sin_port);
56 
57     // 输出客户端的信息
58     printf("client's ip is %s, and port is %d\n", cliIp, cliPort );
59 
60     // 接收客户端发来的数据
61     char recvBuf[1024] = {0};
62     while(1) {
63         int len = recv(cfd, recvBuf, sizeof(recvBuf), 0);
64         if(len == -1) {
65             perror("recv");
66             return -1;
67         } else if(len == 0) {
68             printf("客户端已经断开连接...\n");
69             break;
70         } else if(len > 0) {
71             printf("read buf = %s\n", recvBuf);
72         }
73 
74         // 小写转大写
75         for(int i = 0; i < len; ++i) {
76             recvBuf[i] = toupper(recvBuf[i]);
77         }
78 
79         printf("after buf = %s\n", recvBuf);
80 
81         // 大写字符串发给客户端
82         ret = send(cfd, recvBuf, strlen(recvBuf) + 1, 0);
83         if(ret == -1) {
84             perror("send");
85             return -1;
86         }
87     }
88     
89     close(cfd);
90     close(lfd);
91 
92     return 0;
93 }

 

I/O多路复用(I/O多路转接)

I/O 多路复用使得程序能同时监听多个文件描述符,能够提高程序的性能,Linux 下实现 I/O 多路复用的系统调用主要有 select、poll 和 epoll。

 

1、阻塞方式:

 

 

 

 

2、非阻塞的方式:

 

 

 

 

 

3、IO多路复用方式:

 

 

 

 

select API介绍

 

 

 代码

 1 // sizeof(fd_set) = 128 1024
 2 #include <sys/time.h>
 3 #include <sys/types.h>
 4 #include <unistd.h>
 5 #include <sys/select.h>
 6 int select(int nfds, fd_set *readfds, fd_set *writefds,
 7 fd_set *exceptfds, struct timeval *timeout);
 8     - 参数:
 9     - nfds : 委托内核检测的最大文件描述符的值 + 1
10     - readfds : 要检测的文件描述符的读的集合,委托内核检测哪些文件描述符的读的属性
11         - 一般检测读操作
12         - 对应的是对方发送过来的数据,因为读是被动的接收数据,检测的就是读缓冲
13 14         - 是一个传入传出参数
15     - writefds : 要检测的文件描述符的写的集合,委托内核检测哪些文件描述符的写的属性
16         - 委托内核检测写缓冲区是不是还可以写数据(不满的就可以写)
17     - exceptfds : 检测发生异常的文件描述符的集合
18     - timeout : 设置的超时时间
19         struct timeval {
20         long tv_sec; /* seconds */
21         long tv_usec; /* microseconds */
22         };
23         - NULL : 永久阻塞,直到检测到了文件描述符有变化
24         - tv_sec = 0 tv_usec = 0, 不阻塞
25         - tv_sec > 0 tv_usec > 0, 阻塞对应的时间
26     - 返回值 :
27         - -1 : 失败
28         - >0(n) : 检测的集合中有n个文件描述符发生了变化
29 // 将参数文件描述符fd对应的标志位设置为0
30 void FD_CLR(int fd, fd_set *set);
31 // 判断fd对应的标志位是0还是1, 返回值 : fd对应的标志位的值,0,返回0, 1,返回1
32 int FD_ISSET(int fd, fd_set *set);
33 // 将参数文件描述符fd 对应的标志位,设置为1
34 void FD_SET(int fd, fd_set *set);
35 // fd_set一共有1024 bit, 全部初始化为0
36 void FD_ZERO(fd_set *set);

 

 

 

select代码编写

 代码

client.c

 1 #include <stdio.h>
 2 #include <arpa/inet.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <string.h>
 6 
 7 int main() {
 8 
 9     // 创建socket
10     int fd = socket(PF_INET, SOCK_STREAM, 0);
11     if(fd == -1) {
12         perror("socket");
13         return -1;
14     }
15 
16     struct sockaddr_in seraddr;
17     inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);
18     seraddr.sin_family = AF_INET;
19     seraddr.sin_port = htons(9999);
20 
21     // 连接服务器
22     int ret = connect(fd, (struct sockaddr *)&seraddr, sizeof(seraddr));
23 
24     if(ret == -1){
25         perror("connect");
26         return -1;
27     }
28 
29     int num = 0;
30     while(1) {
31         char sendBuf[1024] = {0};
32         sprintf(sendBuf, "send data %d", num++);
33         write(fd, sendBuf, strlen(sendBuf) + 1);
34 
35         // 接收
36         int len = read(fd, sendBuf, sizeof(sendBuf));
37         if(len == -1) {
38             perror("read");
39             return -1;
40         }else if(len > 0) {
41             printf("read buf = %s\n", sendBuf);
42         } else {
43             printf("服务器已经断开连接...\n");
44             break;
45         }
46         // sleep(1);
47         usleep(1000);
48     }
49 
50     close(fd);
51 
52     return 0;
53 }

 

select.c

 1 #include <stdio.h>
 2 #include <arpa/inet.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <string.h>
 6 #include <sys/select.h>
 7 
 8 int main() {
 9 
10     // 创建socket
11     int lfd = socket(PF_INET, SOCK_STREAM, 0);
12     struct sockaddr_in saddr;
13     saddr.sin_port = htons(9999);
14     saddr.sin_family = AF_INET;
15     saddr.sin_addr.s_addr = INADDR_ANY;
16 
17     // 绑定
18     bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
19 
20     // 监听
21     listen(lfd, 8);
22 
23     // 创建一个fd_set的集合,存放的是需要检测的文件描述符
24     fd_set rdset, tmp;
25     FD_ZERO(&rdset);
26     FD_SET(lfd, &rdset);
27     int maxfd = lfd;
28 
29     while(1) {
30 
31         tmp = rdset;
32 
33         // 调用select系统函数,让内核帮检测哪些文件描述符有数据
34         int ret = select(maxfd + 1, &tmp, NULL, NULL, NULL);
35         if(ret == -1) {
36             perror("select");
37             exit(-1);
38         } else if(ret == 0) {
39             continue;
40         } else if(ret > 0) {
41             // 说明检测到了有文件描述符的对应的缓冲区的数据发生了改变
42             if(FD_ISSET(lfd, &tmp)) {
43                 // 表示有新的客户端连接进来了
44                 struct sockaddr_in cliaddr;
45                 int len = sizeof(cliaddr);
46                 int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);
47 
48                 // 将新的文件描述符加入到集合中
49                 FD_SET(cfd, &rdset);
50 
51                 // 更新最大的文件描述符
52                 maxfd = maxfd > cfd ? maxfd : cfd;
53             }
54 
55             for(int i = lfd + 1; i <= maxfd; i++) {
56                 if(FD_ISSET(i, &tmp)) {
57                     // 说明这个文件描述符对应的客户端发来了数据
58                     char buf[1024] = {0};
59                     int len = read(i, buf, sizeof(buf));
60                     if(len == -1) {
61                         perror("read");
62                         exit(-1);
63                     } else if(len == 0) {
64                         printf("client closed...\n");
65                         close(i);
66                         FD_CLR(i, &rdset);
67                     } else if(len > 0) {
68                         printf("read buf = %s\n", buf);
69                         write(i, buf, strlen(buf) + 1);
70                     }
71                 }
72             }
73 
74         }
75 
76     }
77     close(lfd);
78     return 0;
79 }

 

poll API介绍及代码编写

 

 

 

 代码

client.c

 1 #include <stdio.h>
 2 #include <arpa/inet.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <string.h>
 6 
 7 int main() {
 8 
 9     // 创建socket
10     int fd = socket(PF_INET, SOCK_STREAM, 0);
11     if(fd == -1) {
12         perror("socket");
13         return -1;
14     }
15 
16     struct sockaddr_in seraddr;
17     inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);
18     seraddr.sin_family = AF_INET;
19     seraddr.sin_port = htons(9999);
20 
21     // 连接服务器
22     int ret = connect(fd, (struct sockaddr *)&seraddr, sizeof(seraddr));
23 
24     if(ret == -1){
25         perror("connect");
26         return -1;
27     }
28 
29     int num = 0;
30     while(1) {
31         char sendBuf[1024] = {0};
32         sprintf(sendBuf, "send data %d", num++);
33         write(fd, sendBuf, strlen(sendBuf) + 1);
34 
35         // 接收
36         int len = read(fd, sendBuf, sizeof(sendBuf));
37         if(len == -1) {
38             perror("read");
39             return -1;
40         }else if(len > 0) {
41             printf("read buf = %s\n", sendBuf);
42         } else {
43             printf("服务器已经断开连接...\n");
44             break;
45         }
46         // sleep(1);
47         usleep(1000);
48     }
49 
50     close(fd);
51 
52     return 0;
53 }

 

poll.c

 1 #include <stdio.h>
 2 #include <arpa/inet.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <string.h>
 6 #include <poll.h>
 7 
 8 
 9 int main() {
10 
11     // 创建socket
12     int lfd = socket(PF_INET, SOCK_STREAM, 0);
13     struct sockaddr_in saddr;
14     saddr.sin_port = htons(9999);
15     saddr.sin_family = AF_INET;
16     saddr.sin_addr.s_addr = INADDR_ANY;
17 
18     // 绑定
19     bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
20 
21     // 监听
22     listen(lfd, 8);
23 
24     // 初始化检测的文件描述符数组
25     struct pollfd fds[1024];
26     for(int i = 0; i < 1024; i++) {
27         fds[i].fd = -1;
28         fds[i].events = POLLIN;
29     }
30     fds[0].fd = lfd;
31     int nfds = 0;
32 
33     while(1) {
34 
35         // 调用poll系统函数,让内核帮检测哪些文件描述符有数据
36         int ret = poll(fds, nfds + 1, -1);
37         if(ret == -1) {
38             perror("poll");
39             exit(-1);
40         } else if(ret == 0) {
41             continue;
42         } else if(ret > 0) {
43             // 说明检测到了有文件描述符的对应的缓冲区的数据发生了改变
44             if(fds[0].revents & POLLIN) {
45                 // 表示有新的客户端连接进来了
46                 struct sockaddr_in cliaddr;
47                 int len = sizeof(cliaddr);
48                 int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);
49 
50                 // 将新的文件描述符加入到集合中
51                 for(int i = 1; i < 1024; i++) {
52                     if(fds[i].fd == -1) {
53                         fds[i].fd = cfd;
54                         fds[i].events = POLLIN;
55                         break;
56                     }
57                 }
58 
59                 // 更新最大的文件描述符的索引
60                 nfds = nfds > cfd ? nfds : cfd;
61             }
62 
63             for(int i = 1; i <= nfds; i++) {
64                 if(fds[i].revents & POLLIN) {
65                     // 说明这个文件描述符对应的客户端发来了数据
66                     char buf[1024] = {0};
67                     int len = read(fds[i].fd, buf, sizeof(buf));
68                     if(len == -1) {
69                         perror("read");
70                         exit(-1);
71                     } else if(len == 0) {
72                         printf("client closed...\n");
73                         close(fds[i].fd);
74                         fds[i].fd = -1;
75                     } else if(len > 0) {
76                         printf("read buf = %s\n", buf);
77                         write(fds[i].fd, buf, strlen(buf) + 1);
78                     }
79                 }
80             }
81 
82         }
83 
84     }
85     close(lfd);
86     return 0;
87 }

 

 

epoll

 

 

 

 

 

 

 

 

 

epoll代码编写

client.c

 1 #include <stdio.h>
 2 #include <arpa/inet.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <string.h>
 6 
 7 int main() {
 8 
 9     // 创建socket
10     int fd = socket(PF_INET, SOCK_STREAM, 0);
11     if(fd == -1) {
12         perror("socket");
13         return -1;
14     }
15 
16     struct sockaddr_in seraddr;
17     inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);
18     seraddr.sin_family = AF_INET;
19     seraddr.sin_port = htons(9999);
20 
21     // 连接服务器
22     int ret = connect(fd, (struct sockaddr *)&seraddr, sizeof(seraddr));
23 
24     if(ret == -1){
25         perror("connect");
26         return -1;
27     }
28 
29     int num = 0;
30     while(1) {
31         char sendBuf[1024] = {0};
32         sprintf(sendBuf, "send data %d", num++);
33         write(fd, sendBuf, strlen(sendBuf) + 1);
34 
35         // 接收
36         int len = read(fd, sendBuf, sizeof(sendBuf));
37         if(len == -1) {
38             perror("read");
39             return -1;
40         }else if(len > 0) {
41             printf("read buf = %s\n", sendBuf);
42         } else {
43             printf("服务器已经断开连接...\n");
44             break;
45         }
46         // sleep(1);
47         usleep(1000);
48     }
49 
50     close(fd);
51 
52     return 0;
53 }

 

epoll.c

 1 #include <stdio.h>
 2 #include <arpa/inet.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <string.h>
 6 #include <sys/epoll.h>
 7 
 8 int main() {
 9 
10     // 创建socket
11     int lfd = socket(PF_INET, SOCK_STREAM, 0);
12     struct sockaddr_in saddr;
13     saddr.sin_port = htons(9999);
14     saddr.sin_family = AF_INET;
15     saddr.sin_addr.s_addr = INADDR_ANY;
16 
17     // 绑定
18     bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
19 
20     // 监听
21     listen(lfd, 8);
22 
23     // 调用epoll_create()创建一个epoll实例
24     int epfd = epoll_create(100);
25 
26     // 将监听的文件描述符相关的检测信息添加到epoll实例中
27     struct epoll_event epev;
28     epev.events = EPOLLIN;
29     epev.data.fd = lfd;
30     epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &epev);
31 
32     struct epoll_event epevs[1024];
33 
34     while(1) {
35 
36         int ret = epoll_wait(epfd, epevs, 1024, -1);
37         if(ret == -1) {
38             perror("epoll_wait");
39             exit(-1);
40         }
41 
42         printf("ret = %d\n", ret);
43 
44         for(int i = 0; i < ret; i++) {
45 
46             int curfd = epevs[i].data.fd;
47 
48             if(curfd == lfd) {
49                 // 监听的文件描述符有数据达到,有客户端连接
50                 struct sockaddr_in cliaddr;
51                 int len = sizeof(cliaddr);
52                 int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);
53 
54                 epev.events = EPOLLIN;
55                 epev.data.fd = cfd;
56                 epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &epev);
57             } else {
58                 if(epevs[i].events & EPOLLOUT) {
59                     continue;
60                 }   
61                 // 有数据到达,需要通信
62                 char buf[1024] = {0};
63                 int len = read(curfd, buf, sizeof(buf));
64                 if(len == -1) {
65                     perror("read");
66                     exit(-1);
67                 } else if(len == 0) {
68                     printf("client closed...\n");
69                     epoll_ctl(epfd, EPOLL_CTL_DEL, curfd, NULL);
70                     close(curfd);
71                 } else if(len > 0) {
72                     printf("read buf = %s\n", buf);
73                     write(curfd, buf, strlen(buf) + 1);
74                 }
75 
76             }
77 
78         }
79     }
80 
81     close(lfd);
82     close(epfd);
83     return 0;
84 }

  

epoll的两种工作模式

 

 

 

epoll_et.c

 1 #include <stdio.h>
 2 #include <arpa/inet.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <string.h>
 6 #include <sys/epoll.h>
 7 #include <fcntl.h>
 8 #include <errno.h>
 9 
10 int main() {
11 
12     // 创建socket
13     int lfd = socket(PF_INET, SOCK_STREAM, 0);
14     struct sockaddr_in saddr;
15     saddr.sin_port = htons(9999);
16     saddr.sin_family = AF_INET;
17     saddr.sin_addr.s_addr = INADDR_ANY;
18 
19     // 绑定
20     bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
21 
22     // 监听
23     listen(lfd, 8);
24 
25     // 调用epoll_create()创建一个epoll实例
26     int epfd = epoll_create(100);
27 
28     // 将监听的文件描述符相关的检测信息添加到epoll实例中
29     struct epoll_event epev;
30     epev.events = EPOLLIN;
31     epev.data.fd = lfd;
32     epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &epev);
33 
34     struct epoll_event epevs[1024];
35 
36     while(1) {
37 
38         int ret = epoll_wait(epfd, epevs, 1024, -1);
39         if(ret == -1) {
40             perror("epoll_wait");
41             exit(-1);
42         }
43 
44         printf("ret = %d\n", ret);
45 
46         for(int i = 0; i < ret; i++) {
47 
48             int curfd = epevs[i].data.fd;
49 
50             if(curfd == lfd) {
51                 // 监听的文件描述符有数据达到,有客户端连接
52                 struct sockaddr_in cliaddr;
53                 int len = sizeof(cliaddr);
54                 int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);
55 
56                 // 设置cfd属性非阻塞
57                 int flag = fcntl(cfd, F_GETFL);
58                 flag | O_NONBLOCK;
59                 fcntl(cfd, F_SETFL, flag);
60 
61                 epev.events = EPOLLIN | EPOLLET;    // 设置边沿触发
62                 epev.data.fd = cfd;
63                 epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &epev);
64             } else {
65                 if(epevs[i].events & EPOLLOUT) {
66                     continue;
67                 }  
68 
69                 // 循环读取出所有数据
70                 char buf[5];
71                 int len = 0;
72                 while( (len = read(curfd, buf, sizeof(buf))) > 0) {
73                     // 打印数据
74                     // printf("recv data : %s\n", buf);
75                     write(STDOUT_FILENO, buf, len);
76                     write(curfd, buf, len);
77                 }
78                 if(len == 0) {
79                     printf("client closed....");
80                 }else if(len == -1) {
81                     if(errno == EAGAIN) {
82                         printf("data over.....");
83                     }else {
84                         perror("read");
85                         exit(-1);
86                     }
87                     
88                 }
89 
90             }
91 
92         }
93     }
94 
95     close(lfd);
96     close(epfd);
97     return 0;
98 }

 

epoll_lt.c

 1 #include <stdio.h>
 2 #include <arpa/inet.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <string.h>
 6 #include <sys/epoll.h>
 7 
 8 int main() {
 9 
10     // 创建socket
11     int lfd = socket(PF_INET, SOCK_STREAM, 0);
12     struct sockaddr_in saddr;
13     saddr.sin_port = htons(9999);
14     saddr.sin_family = AF_INET;
15     saddr.sin_addr.s_addr = INADDR_ANY;
16 
17     // 绑定
18     bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
19 
20     // 监听
21     listen(lfd, 8);
22 
23     // 调用epoll_create()创建一个epoll实例
24     int epfd = epoll_create(100);
25 
26     // 将监听的文件描述符相关的检测信息添加到epoll实例中
27     struct epoll_event epev;
28     epev.events = EPOLLIN;
29     epev.data.fd = lfd;
30     epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &epev);
31 
32     struct epoll_event epevs[1024];
33 
34     while(1) {
35 
36         int ret = epoll_wait(epfd, epevs, 1024, -1);
37         if(ret == -1) {
38             perror("epoll_wait");
39             exit(-1);
40         }
41 
42         printf("ret = %d\n", ret);
43 
44         for(int i = 0; i < ret; i++) {
45 
46             int curfd = epevs[i].data.fd;
47 
48             if(curfd == lfd) {
49                 // 监听的文件描述符有数据达到,有客户端连接
50                 struct sockaddr_in cliaddr;
51                 int len = sizeof(cliaddr);
52                 int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);
53 
54                 epev.events = EPOLLIN;
55                 epev.data.fd = cfd;
56                 epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &epev);
57             } else {
58                 if(epevs[i].events & EPOLLOUT) {
59                     continue;
60                 }   
61                 // 有数据到达,需要通信
62                 char buf[5] = {0};
63                 int len = read(curfd, buf, sizeof(buf));
64                 if(len == -1) {
65                     perror("read");
66                     exit(-1);
67                 } else if(len == 0) {
68                     printf("client closed...\n");
69                     epoll_ctl(epfd, EPOLL_CTL_DEL, curfd, NULL);
70                     close(curfd);
71                 } else if(len > 0) {
72                     printf("read buf = %s\n", buf);
73                     write(curfd, buf, strlen(buf) + 1);
74                 }
75 
76             }
77 
78         }
79     }
80 
81     close(lfd);
82     close(epfd);
83     return 0;
84 }

 

 

UDP通信

 

 

 

 

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <string.h>
 5 #include <arpa/inet.h>
 6 
 7 int main() {
 8 
 9     // 1.创建一个通信的socket
10     int fd = socket(PF_INET, SOCK_DGRAM, 0);
11     
12     if(fd == -1) {
13         perror("socket");
14         exit(-1);
15     }   
16 
17     struct sockaddr_in addr;
18     addr.sin_family = AF_INET;
19     addr.sin_port = htons(9999);
20     addr.sin_addr.s_addr = INADDR_ANY;
21 
22     // 2.绑定
23     int ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
24     if(ret == -1) {
25         perror("bind");
26         exit(-1);
27     }
28 
29     // 3.通信
30     while(1) {
31         char recvbuf[128];
32         char ipbuf[16];
33 
34         struct sockaddr_in cliaddr;
35         int len = sizeof(cliaddr);
36 
37         // 接收数据
38         int num = recvfrom(fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&cliaddr, &len);
39 
40         printf("client IP : %s, Port : %d\n", 
41             inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)),
42             ntohs(cliaddr.sin_port));
43 
44         printf("client say : %s\n", recvbuf);
45 
46         // 发送数据
47         sendto(fd, recvbuf, strlen(recvbuf) + 1, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
48 
49     }
50 
51     close(fd);
52     return 0;
53 }

 

广播

 

 

 

bro_client.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <string.h>
 5 #include <arpa/inet.h>
 6 
 7 int main() {
 8 
 9     // 1.创建一个通信的socket
10     int fd = socket(PF_INET, SOCK_DGRAM, 0);
11     if(fd == -1) {
12         perror("socket");
13         exit(-1);
14     }   
15 
16     struct in_addr in;
17 
18     // 2.客户端绑定本地的IP和端口
19     struct sockaddr_in addr;
20     addr.sin_family = AF_INET;
21     addr.sin_port = htons(9999);
22     addr.sin_addr.s_addr = INADDR_ANY;
23 
24     int ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
25     if(ret == -1) {
26         perror("bind");
27         exit(-1);
28     }
29 
30     // 3.通信
31     while(1) {
32         
33         char buf[128];
34         // 接收数据
35         int num = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
36         printf("server say : %s\n", buf);
37 
38     }
39 
40     close(fd);
41     return 0;
42 }

 

 

bro_server.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <string.h>
 5 #include <arpa/inet.h>
 6 
 7 int main() {
 8 
 9     // 1.创建一个通信的socket
10     int fd = socket(PF_INET, SOCK_DGRAM, 0);
11     if(fd == -1) {
12         perror("socket");
13         exit(-1);
14     }   
15 
16     // 2.设置广播属性
17     int op = 1;
18     setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &op, sizeof(op));
19     
20     // 3.创建一个广播的地址
21     struct sockaddr_in cliaddr;
22     cliaddr.sin_family = AF_INET;
23     cliaddr.sin_port = htons(9999);
24     inet_pton(AF_INET, "192.168.193.255", &cliaddr.sin_addr.s_addr);
25 
26     // 3.通信
27     int num = 0;
28     while(1) {
29        
30         char sendBuf[128];
31         sprintf(sendBuf, "hello, client....%d\n", num++);
32         // 发送数据
33         sendto(fd, sendBuf, strlen(sendBuf) + 1, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
34         printf("广播的数据:%s\n", sendBuf);
35         sleep(1);
36     }
37 
38     close(fd);
39     return 0;
40 }

组播(多播)

 

 

 

 

 multi_client.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <string.h>
 5 #include <arpa/inet.h>
 6 
 7 int main() {
 8 
 9     // 1.创建一个通信的socket
10     int fd = socket(PF_INET, SOCK_DGRAM, 0);
11     if(fd == -1) {
12         perror("socket");
13         exit(-1);
14     }   
15 
16     struct in_addr in;
17     // 2.客户端绑定本地的IP和端口
18     struct sockaddr_in addr;
19     addr.sin_family = AF_INET;
20     addr.sin_port = htons(9999);
21     addr.sin_addr.s_addr = INADDR_ANY;
22 
23     int ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
24     if(ret == -1) {
25         perror("bind");
26         exit(-1);
27     }
28 
29     struct ip_mreq op;
30     inet_pton(AF_INET, "239.0.0.10", &op.imr_multiaddr.s_addr);
31     op.imr_interface.s_addr = INADDR_ANY;
32 
33     // 加入到多播组
34     setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &op, sizeof(op));
35 
36     // 3.通信
37     while(1) {
38         
39         char buf[128];
40         // 接收数据
41         int num = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
42         printf("server say : %s\n", buf);
43 
44     }
45 
46     close(fd);
47     return 0;
48 }

multi_server.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <string.h>
 5 #include <arpa/inet.h>
 6 
 7 int main() {
 8 
 9     // 1.创建一个通信的socket
10     int fd = socket(PF_INET, SOCK_DGRAM, 0);
11     if(fd == -1) {
12         perror("socket");
13         exit(-1);
14     }   
15 
16     // 2.设置多播的属性,设置外出接口
17     struct in_addr imr_multiaddr;
18     // 初始化多播地址
19     inet_pton(AF_INET, "239.0.0.10", &imr_multiaddr.s_addr);
20     setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &imr_multiaddr, sizeof(imr_multiaddr));
21     
22     // 3.初始化客户端的地址信息
23     struct sockaddr_in cliaddr;
24     cliaddr.sin_family = AF_INET;
25     cliaddr.sin_port = htons(9999);
26     inet_pton(AF_INET, "239.0.0.10", &cliaddr.sin_addr.s_addr);
27 
28     // 3.通信
29     int num = 0;
30     while(1) {
31        
32         char sendBuf[128];
33         sprintf(sendBuf, "hello, client....%d\n", num++);
34         // 发送数据
35         sendto(fd, sendBuf, strlen(sendBuf) + 1, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
36         printf("组播的数据:%s\n", sendBuf);
37         sleep(1);
38     }
39 
40     close(fd);
41     return 0;
42 }

 

 

本地套接字通信

 

 

 ipc_client.c

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <arpa/inet.h>
 6 #include <sys/un.h>
 7 
 8 int main() {
 9 
10     unlink("client.sock");
11 
12     // 1.创建套接字
13     int cfd = socket(AF_LOCAL, SOCK_STREAM, 0);
14     if(cfd == -1) {
15         perror("socket");
16         exit(-1);
17     }
18 
19     // 2.绑定本地套接字文件
20     struct sockaddr_un addr;
21     addr.sun_family = AF_LOCAL;
22     strcpy(addr.sun_path, "client.sock");
23     int ret = bind(cfd, (struct sockaddr *)&addr, sizeof(addr));
24     if(ret == -1) {
25         perror("bind");
26         exit(-1);
27     }
28 
29     // 3.连接服务器
30     struct sockaddr_un seraddr;
31     seraddr.sun_family = AF_LOCAL;
32     strcpy(seraddr.sun_path, "server.sock");
33     ret = connect(cfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
34     if(ret == -1) {
35         perror("connect");
36         exit(-1);
37     }
38 
39     // 4.通信
40     int num = 0;
41     while(1) {
42 
43         // 发送数据
44         char buf[128];
45         sprintf(buf, "hello, i am client %d\n", num++);
46         send(cfd, buf, strlen(buf) + 1, 0);
47         printf("client say : %s\n", buf);
48 
49         // 接收数据
50         int len = recv(cfd, buf, sizeof(buf), 0);
51 
52         if(len == -1) {
53             perror("recv");
54             exit(-1);
55         } else if(len == 0) {
56             printf("server closed....\n");
57             break;
58         } else if(len > 0) {
59             printf("server say : %s\n", buf);
60         }
61 
62         sleep(1);
63 
64     }
65 
66     close(cfd);
67     return 0;
68 }

ipc_server.c

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <arpa/inet.h>
 6 #include <sys/un.h>
 7 
 8 int main() {
 9 
10     unlink("server.sock");
11 
12     // 1.创建监听的套接字
13     int lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
14     if(lfd == -1) {
15         perror("socket");
16         exit(-1);
17     }
18 
19     // 2.绑定本地套接字文件
20     struct sockaddr_un addr;
21     addr.sun_family = AF_LOCAL;
22     strcpy(addr.sun_path, "server.sock");
23     int ret = bind(lfd, (struct sockaddr *)&addr, sizeof(addr));
24     if(ret == -1) {
25         perror("bind");
26         exit(-1);
27     }
28 
29     // 3.监听
30     ret = listen(lfd, 100);
31     if(ret == -1) {
32         perror("listen");
33         exit(-1);
34     }
35 
36     // 4.等待客户端连接
37     struct sockaddr_un cliaddr;
38     int len = sizeof(cliaddr);
39     int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);
40     if(cfd == -1) {
41         perror("accept");
42         exit(-1);
43     }
44 
45     printf("client socket filename: %s\n", cliaddr.sun_path);
46 
47     // 5.通信
48     while(1) {
49 
50         char buf[128];
51         int len = recv(cfd, buf, sizeof(buf), 0);
52 
53         if(len == -1) {
54             perror("recv");
55             exit(-1);
56         } else if(len == 0) {
57             printf("client closed....\n");
58             break;
59         } else if(len > 0) {
60             printf("client say : %s\n", buf);
61             send(cfd, buf, len, 0);
62         }
63 
64     }
65 
66     close(cfd);
67     close(lfd);
68 
69     return 0;
70 }

 

posted @ 2022-02-28 22:06  白雪儿  Views(152)  Comments(0Edit  收藏  举报