代码来自B站就业班视频
p53课
本文是简单搬运;
1.函数原型
2.对代码的解释:
2.1为什么要设置tmpfds 代替readfds?
入参的三个文件描述符集合,都是传入传出参数,所以,每次调用select 他们三个都产生了变化,都需要重置。
为了避开重复清空重置的操作,先用tmpfds 代替读描述符集合readfds ,让tmpfds先变来变去,readfds跟着修改就行了
2.2 目前对老师的代码还有疑惑的地方
(1) 手动修改readfds 会不会有遗漏的情况。。
(2)为什么文件描述符遍历的时候,是从sfd (监听的描述符) 加1开始。 看很多 博主的文章是从0 开始遍历的,从0开始遍历到本代码中nready (nready是 select函数的返回值,意思是就绪的文件描述符的数量)。
但是反正代码能跑。。。目前已经修改完毕!!并发也可以实现。
//select模型 初级代码 第一次写的代码 bianli from maxfd+1 nready??? #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #include <ctype.h> #include <sys/select.h> #include <errno.h> //将网络大端模式的地址,转化为字符串 //const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); int main() { int sfd = socket(AF_INET, SOCK_STREAM, 0); //设置端口复用,这两行看不懂的跳过去,写不写不影响 int opt =1; setsockopt ( sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)); //定义一个地址结gou体 struct sockaddr_in servad; bzero(& servad, sizeof( servad)); servad.sin_family =AF_INET; servad.sin_port =htons(8888); servad.sin_addr.s_addr = htonl (INADDR_ANY); int ret = bind(sfd, (struct sockaddr*)&servad, sizeof( servad)); //将socket从主动变为被动(服务器必备),这样可以监听来自客户的请求 listen(sfd,128); //select 模型的相关参数 设置 //定义fd_set 类型的变量, 并且清空它们 fd_set readfds, tmpfds; FD_ZERO ( &readfds); FD_ZERO ( &tmpfds); //把sfd加入到readfds中,让内核监视sfd的变化 FD_SET (sfd, &readfds); int maxfd = sfd; int newfd ; int i =0; int n =0; int nready =0; int sockfd; while (1) { tmpfds = readfds; //因为超时时间设置为NULL ,所以进程会无限阻塞在这一步,除非有了变化才往下走 nready = select (maxfd+1, &tmpfds, NULL, NULL, NULL); if (nready <=0) { if (errno ==EINTR ){ continue; } break; } //两种情况,第一种,有新的连接请求。注意,tmpfds 已经变化了,但是readfds没有变化 if ( FD_ISSET (sfd, &tmpfds)) { newfd = accept (sfd,NULL, NULL); //将newfd纳入内核的监控范围 FD_SET ( newfd ,&readfds ); if (maxfd < newfd) { maxfd = newfd; } } //第二种情况,目前的文件描述符,有新的内容到了缓冲区了 ..主要是for循环里的东西不懂 //因为后面i 有变化,所以先定义sockfd = i for ( i= sfd+1; i <= maxfd; i++) { sockfd = i; if (FD_ISSET (sockfd, &tmpfds) ){ char buf[1024]; //读数据 memset(buf,0x00,sizeof(buf)); n =read (sockfd, buf, sizeof(buf)); if (n <=0){ printf(" read error or client close ,n==[%d] ", n); close (sockfd); FD_CLR (sockfd, &readfds); } else { printf ("这里是服务器端n =%d ,读到的是%s ", n,buf); //将小写字母都转化wie大写 for (int j =0; j<n; j++) { buf[j] =toupper (buf[j]); } //发送数据 write (sockfd, buf, n); } } } } close (sfd); return 0; }
注意!!这里面因为用了tmpfds 代替readfds ,所以细节上写错一点就出错!!比如我之前就错写成了
FD_CLR (sockfd, &tmpfds);
导致当有3个客户端同时连接服务器的时候,关闭一个客户端,其他的客户端就无法接收到服务器发回来的大写字母了,!!!同时服务器也关闭了!