select()方法实现io多路复用实现服务器编程模型
本文通过pthread多线程以及io多路复用中的select()方法实现两种服务器编程的模型。
1.初始化模型
//定义fd fd默认block--阻塞状态
int listenfd = socket(AF_INET,SOCK_STREAM,0);
if (listenfd == -1 ) return -1;
//定义网络
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 地址 //INADDR_ANY --0.0.0.0
servaddr.sin_port = htons(8080); //绑定端口
if(-1 == bind( listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))){
return -2;
}
listen(listenfd,10);
2.多线程
主函数
while(1){
struct sockaddr_in client;
socklen_t len = sizeof(client);
int clientfd = accept(listenfd,(struct sockaddr*)&client,&len);
pthread_t threadid;
pthread_create(&threadid,NULL,routine,&clientfd);
}
实现函数
void *routine(void *arg){
int clientfd =*(int *)arg;
while(1){
unsigned char buffer[BUFFER_LENGTH]={0};
int ret = recv(clientfd,buffer,BUFFER_LENGTH,0);
if(ret == 0){
close(clientfd);
break;
}
printf("buffer : %s , ret: %d\n", buffer,ret);
send(clientfd,buffer,ret,0);
}
}
3. select 方法实现 io多路复用
fd_set rfds,wfds,rset,wset;
FD_ZERO(&rfds);
FD_SET(listenfd,&rfds);
FD_ZERO(&wfds);
int maxfd=listenfd;
int ret=0;
unsigned char buffer[BUFFER_LENGTH]={0};
//int fd,
while(1){
FD_SET(listenfd,&rfds);
rset = rfds;
wset = wfds;
int nready = select(maxfd+1,&rset,&wset,NULL,NULL); //返回的是描述符数目
if(FD_ISSET(listenfd,&rset)){
struct sockaddr_in client;
socklen_t len = sizeof(client);
int clientfd = accept(listenfd,(struct sockaddr*)&client,&len); //accept()会自动清空读取的事件
FD_SET(clientfd,&rfds);
if(clientfd>maxfd) maxfd=clientfd;
}
int i=0;
for(i=3;i<=maxfd;i++){
if(FD_ISSET(i,&rset)){ //判断具体已就绪的文件描述,在rset里面查第i位是否已经就绪
memset(buffer, 0, sizeof(buffer)); //重置缓冲区
ret = recv(i,buffer,sizeof(buffer)-1,0);
if(ret == 0){
close(i);
FD_CLR(i,&rfds);
//break;
}else if(ret >0){
printf("buffer : %s , ret: %d\n", buffer,ret);
FD_SET(i,&wfds);
}
}
if(FD_ISSET(i,&wset)){
ret =send(i,buffer,ret,0);
FD_CLR(i,&wfds);
FD_SET(i,&rfds);
}
}
}
4.代码整理
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<fcntl.h>
#include<unistd.h>
#include<pthread.h>
#define BUFFER_LENGTH 128
void *routine(void *arg){
int clientfd =*(int *)arg;
while(1){
unsigned char buffer[BUFFER_LENGTH]={0};
int ret = recv(clientfd,buffer,BUFFER_LENGTH,0);
if(ret == 0){
close(clientfd);
break;
}
printf("buffer : %s , ret: %d\n", buffer,ret);
send(clientfd,buffer,ret,0);
}
}
//0,1,2
//stdin,stdout,stderr
int main(){
//定义fd fd默认block--阻塞状态
int listenfd = socket(AF_INET,SOCK_STREAM,0);
if (listenfd == -1 ) return -1;
//定义网络
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 地址 //INADDR_ANY --0.0.0.0
servaddr.sin_port = htons(8080); //绑定端口 --- 进酒店大堂
if(-1 == bind( listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))){
return -2;
}
#if 0 //nonblock
//将fd设置成非阻塞的
int flag = fcntl(listenfd,F_GETFL,0);
flag |= O_NONBLOCK;
fcntl(listenfd,F_SETFL,flag);
#endif
listen(listenfd,10);
#if 0 //还是只有单个accept()可以执行,只有一个close()了,才能开启另一个accept()
while(1)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int clientfd = accept(listenfd,(struct sockaddr*)&client,&len); //accept()就是bind()以后分配fd --- 找大堂服务员的过程
while(1){
unsigned char buffer[BUFFER_LENGTH]={0};
int ret = recv(clientfd,buffer,BUFFER_LENGTH,0);
if(ret == 0){
close(clientfd);
break;
}
printf("buffer : %s , ret: %d\n", buffer,ret);
send(clientfd,buffer,ret,0); //send()>0不等于发送成功,只是说明发送到协议栈了
}
}
//printf("clientfd: %d\n",clientfd);
#elif 0
while(1){
struct sockaddr_in client;
socklen_t len = sizeof(client);
int clientfd = accept(listenfd,(struct sockaddr*)&client,&len);
pthread_t threadid;
pthread_create(&threadid,NULL,routine,&clientfd);
}
#else //io多路复用
fd_set rfds,wfds,rset,wset;
FD_ZERO(&rfds);
FD_SET(listenfd,&rfds);
FD_ZERO(&wfds);
int maxfd=listenfd;
int ret=0;
unsigned char buffer[BUFFER_LENGTH]={0};
//int fd,
while(1){
FD_SET(listenfd,&rfds);
rset = rfds;
wset = wfds;
int nready = select(maxfd+1,&rset,&wset,NULL,NULL); //select()管理io的秘书,返回的是描述符数目
if(FD_ISSET(listenfd,&rset)){
struct sockaddr_in client;
socklen_t len = sizeof(client);
int clientfd = accept(listenfd,(struct sockaddr*)&client,&len); //accept()会自动清空读取的事件
FD_SET(clientfd,&rfds);
if(clientfd>maxfd) maxfd=clientfd;
}
int i=0;
for(i=3;i<=maxfd;i++){
if(FD_ISSET(i,&rset)){ //判断具体已就绪的文件描述,在rset里面查第i位是否已经就绪
memset(buffer, 0, sizeof(buffer)); //重置缓冲区
ret = recv(i,buffer,sizeof(buffer)-1,0);
if(ret == 0){
close(i);
FD_CLR(i,&rfds);
//break;
}else if(ret >0){
printf("buffer : %s , ret: %d\n", buffer,ret);
FD_SET(i,&wfds);
}
}
if(FD_ISSET(i,&wset)){
ret =send(i,buffer,ret,0);
FD_CLR(i,&wfds);
FD_SET(i,&rfds);
}
}
}
#endif
}
5. 运行截图
客户端发送:
服务端接收: