Esempio n. 1
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);
                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;
}
Esempio n. 3
0
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;
}
Esempio n. 4
0
/*
 * 函数名: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;
}
Esempio n. 6
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;
 }
Esempio n. 7
0
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;
				}
			}
		}
	}
}