select:
类似poll,只是select监听的是文件描述符的位数组。监听的数组每一位与真正的文件描述符是一一对应的。
server.c
#include<stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void start(){
//֨ӥһٶޡٹͥèθçַ֘ޡٹͥéҪ՚listenfdݨbքʱ۲ԃ
struct sockaddr_in my_addr;
my_addr.sin_family = AF_INET; // ipv4
my_addr.sin_port = htons(8080);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//
//֨ӥһٶޡٹͥèθçַ֘ޡٹͥéҪ՚acceptքʱ۲ԫԶࠍۧքхϢ
struct sockaddr_in client_addr;
char cli_ip[INET_ADDRSTRLEN] = "";
//fdìԫࠍۧͨх
int clientfd = 0;
//փսһٶlistenքfd
int listenfd = socket(AF_INET,SOCK_STREAM,0);
//ѳ֨һٶࠚ+ip,̹ӔϒćࠉӔɏΪһٶsocketߍˇһٶip+ࠚքθçԌܲ֟τݾ
bind(listenfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
//ʨ׃ͬʱŜٻޓ˜ȫȳΪ128ٶìעӢˇͬһʱݤ
listen(listenfd, 128);
printf("listen client @port=%d...\n",8080);
//փսһٶfd
//connfd = accept(lfd, (struct sockaddr*)&client_addr, &cliaddr_len);
//inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
//printf("----------------------------------------------\n");
//printf("client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));
//ӎ˽֨ӥ
int lastfd = listenfd;
int i;
//λͼìڃȒmޢΪܯۏìŜٻݠͽսR˂ݾ
fd_set readset,totalSet;
//ЈȥࠕёλͼքֵּلΪ0
FD_ZERO(&readset);
//listenfd ҭʾlistenfdϒѨҪݠͽ̻քR˂ݾ
FD_SET(listenfd, &totalSet);
while(1)
{
readset = totalSet;
//setup 1 ёݠͽք͗ޓؖۍࠍۧք͗ޓؖٸselect
int z = select(lastfd+1,&readset,NULL,NULL,NULL);
//ƐˇرԐ˂ݾעʺ
if(z>0){
//ɧڻԐ˂ݾעʺìǤʵϒҢһ֪րˇʲô˂ݾìիˇԐ}זȩ࠶
if(FD_ISSET(listenfd,&readset)){
socklen_t cliaddr_len = sizeof(client_addr); //ӲΪ̻ˇԫɫԫԶìܡزه
clientfd = accept(listenfd, (struct sockaddr*)&client_addr, &cliaddr_len);
inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
printf("----------------------------------------------\n");
printf("client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));
FD_SET(clientfd, &totalSet);
lastfd=clientfd;
if(0==--z){
continue;
}
}
for(i=listenfd+1;i<=lastfd;i++){//Ԧmࠍۧעڽ4քлϢ
if(FD_ISSET(i,&readset)){//ɧڻ֚IٶfdԐR˂ݾìŇôֱޓёŚɝׁԶ4
char recv_buf[512] = "";
int rs=read(i,recv_buf,sizeof(recv_buf));
if(rs==0){//ࠍۧژҕ
close(i);
FD_CLR(i,&totalSet);
}else{
printf("%s\n",recv_buf);
//write(0,recv_buf,rs);
}
}
}
}
}
}
int main(){
start();
return 0;
}
poll:
首先,服务启动时,服务端的socket会server_init()返回值int s 是一个文件描述符,将这个文件描述符放到一个pollfd数组(或链表)pfd的第0位中,
while(1){
int ret poll(pfd , 监听的文件描述符个数 , ...)
ret <0,有error;ret = 0,超时;ret>0 ,有事件发生。
然后:
if(pfd[0].revents & POLLIN){
是连接事件
int rws = accept(s,...);
if(rws < 0 ){
error;
}
并将这个rws放入到pfd数组中
}
最后,遍历数组pfd的从1开始的文件描述符
if(pfd[i].revents & POLLIN){
有写事件发生
int num = recv(pfd[i].fd ,buf ,...)
num 为读到数据的字节数。
}
}
PS:accept只监听连接文件描述符。select方法可以监听连接或读写事件的发生。recv只能监听读写事件的发生。
epoll:
将待监控的文件描述符存放在一个树中,调用epoll_wait(),监控这些文件描述符,当这些文件描述符状态有变化时,会将文件描述符与具体的状态绑定一起,只需要取出后进行判断就可以了。
在调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个rdllist双向链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个rdllist双向链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。
socket可以理解为: ip + 端口 形成的一个文件。