前言:关于select原理,只有弄懂 了内核的工作原理之后,才能真正明白。在CSDN上找了很多文章,要么是列出实践代码写一个服务器,要么是分析源码,都是一头雾水看不懂,所以推荐这篇讲原理的文章,先看明白
上篇代码的缺点:已经讲过了,这是第二次优化的代码
代码有点问题,现在第二个客户端开启不了 排查中
//select模型 第二次代码,将所有文件描述符放金一个数组中,减少便利此书
#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>
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 ;
int nready =0;
int sockfd;
char buf[FD_SETSIZE];
int connfd[FD_SETSIZE];
//保存所有有效的 文件描述符
for (i =0;i<FD_SETSIZE; i++){
connfd[i] =-1;
}
while (1) {
tmpfds = readfds;
//因为超时时间设置为NULL ,所以进程会无限阻塞在这一步,除非有了变化才往下走
nready = select (maxfd+1, &tmpfds, NULL, NULL, NULL);
if (nready >0) {
//两种情况,第一种,有新的连接请求。注意,tmpfds 已经变化了,但是readfds没有变化
if ( FD_ISSET (sfd, &tmpfds)) {
newfd = accept (sfd,NULL, NULL);
if (newfd<0) {
if(errno ==ECONNABORTED ||errno == EINTR){
continue;
}
break;
}
//在数组里找一个最靠前的,还是-1的地方,替换成newfd
for (i =0; i<FD_SETSIZE; i++){
if( connfd[i] ==-1) {
connfd [i] =newfd;
break;
}
}
//如果整个数组都不是-1了说明存满了文件描述符
if (i ==FD_SETSIZE){
printf("connfd array is full \n");
close(newfd);
continue;
}
//将newfd纳入内核的监控范围
FD_SET ( newfd ,&readfds );
if (maxfd < newfd) {
maxfd = newfd;
}
}
//第二种情况,目前的文件描述符,有新的内容到了缓冲区了 ..主要是for循环里的东西不懂
//因为后面i 有变化,所以先定义sockfd = i
for ( i= 0; i <FD_SETSIZE; i++) {
sockfd = i;
if (FD_ISSET (sockfd, &tmpfds) ){
char buf[1024];
//读数据
memset(buf,0x00,sizeof(buf));
int n =read (sockfd, buf, sizeof(buf));
if (n <=0){
printf(" read error or client close ,n==[%d]\n", n);
close (sockfd);
FD_CLR (sockfd, &readfds);
}
else {
printf ("这里是服务器端n =%d ,读到的是%s \n", n,buf);
//将小写字母都转化wie大写
for (int j =0; j<n; j++) {
buf[j] =toupper (buf[j]);
} // ctype.h头文件
//发送数据
write (sockfd, buf, n);
}
}
}
}
}