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 }