采用多进程固然能很好的实现业务分离,但是开辟一个新进程需要的资源太多,而且一些简单的业务本不需要开辟一个新进程进行处理,因此出现严重的资源浪费,能不能有一种技术不仅仅能替代多进程的移步分离处理多客户端情况,而且会造成太大的资源浪费。于是I/O技术就被提出来了,I/O复用关键在于掌握select函数的使用。
本章主要任务是采用I/O改写之前的基于多进程回声服务端设计,使得服务器端开辟一个进程就能同时处理多个客户端之间的数据交换。
select函数
关于该函数的介绍,我觉得这篇博客讲得非常清楚,所以不再重复叙述。
select 函数测试程序
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
#define BUF_SIZE 30
int main(int argc, char const *argv[])
{
fd_set reads, temps;
int result,str_len;
char buf[BUF_SIZE];
struct timeval timeout;
//初始化
FD_ZERO(&reads);
/**
* @brief 将标准输入加入监听文件描述符集
* @note
* @retval
*/
FD_SET(0,&reads);
/**
* @brief timeout.tv_sec=5,
* @note
* @retval None
*/
while (1)
{
//不断更新监听的文件描述符集
temps=reads;
timeout.tv_sec=5;
timeout.tv_usec=0;
/**
* @brief select函数的使用
* @note
* @retval
*/
result=select(1,&temps,0,0,&timeout);
if(result==-1)
{
puts("select () error!");
break;
}
else if(result==0)
{
puts("Timeout!");
}
else{
/**
* @brief fd=0代表标准输入文件描述符,在FD_ISSET参数设置里面第一个参数为0就是指标准输入,这个宏用于确定fd所指的文件描述符是否在监听文件描述符集里面。
* @note
* @retval
*/
if(FD_ISSET(0,&temps))
{
str_len=read(0,buf,BUF_SIZE);
buf[str_len]=0;
printf("message from console :%s\n",buf);
}
}
}
return 0;
}
实现I/O复用服务器
-
客户端实现代码(在之前章节里面随便找一个客户端代码即可)
//client.c #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<arpa/inet.h> #include<sys/socket.h> #define BUF_SIZE 1024 void error_handling(char *message); int main(int argc,char *argv[]) { int sock; char message[BUF_SIZE]; int str_len; struct sockaddr_in serv_adr; if(argc!=3) { printf("Usage:%s<IP><port> \n",argv[0]); exit(1); } sock=socket(PF_INET,SOCK_STREAM,0); if(sock==-1)error_handling("socket() error"); memset(&serv_adr,0,sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=inet_addr(argv[1]); serv_adr.sin_port=htons(atoi(argv[2])); if(connect(sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1) error_handling("connect() error"); else{ puts("Connected....."); } while(1) { fputs("Input message(Q to quit):",stdout); fgets(message,BUF_SIZE,stdin); if(!strcmp(message,"q\n") || !strcmp(message,"Q\n")) { printf("testing......\n"); break; } write(sock,message,strlen(message)); str_len=read(sock,message,BUF_SIZE-1); message[str_len]=0; printf("Message from server:%s",message); } close(sock); return 0; } void error_handling(char *message) { fputs(message,stderr); fputc('\n',stderr); exit(1); }
-
服务端实现代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/select.h> #define BUF_SIZE 100 void error_handling(char* message); int main(int argc, char const *argv[]) { int serv_sock,clnt_sock; struct sockaddr_in serv_adr,clnt_adr; struct timeval timeout; fd_set reads,cpy_reads; socklen_t adr_sz; int fd_max,str_len,fd_num,i; char buf[BUF_SIZE]; if(argc!=2) { printf("Usage : %s <port> \n",argv[0]); exit(1); } serv_sock=socket(PF_INET,SOCK_STREAM,0); memset(&serv_adr,0,sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=htonl(INADDR_ANY); serv_adr.sin_port=htons(atoi(argv[1])); /** * @brief 绑定接口与地址 * @note * @retval None */ if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1) error_handling("bind() error"); /** * @brief 监听请求连接 * @note * @retval None */ if(listen(serv_sock,5)==-1)error_handling("listen() error"); /** * @brief 初始化监听套接字集 * @note * @retval None */ FD_ZERO(&reads); FD_SET(serv_sock,&reads); /** * @brief 注意理解这行代码 * @note * @retval None */ fd_max=serv_sock; while(1) { cpy_reads=reads; timeout.tv_sec=5; timeout.tv_usec=5000; if((fd_num=select(fd_max+1,&cpy_reads,0,0,&timeout))==-1)break; if(fd_num==0)continue; for(i=0;i<fd_max+1;i++) { if(FD_ISSET(i,&cpy_reads)) { if(i==serv_sock)//处理连接 { adr_sz=sizeof(clnt_adr); clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_adr,&adr_sz); FD_SET(clnt_sock,&reads); if(fd_max<clnt_sock)fd_max=clnt_sock; printf("connected client: %d\n",clnt_sock); } else{ //读取message str_len=read(i,buf,BUF_SIZE); if(str_len==0) { FD_CLR(i,&reads); close(i); printf("closed client : %d\n",i); }else{ write(i,buf,str_len); } } } } } close(serv_sock); return 0; } void error_handling(char *message) { fputs(message,stderr); fputc('\n',stderr); exit(1); }