zoukankan      html  css  js  c++  java
  • Linux系统编程——水平触发和边沿触发

    事件模型

    Edge Triggered (ET) 边缘触发只有数据到来,才触发,不管缓存区中是否还有数据。

    Level Triggered (LT) 水平触发只要有数据都会触发。

    首先介绍一下LT工作模式:

    LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.

    优点:当进行socket通信的时候,保证了数据的完整输出,进行IO操作的时候,如果还有数据,就会一直的通知你。

    缺点:由于只要还有数据,内核就会不停的从内核空间转到用户空间,所有占用了大量内核资源,试想一下当有大量数据到来的时候,每次读取一个字节,这样就会不停的进行切换。内核资源的浪费严重。效率来讲也是很低的。

    ET:

    ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知。请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once).

    优点:每次内核只会通知一次,大大减少了内核资源的浪费,提高效率。

    缺点:不能保证数据的完整。不能及时的取出所有的数据。

    应用场景: 处理大数据。使用non-block模式的socket。

    水平触发:

    tcp_server.c

    只要有数据就触发,这里为了显示效果将buf[]大小改为5。

    #include <func.h>
    //水平触发,缓冲区有数据就触发
    int main(int argc,char* argv[])
    {
    	ARGS_CHECK(argc,3);
    	int socketFd;
    	socketFd = socket(AF_INET,SOCK_STREAM,0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	int ret;
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息
    	int new_fd;
    	struct sockaddr_in client;
    	bzero(&client, sizeof(client));
    	int addrlen = sizeof(client);
    	new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen);
    	ERROR_CHECK(new_fd, -1, "accept");
    	printf("client ip=%s, port=%d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    	char buf[5] = {0};
    	int epfd = epoll_create(1);//参数size表示监听的数目大小
    	ERROR_CHECK(epfd, -1, "epoll_create");
    	struct epoll_event event, evs[2];
    	event.events = EPOLLIN; //表示对应的文件描述符可读,监控读事件
    	event.data.fd = STDIN_FILENO;
    	ret = epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &event);//一次注册永久生效
    	ERROR_CHECK(ret, -1, "epoll_ctl");
    	event.data.fd = new_fd;
    	epoll_ctl(epfd, EPOLL_CTL_ADD, new_fd, &event);
    	int i, readyFdNum;
    	while(1){
    		memset(evs, 0, sizeof(evs));
    		readyFdNum = epoll_wait(epfd, evs, 2, -1);//大小为2,-1表示永久阻塞,返回值是需要处理的事件数目
    		for(i = 0;i < readyFdNum;i++){
    			if(evs[i].data.fd == new_fd){
    				bzero(buf, sizeof(buf));
    				ret = recv(new_fd, buf, sizeof(buf) - 1, 0);
    				ERROR_CHECK(ret, -1, "recv");
    				if(ret == 0){
    					printf("byebye!
    ");
    					goto chatOver;
    				}			
    				printf("%s
    ", buf);
    			}
    			if(0 == evs[i].data.fd){
    				memset(buf, 0, sizeof(buf));
    				ret = read(STDIN_FILENO, buf, sizeof(buf));
    				if(ret == 0){
    					printf("byebye!
    ");
    					goto chatOver;
    				}
    				ret = send(new_fd, buf, strlen(buf) - 1, 0);
    				ERROR_CHECK(ret, -1, "send");
    			}
    		}
    	}
    chatOver:
    	close(new_fd);
    	close(socketFd);
    	return 0;
    }
    

    tcp_client.c

    #include <func.h>
    
    int main(int argc,char* argv[])
    {
    	ARGS_CHECK(argc,3);
    	int socketFd;
    	socketFd=socket(AF_INET,SOCK_STREAM,0);
    	ERROR_CHECK(socketFd,-1,"socket");
    	struct sockaddr_in ser;
    	bzero(&ser,sizeof(ser));
    	ser.sin_family=AF_INET;
    	ser.sin_port=htons(atoi(argv[2]));
    	ser.sin_addr.s_addr=inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	int ret;
    	ret=connect(socketFd,(struct sockaddr*)&ser,sizeof(ser));
    	ERROR_CHECK(ret, -1, "connect");
    	printf("connect success
    ");
    	char buf[128]={0};
    	fd_set rdset;
    	while(1){
    		FD_ZERO(&rdset);
    		FD_SET(STDIN_FILENO, &rdset);
    		FD_SET(socketFd, &rdset);
    		ret = select(socketFd + 1, &rdset, NULL, NULL, NULL);
    		if(FD_ISSET(socketFd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recv(socketFd, buf, sizeof(buf), 0);
    			ERROR_CHECK(ret, -1, "recv");
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			memset(buf, 0, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			ret = send(socketFd, buf ,strlen(buf) - 1, 0);
    			ERROR_CHECK(ret, -1, "send");
    		}
    	}
    	close(socketFd);
    }
    

    当客户端输入较长的字符串时,效果如下:

    1556457978494

    边沿触发:

    tcp_server.c

    主要修改了注册信号EPOLLET,另外加上了changeNonblock(new_fd)非阻塞,同时在数据到来时加了while(1)循环读取,判断read的返回值跳出循环。

    #include <func.h>
    //水平出发,缓冲区有数据就触发
    //边沿触发,缓冲区数据出现增加时触发
    
    void changeNonblock(int fd){
        int status = fcntl(fd, F_GETFL);
        status = status|O_NONBLOCK;
        fcntl(fd, F_SETFL, status);
    }
    
    int main(int argc,char* argv[])
    {
    	ARGS_CHECK(argc,3);
    	int socketFd;
    	socketFd = socket(AF_INET,SOCK_STREAM,0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	int ret;
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息
    	int new_fd;
    	struct sockaddr_in client;
    	bzero(&client, sizeof(client));
    	int addrlen = sizeof(client);
    	new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen);
    	ERROR_CHECK(new_fd, -1, "accept");
    	printf("client ip=%s, port=%d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    	char buf[5] = {0};
    	int epfd = epoll_create(1);//参数size表示监听的数目大小
    	ERROR_CHECK(epfd, -1, "epoll_create");
    	struct epoll_event event, evs[2];
    	event.events = EPOLLIN|EPOLLET; //表示对应的文件描述符可读,监控读事件
    	event.data.fd = STDIN_FILENO;
        changeNonblock(new_fd);
    	ret = epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &event);//一次注册永久生效
    	ERROR_CHECK(ret, -1, "epoll_ctl");
    	event.data.fd = new_fd;
    	epoll_ctl(epfd, EPOLL_CTL_ADD, new_fd, &event);
    	int i, readyFdNum;
    	while(1){
    		memset(evs, 0, sizeof(evs));
    		readyFdNum = epoll_wait(epfd, evs, 2, -1);//大小为2,-1表示永久阻塞,返回值是需要处理的事件数目
    		for(i = 0;i < readyFdNum;i++){
    			if(evs[i].data.fd == new_fd){
                    while(1){
    	         		bzero(buf, sizeof(buf));
    	         		ret = recv(new_fd, buf, sizeof(buf) - 1, 0);
    	         		//ERROR_CHECK(ret, -1, "recv");
    	         		if(ret == 0){
    	         			printf("byebye!
    ");
    	         			goto chatOver;
    	         		}
                        else if(-1 == ret){
                            printf("
    ");
                            break;
                        }
                        else{
    	         		    printf("%s", buf); //不打换行,等break后再换行刷新
                        }
                    }
                }
    			if(0 == evs[i].data.fd){
    				memset(buf, 0, sizeof(buf));
    				ret = read(STDIN_FILENO, buf, sizeof(buf));
    				if(ret == 0){
    					printf("byebye!
    ");
    					goto chatOver;
    				}
    				ret = send(new_fd, buf, strlen(buf) - 1, 0);
    				ERROR_CHECK(ret, -1, "send");
    			}
    		}
    	}
    chatOver:
    	close(new_fd);
    	close(socketFd);
    	return 0;
    }
    
    

    1556457985915

  • 相关阅读:
    DeepLearning之路(三)MLP
    DeepLearning之路(二)SoftMax回归
    DeepLearning之路(一)逻辑回归
    自然语言处理工具
    一个 11 行 Python 代码实现的神经网络
    对联广告
    Java多线程
    QT数据库操作
    QT笔记
    C++基础入门
  • 原文地址:https://www.cnblogs.com/Mered1th/p/10786863.html
Copyright © 2011-2022 走看看