同一时刻能处理多个客户端
int init_tcp_ser(const char *ip,unsigned short port) { int sockfd= socket(AF_INET,SOCK_STREAM,0); if(-1== sockfd) { perror("fail socket"); return -1; } struct sockaddr_in ser; ser.sin_family = AF_INET; ser.sin_port = htons(port); ser.sin_addr.s_addr = inet_addr(ip); int ret = bind(sockfd,(struct sockaddr *)&ser,sizeof(ser)); if(-1 == ret) { perror("bind fail"); return -1; } ret = listen(sockfd,128); if(-1 == ret) { perror("listen fail"); return -1; } return sockfd; } void handler(int signo) { wait(NULL); } int main(int argc, char *argv[]) { pid_t pid = 0; int connfd = 0; char buf[1024] = {0}; signal(SIGCHLD,handler); int sockfd = init_tcp_ser("192.168.42.128",50000); if(-1 == sockfd) { return -1; } while(1) { connfd = accept(sockfd,NULL,NULL); if(-1==connfd) { perror("fail accept"); return -1; } pid = fork(); if (pid > 0) { } else if(0==pid) { while(1) { memset(buf,0,sizeof(buf)); ssize_t size = recv(connfd,buf,sizeof(buf),0); if(size <= 0) { break; } printf("cli----->%s\n",buf); strcat(buf,"---->ok!\n"); send(connfd,buf,strlen(buf),0); } close(connfd); } } return 0; }
int init_tcp_ser(const char *ip,unsigned short port) { int sockfd = socket(AF_INET,SOCK_STREAM,0); if(-1 == sockfd) { perror("fail socket"); return -1; } struct sockaddr_in ser; ser.sin_family = AF_INET; ser.sin_port =htons(port); ser.sin_addr.s_addr = inet_addr(ip); int ret = bind(sockfd,(struct sockaddr *)&ser,sizeof(ser)); if(-1 == ret) { perror("bind fail"); return -1; } ret = listen(sockfd,128); if(-1 == ret) { perror("listen fail"); return -1; } return sockfd; } void *do_Something(void *arg) { char buf[1024]= {0}; int connfd = *(int *)arg; while(1) { memset(buf,0,sizeof(buf)); ssize_t size = recv(connfd,buf,sizeof(buf),0); if(size <= 0) { break; } printf("----->%s\n",buf); strcat(buf,"------>ok!\n"); send(connfd,buf,strlen(buf),0); } close(connfd); return NULL; } int main(int argc, char *argv[]) { int connfd = 0; int sockfd = init_tcp_ser("192.168.42.128",50000); if(sockfd == -1) { printf("init_tcp_ser fail"); return -1; } while(1) { connfd = accept(sockfd,NULL,NULL); if(-1 == sockfd) { perror("accept fail"); return -1; } pthread_t tid; int ret1 = pthread_create(&tid,NULL,do_Something,&connfd); if(ret1 != 0) { perror("pthread_create fail"); return -1; } pthread_detach(tid); void *retval; if(pthread_join(tid,&retval) == 0) { perror("pthread_join fail"); return -1; } } }
select模型通过调用select函数来检查一个或多个文件描述符(在socket编程中通常指套接字)的状态,包括可读、可写以及异常。select函数的原型如下:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout);
功能:
监听文件描述符集合
参数:
nfds:监测的文件描述符上限值(最大文件描述符的值+1)
readfds:读文件描述符集合
writefds:写文件描述符集合
exceptfds:异常条件的描述符集合
timeout:设置超时时间
NULL:一直等待
返回值:
成功返回产生事件文件描述符个数
失败返回-1
定时时间到达仍没有事件产生返回0void FD_CLR(int fd, fd_set *set);
将fd从文件描述符集合中清除
int FD_ISSET(int fd, fd_set *set);
判断文件描述符fd是否仍在文件描述符集合中
void FD_SET(int fd, fd_set *set);
将fd加入文件描述符集合中
void FD_ZERO(fd_set *set);
文件描述符集合清0
优点:
缺点:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能:
监听文件描述符集合中的事件
参数:
fds:文件描述符集合事件数组首地址
nfds:事件个数
timeout:超时时间
返回值:
成功返回产生事件的文件描述符个数
失败返回-1
超时时间到达仍没有产生事件返回0
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
epoll模型:
1)epoll_create 创建epoll文件描述符集合
2)epoll_ctl 添加关注的文件描述符
3)epoll_wait 监控io事件
4)epoll_ctl 从事件集合中删除完成的文件描述符
int epoll_create(int size);
功能:
创建一个监听事件表(内核中)
参数:
size:监听事件最大个数
返回值:
成功返回非负值:表示epoll事件表对象(句柄)
失败返回-1
5.epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:
在监听事件表中新增一个事件
参数:
epfd:事件表文件描述符
op:EPOLL_CTL_ADD 新增事件
EPOLL_CTL_MOD 修改事件
EPOLL_CTL_DEL 删除事件
fd:文件描述符
events:事件相关结构体
返回值:
成功返回0
失败返回-1
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
events:
EPOLLIN 读事件
EPOLLOUT 写事件
EPOLLET 边沿触发
LT 水平触发
6.epoll_wait
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
功能:
监听事件表中的事件,并将产生的事件存放到结构体数组中
参数:
epfd:事件表文件描述符
events:存放结果事件结构体数组空间首地址
maxevents:最多存放事件个数
timeout:超时时间
-1:阻塞等待直到有事件发生
返回值:
成功返回产生事件个数
失败返回-1
超时时间到达没有事件发生返回0
优点:
缺点:
优点:
缺点:
优点:
缺点:
综上所述,select、poll和epoll各有优缺点,在实际应用中应根据具体需求和环境选择合适的I/O多路复用技术。对于需要处理大量并发连接的场景,epoll是更好的选择;而对于跨平台性要求较高的场景,则可能需要考虑使用select或poll。