想给公司做个网站怎么做河南省建筑工程信息网
想给公司做个网站怎么做,河南省建筑工程信息网,企业网站建立教程,重庆网站建设营销io复用函数就是服务器端用一个进程/线程#xff0c;同时监听多个文件描述符#xff0c;谁就绪就处理谁#xff0c;不需要为每个连接单独开一个进程或者是线程。1.文件描述符文件描述符的概念#xff1a;
文件描述符就是操作系统给当前进程中#xff08;打开的文件#xf…io复用函数就是服务器端用一个进程/线程同时监听多个文件描述符谁就绪就处理谁不需要为每个连接单独开一个进程或者是线程。1.文件描述符文件描述符的概念文件描述符就是操作系统给当前进程中打开的文件设备网络连接分配的一个非负数字简称fd进程通过这些数字编号就可以找到相对应的资源而不用跟复杂的底层资源打交道。三个标准的文件描述符fd1 stdin标准输入描述符fd2 stdout标准输出描述符fd3 stderr标准错误输出描述符2.select流程用户将要监控的文件描述符添加到fd_set集合中调用select函数内核轮询的检查所有监控的文件描述符当有事件发生或者超时的时候返回给用户程序用户遍历fd_set检查哪些描述符就绪。select函数int select(int fds, fds_set*readfds,//可读 fds_set*writefds,//可写 fds_exceptfds,//异常 struct timeval*timeout);//超时 struct timeval { long tv_sec; //秒数 long tv_usec; // 微秒数 };select代码示例#include stdio.h #include stdlib.h #include unistd.h #include string.h #include sys/select.h #include time.h #include sys/socket.h #include arpa/inet.h #include netinet/in.h #define MAX_FD 128 #define DATALEN 1024 int InitSocket() { // 1. 创建TCP套接字IPv4、字节流、默认TCP协议 int sockfd socket(AF_INET, SOCK_STREAM, 0); if(sockfd -1) { perror(socket create failed); // 补充错误提示方便调试 return -1; } // 2. 初始化地址结构体 struct sockaddr_in sadd; memset(sadd, 0, sizeof(sadd)); // 清空结构体 sadd.sin_family AF_INET; // IPv4地址族 sadd.sin_port htons(6000); // 绑定6000端口转网络字节序 sadd.sin_addr.s_addr inet_addr(127.0.0.1); // 绑定本地回环地址 // 3. 绑定套接字到指定地址和端口 int res bind(sockfd, (struct sockaddr*)sadd, sizeof(sadd)); if(res -1) { perror(bind failed); close(sockfd); // 绑定失败需关闭套接字避免资源泄漏 return -1; } // 4. 开始监听连接队列长度5 res listen(sockfd, 5); if(res -1) { perror(listen failed); close(sockfd); // 监听失败关闭套接字 return -1; } printf(InitSocket success! Listen on 127.0.0.1:6000\n); return sockfd; } // 初始化文件描述符数组 void InitFds(int fds[], int n) { int i 0; for (; i n; i) { fds[i] -1; } } // 将套接字描述符添加到数组中 void AddFdToFds(int fds[], int fd, int n) { int i 0; for (; i n; i) { if (fds[i] -1) { fds[i] fd; break; } } } // 删除数组中的套接字描述符 void DelFdFromFds(int fds[], int fd, int n) { int i 0; for (; i n; i) { if (fds[i] fd) { fds[i] -1; break; } } } // 将数组中的套接字描述符设置到fd_set变量中,并返回当前最大的文件描述符值 int SetFdToFdset(fd_set *fdset, int fds[], int n) { FD_ZERO(fdset); int i 0, maxfd fds[0]; for (; i n; i) { if (fds[i] ! -1) { FD_SET(fds[i], fdset); if (fds[i] maxfd) { maxfd fds[i]; } } } return maxfd; } void GetClientLink(int sockfd, int fds[], int n) { struct sockaddr_in caddr; memset(caddr, 0, sizeof(caddr)); socklen_t len sizeof(caddr); int c accept(sockfd, (struct sockaddr *)caddr, len); if (c 0) { return; } printf(A client connection was successful\n); AddFdToFds(fds, c, n); } // 处理客户端数据 void DealClientData(int fds[], int n, int clifd) { char data[DATALEN] {0}; int num recv(clifd, data, DATALEN - 1, 0); if (num 0) { DelFdFromFds(fds, clifd, n); close(clifd); printf(A client disconnected\n); } else { printf(%d: %s\n, clifd, data); send(clifd, OK, 2, 0); } } // 处理select返回的就绪事件 void DealReadyEvent(int fds[], int n, fd_set *fdset, int sockfd) { int i 0; for (; i n; i) { if (fds[i] ! -1 FD_ISSET(fds[i], fdset)) { if (fds[i] sockfd) { GetClientLink(sockfd, fds, n); } else { DealClientData(fds, n, fds[i]); } } } } int main() { int sockfd InitSocket(); assert(sockfd ! -1); fd_set readfds; int fds[MAX_FD]; InitFds(fds, MAX_FD); InitFds(fds, MAX_FD); AddFdToFds(fds, sockfd, MAX_FD); while (1) { int maxfd SetFdToFdset(readfds, fds, MAX_FD); struct timeval timeout; timeout.tv_sec 2; // 秒数 timeout.tv_usec 0; // 微秒数 int n select(maxfd 1, readfds, NULL, NULL, timeout); if (n 0) { printf(select error\n); break; } else if (n 0) { printf(time out\n); continue; } DealReadyEvent(fds, MAX_FD, readfds, sockfd); } exit(0); }3.poll流程定义和初始化pollfd结构体数组调用poll函数将监控信息传递到内核中用户遍历 pollfd 数组检查哪些描述符就绪。#includepoll.h int poll(struct pollfd*fds,nds_t nfds,int timeout);pollfd结构体struct pollfd { int fd;//文件描述符 short events;//注册的事件 short revents;//实际发生事件有内核填充 }代码演示#include stdio.h #include stdlib.h #include unistd.h #include string.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include poll.h #define MAXFD 10 int socket_init(); void fds_init(struct pollfd fds[]) { for(int i 0; i MAXFD; i ) { fds[i].fd -1; fds[i].events 0; fds[i].revents 0; } } void fds_add(struct pollfd fds[], int fd) { for(int i 0; i MAXFD; i ) { if( fds[i].fd -1 )//数组该元素空闲 { fds[i].fd fd; fds[i].events POLLIN;//读 fds[i].revents 0; break; } } } void fds_del(struct pollfd fds[], int fd) { for(int i 0; i MAXFD; i ) { if( fds[i].fd fd) { fds[i].fd -1; fds[i].events 0; fds[i].revents 0; break; } } } void accept_client(int sockfd, struct pollfd fds[]) { int c accept(sockfd,NULL,NULL);//新产生了连接c if( c 0 ) { return; } printf(accept c%d\n,c); fds_add(fds,c); } void recv_data(int c, struct pollfd fds[]) { char buff[128] {0}; int num recv(c,buff,127,0);//接受数据 if( num 0 )//对方关闭了 { fds_del(fds,c);//移除该描述符 printf(close(%d)\n,c); return ; } printf(buff%s\n,buff); send(c,ok,2,0); } int main() { int sockfd socket_init();//创建 if( sockfd -1 ) { exit(1); } struct pollfd fds[MAXFD];//10 fds_init(fds);//fd-1 fds_add(fds,sockfd);//将监听套接子sockfd添加到poll的数组 while( 1 ) { int n poll(fds,MAXFD,5000);//阻塞 if( n -1 ) { printf(poll err\n); } else if( n 0 ) { printf(time out\n); } else { for(int i 0; i MAXFD;i ) { if( fds[i].fd -1 ) { continue; } if( fds[i].revents POLLIN )//POLLIN 读 { //sockfd, c if( fds[i].fd sockfd ) { //accept accept_client(sockfd,fds); } else { //recv recv_data(fds[i].fd,fds); } } } } } } int socket_init() { int sockfd socket(AF_INET,SOCK_STREAM,0); if( sockfd -1 ) { return -1; } struct sockaddr_in saddr; memset(saddr,0,sizeof(saddr)); saddr.sin_family AF_INET; saddr.sin_port htons(6000); saddr.sin_addr.s_addr inet_addr(127.0.0.1); int res bind(sockfd,(struct sockaddr*)saddr,sizeof(saddr)); if( res -1 ) { printf(bind err\n); return -1; } res listen(sockfd,5); if( res -1 ) { return -1; } return sockfd; }4.epollepoll是Linux特有的io复用实现函数。epoll的实现流程 1.epoll_creare()创建内核事件表实际就是红黑树创建就绪就绪队列(创建链表。2.epoll内核实现采用注册回调函数的方式时间复杂度是O(1).3.epoll_ctl实现文件描述符的添加和删除epoll_wait返回直接获取到已经就绪的文件描述符。代码演示 #include stdio.h #include stdlib.h #include unistd.h #include string.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include sys/epoll.h #define MAXFD 10 void epoll_add(int epfd,int fd) { struct epoll_event ev; ev.data.fd fd; ev.events EPOLLIN|EPOLLRDHUP;//读 if( epoll_ctl(epfd,EPOLL_CTL_ADD,fd,ev) -1) { printf(epoll ctl add err\n); } } void epoll_del(int epfd, int fd) { if( epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL) -1) { printf(epoll ctl del err\n); } } void accept_client(int sockfd, int epfd) { int c accept(sockfd,NULL,NULL);//接受新连接 if( c 0 ) { return; } printf(accpet c%d\n,c); epoll_add(epfd,c);//新连接加入到内核事件表(红黑树) } void recv_data(int c, int epfd) { char buff[128] {0}; int n recv(c,buff,127,0); if( n 0 ) { epoll_del(epfd,c); close(c); printf(client close\n); return; } printf(buff(%d)%s\n,c,buff); send(c,ok,2,0); } int socket_init(); int main() { int sockfd socket_init(); if( -1 sockfd ) { exit(1); } int epfd epoll_create(MAXFD);//创建内核事件表 rbr红黑树, rdlist 就绪队列 if( -1 epfd ) { exit(1); } epoll_add(epfd,sockfd);//将sockfd,添加到内核事件表红黑树 struct epoll_event evs[MAXFD];//10 数组存放就像描述符 while( 1 ) { int n epoll_wait(epfd,evs,MAXFD,5000);//-1 if( n -1 ) { printf(epoll err\n); } else if( n 0 ) { printf(time out\n); } else { for(int i 0; i n; i ) { int fd evs[i].data.fd; if( evs[i].events EPOLLRDHUP ) { printf(close hup c%d\n,fd); epoll_del(epfd,fd); close(fd); continue; } if( evs[i].events EPOLLIN ) { if( fd sockfd ) { accept_client(fd,epfd); } else { recv_data(fd,epfd); } } //if( evs[i].events EPOLLOUT) } } } } int socket_init() { int sockfd socket(AF_INET,SOCK_STREAM,0); if(-1 sockfd ) { return -1; } struct sockaddr_in saddr; memset(saddr,0,sizeof(saddr)); saddr.sin_family AF_INET; saddr.sin_port htons(6000); saddr.sin_addr.s_addr inet_addr(127.0.0.1); int res bind(sockfd,(struct sockaddr*)saddr,sizeof(saddr)); if( -1 res) { printf(bind err\n); return -1; } res listen(sockfd,5); if( -1 res ) { return -1; } return sockfd; }select poll和epoll的区别:1.每次条用select和poll需要将描述符拷贝到内核2.内核实现轮询的方式3.select poll返回后需要遍历所有的描述符找到就绪的Onepoll1.每个描述符只需要拷贝一次到内核内核创建事件2.内核实现回调函数3.epoll返回后直接返回就绪的文件描述符5.ET模式和LT模式LT模式水平触发只要fd处于就绪状态每次调用epoll——wait就会返回fd的事件ET模式边缘触发当fd处于就绪状态变化的时候才会返回事件。