/** * connect_timeout - connect * @fd: 套接字 * @addr: 要连接的对方地址 * @wait_seconds: 等待超时秒数,如果为0表示正常模式 * 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT */ int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds) { int ret; socklen_t addrlen = sizeof(struct sockaddr_in); if (wait_seconds > 0) activate_nonblock(fd); ret = connect(fd, (struct sockaddr*)addr, addrlen); if (ret < 0 && errno == EINPROGRESS) { fd_set connect_fdset; struct timeval timeout; FD_ZERO(&connect_fdset); FD_SET(fd, &connect_fdset); timeout.tv_sec = wait_seconds; timeout.tv_usec = 0; do { /* 一量连接建立,套接字就可写 */ ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout); } while (ret < 0 && errno == EINTR); if (ret == 0) { ret = -1; errno = ETIMEDOUT; } else if (ret < 0) return -1; else if (ret == 1) { /* ret返回为1,可能有两种情况,一种是连接建立成功,一种是套接字产生错误,*/ /* 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 */ int err; socklen_t socklen = sizeof(err); int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen); if (sockoptret == -1) { return -1; } if (err == 0) { ret = 0; } else { errno = err; ret = -1; } } } if (wait_seconds > 0) { deactivate_nonblock(fd); } return ret; }
int connect_timeout(int sockfd,const struct sockaddr *addr, socklen_t addrlen,int tval) { int ret = 0; if(tval > 0) { //这里首先进行connect操作,然后是 //定时看连接是否超时,如果不超时就可以写了 //所以不能阻塞到连接成功, //而应该通过select来定时判断是否连接成功 activate_nonblock(sockfd); } ret = connect(sockfd,(struct sockaddr *)addr,addrlen); if(ret < 0 && EINPROGRESS == errno) { fd_set connect_fdset; struct timeval timeout; timeout.tv_sec = tval; timeout.tv_usec = 0; FD_ZERO(&connect_fdset); FD_SET(sockfd,&connect_fdset); do { ret = select(sockfd + 1,NULL,&connect_fdset,NULL,&timeout); } while(ret < 0 && EINTR == errno); if(0 == ret) { //timeout errno = ETIMEDOUT; ret = -1; } else if(ret < 0) { return ret; } else { //这里返回有两种情况,要么是连接成功,要么连接失败, //但是连接失败不会在select中以错误的方式返回 //所以需要再次使用getsockopt来进行近一步的判断 int err; socklen_t socklen = sizeof(err); int sockoptret = getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&err,&socklen); if(-1 == sockoptret) { return sockoptret; } if(0 == err) { ret = 0; } else { errno = err; ret = -1; } } } //要恢复阻塞的状态 if(tval > 0) { deactivate_nonblock(sockfd); } return ret; }
int connect_timeout(int fd, struct sockaddr *addr, unsigned int wait_seconds) { int ret = 0; socklen_t addrlen = sizeof(struct sockaddr_in); if(wait_seconds > 0) activate_nonblock(fd); ret = connect(fd, (struct sockaddr *)addr, addrlen); if(ret == -1 && errno == EINPROGRESS) { fd_set connect_fdset; FD_ZERO(&connect_fdset); FD_SET(fd, &connect_fdset); struct timeval timeout; timeout.tv_sec = wait_seconds; timeout.tv_usec = 0; do{ ret = select(fd+1, NULL, &connect_fdset, NULL, &timeout); }while(ret == -1 && errno == EINTR); if(ret == 0){ ret = -1; errno = ETIMEDOUT; }else if(ret < 0){ ret = -1; }else if(ret == 1){ int err; socklen_t socklen = sizeof(err); int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen); if(sockoptret == -1){ return -1; }else if(err == 0){ ret = 0; }else{ errno = err; ret = -1; } } } if(wait_seconds > 0){ deactivate_nonblock(fd); } return ret; }
/* * 函数名:connect_timeout * 描述:客服端接受数据 * 参数: * * 返回: * */ int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds) { int ret; //获取socket结构体的大小。 socklen_t addrlen = sizeof(struct sockaddr_in); //如果传入的等待时间大于0就取消socket的阻塞状态,0则不执行。 if (wait_seconds > 0) activate_nonblock(fd); //链接 /* * int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen); * * */ ret = connect(fd, (struct sockaddr*) addr, addrlen); //EINPROGRESS 正在处理 if (ret < 0 && errno == EINPROGRESS) { /* * void FD_CLR(int fd, fd_set *set); * int FD_ISSET(int fd, fd_set *set); * void FD_SET(int fd, fd_set *set); * void FD_ZERO(fd_set *set); * */ //设置监听集合 fd_set connect_fdset; struct timeval timeout; //初始化集合 FD_ZERO(&connect_fdset); //把fd 文件描述符的socket加入监听集合 FD_SET(fd, &connect_fdset); /* * struct timeval { * long tv_sec; // seconds 秒 * long tv_usec; // microseconds 微妙 * }; * */ timeout.tv_sec = wait_seconds; timeout.tv_usec = 0; do { // 一但连接建立,则套接字就可写 所以connect_fdset放在了写集合中 ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout); } while (ret < 0 && errno == EINTR); if (ret == 0) { ret = -1; /* * #define ETIMEDOUT 110 // Connection timed out * Tcp是面向连接的。在程序中表现为,当tcp检测到对端socket不再可 * 用时(不能发出探测包,或探测包没有收到ACK的响应包),select会 * 返回socket可读,并且在recv时返回-1,同时置上errno为ETIMEDOUT。 * */ errno = ETIMEDOUT; } else if (ret < 0) return -1; else if (ret == 1) { //printf("22222222222222222\n"); /* ret返回为1(表示套接字可写),可能有两种情况,一种是连接建立成功,一种是套接字产生错误,*/ /* 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 */ int err; socklen_t socklen = sizeof(err); //获取socket的状态 int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen); if (sockoptret == -1) { return -1; } if (err == 0) { ret = 0; } else { errno = err; ret = -1; } } } if (wait_seconds > 0) { deactivate_nonblock(fd); } return ret; }
int main(void) { int count = 0; //signal(SIGCHLD, SIG_IGN); // signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, handle_sigpipe); signal(SIGCHLD, handle_sigchld); int listenfd; /*if((listenfd = socket(PF_INET, SOCK_STRAM, 0))<0);*/ if((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) ERR_EXIT("socket"); struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5188); servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//表示本机的任意地址 // servaddr.sin_addr.s_addr = htonol(INADDR_ANY); // servaddr.sin_addr.s_addr = inet_addr("127, 0, 0, 1"); // inet_aton("127.0.0.1", &servaddr.sin_addr); // int on = 1; if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))< 0) ERR_EXIT("setxockopt"); if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))<0) ERR_EXIT("bind"); if(listen(listenfd, SOMAXCONN) < 0) ERR_EXIT("listen"); std::vector<int> clients; int epollfd; epollfd = epoll_create1(EPOLL_CLOEXEC); struct epoll_event event; event.data.fd = listenfd; event.events = EPOLLIN | EPOLLET; epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event); EventList events(16); struct sockaddr_in peeraddr; socklen_t peerlen; int conn; int i; int nready; while(1){ nready = epoll_wait(epollfd, &*events.begin(), static_cast<int>(events.size()), -1); if(nready == -1){ if(errno == EINTR) continue; ERR_EXIT("epoll_wait"); } if(nready == 0) continue; if((size_t)nready == events.size()) events.resize(events.size()*2); for(i = 0; i < nready; i++){ if(events[i].data.fd == listenfd){ peerlen = sizeof(peeraddr); // 对方的地址长度一定要有初始值 conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen); if(conn == -1) ERR_EXIT("accept"); printf("ip=%s port=%d\n",inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)) ; printf("count=%d\n", ++count); clients.push_back(conn); activate_nonblock(conn); event.data.fd = conn; event.events = EPOLLIN | EPOLLET; epoll_ctl(epollfd, EPOLL_CTL_ADD, conn, &event); } else if (events[i].events & EPOLLIN){// 这是已连接套接字产生的可读事件 conn = events[i].data.fd; if(conn < 0) continue; char recvbuf[1024] = {0}; int ret = readline(conn, recvbuf, 1024); if(ret == -1) ERR_EXIT("readline"); if(ret == 0){ printf("client close\n"); close(conn); event = events[i]; epoll_ctl(epollfd, EPOLL_CTL_DEL, conn, &event); clients.erase(std::remove(clients.begin(), clients.end(), conn), clients.end()); } fputs(recvbuf, stdout); writen(conn, recvbuf, strlen(recvbuf)); } } } return 0; }
/** * connect_timeout - connect * @fd: 套接字 * @addr: 要连接的对方地址 * @wait_seconds: 等待超时秒数,如果为0表示正常模式 * 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT */ int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds) { int ret; socklen_t addrlen = sizeof(struct sockaddr_in); if (wait_seconds > 0) activate_nonblock(fd); ret = connect(fd, (struct sockaddr*)addr, addrlen); if (ret < 0 && errno == EINPROGRESS) { /* fd_set connect_fdset; struct timeval timeout; FD_ZERO(&connect_fdset); FD_SET(fd, &connect_fdset); timeout.tv_sec = wait_seconds; timeout.tv_usec = 0; do { ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout); } while (ret < 0 && errno == EINTR); */ int epfd; epfd = epoll_create1(0); struct epoll_event evn; struct epoll_event *events = NULL; evn.data.fd = fd; evn.events = EPOLLOUT | EPOLLET; epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &evn); do{ ret = epoll_wait(epfd, events, MAXEVENTS, wait_seconds); }while(ret < 0 && errno == EINTR); if (ret == 0) { ret = -1; errno = ETIMEDOUT; } else if (ret < 0) return -1; else if (ret == 1) { /* ret返回为1,可能有两种情况,一种是连接建立成功,一种是套接字产生错误,*/ /* 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 */ int err; socklen_t socklen = sizeof(err); int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen); if (sockoptret == -1) { return -1; } if (err == 0) { ret = 0; } else { errno = err; ret = -1; } } } if (wait_seconds > 0) { deactivate_nonblock(fd); } return ret; }
void http_server_main_loop_epoll_et(int listenedfd) { int i = 0; int nready = 0; int connfd = -1; int ret = 0; struct sockaddr_in peeraddr; socklen_t peerlen = sizeof(peeraddr); http_data h_info; //建立epoll机制 int epollfd = epoll_create1(EPOLL_CLOEXEC); struct epoll_event event; //采用et模式的时候所有加入epoll的套接字都要采用非阻塞的方式 //如果文件描述符是阻塞的,那么读或者写操作会因为没有后续的事 //件而一直处于阻塞状态(饥渴状态) activate_nonblock(listenedfd); event.data.fd = listenedfd; event.events = EPOLLIN | EPOLLET;//采用边沿触发的方式 epoll_ctl(epollfd,EPOLL_CTL_ADD,listenedfd,&event); struct epoll_event events[MAX_FD_SETSIZE]; while(1) { nready = epoll_wait(epollfd,events,MAX_FD_SETSIZE,-1); if(-1 == nready) { if(EINTR == errno) { continue; } perror("epoll_wait"); break; } if(0 == nready) { continue; } //每次返回的事件都会保存到events数组中所以可以直接来进行判断 for(i = 0;i < nready;++i) { if(events[i].data.fd == listenedfd) { while(1) { //accept可能会有多次连接(特别是在进行并发测试的时候) //如果不加,可能会导致write出现EPIPE的错误可能是由于 //epoll的ET模式的机制,如果同时到达的accept不能处理 //完,那么有些就会饿死在epoll中 peerlen = sizeof(peeraddr); connfd = accept(listenedfd, (struct sockaddr *)&peeraddr,&peerlen); if(connfd < 0) { if(EAGAIN == errno || EWOULDBLOCK == errno) { break; } else { perror("accept"); return; } } DEBUG_PRINT("ip = %s port = %d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); //设定为非阻塞套接字 activate_nonblock(connfd); event.data.fd = connfd; event.events = EPOLLIN | EPOLLET; epoll_ctl(epollfd,EPOLL_CTL_ADD,connfd,&event); } } else if(events[i].events & EPOLLIN) { connfd = events[i].data.fd; if(connfd < 0) { continue; } //业务逻辑代码 bzero(&h_info,sizeof(h_info)); h_info.data_len = sizeof(h_info.buf); //处理接收到的数据 ret = read_http_data(connfd,&h_info); if(ret < 0) { if(EAGAIN == errno || EWOULDBLOCK == errno) { continue; } else { perror("read_http_data"); return; } } if(0 == ret) { //client close DEBUG_PRINT("client close\n"); //FD_CLR(client[i],&rfset); close(connfd); //从epoll中删除代码 event = events[i]; epoll_ctl(epollfd,EPOLL_CTL_DEL,connfd,&event); continue; } //解析数据填充数据类型结构体 ret = analysis_http_data(&h_info); ret = send_http_data(connfd,&h_info); if(ret < 0) { //这里一般来说会有EPIPE的错误,但是在其它很多的程序中, //对这个错误似乎是无法接受的直接报错退出 perror("send_http_data"); return; } } } } }