首页 > 系统相关 >【Linux网络编程-7】epoll边沿触发

【Linux网络编程-7】epoll边沿触发

时间:2024-07-18 18:40:06浏览次数:23  
标签:ev epoll int 编程 epollfd fd Linux include

非阻塞recv
EAGAIN、EWOULDBLOCK错误码值11

返回值 含义
>0 接收字节数
0 接收FIN包,连接被对端断开
-1 (errno==EAGAIN||EWOULDBLOCK)表示还有数据未读。反之,则表示发生了错误。
//epollServer.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <errno.h>
#include <vector>
#include <time.h>
#include <sys/epoll.h>
#include <fcntl.h>

#define MAXEVENTS 100

int InitServer(const unsigned int port);

int setnoblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) {
        return -1;
    }

    flags |= O_NONBLOCK;
    if (fcntl(fd, F_SETFL, flags) == -1) {
        return -1;
    }

    return 0;
}

bool addfd(int epollfd, int fd, struct epoll_event* ev)
{
    memset(ev,0,sizeof(struct epoll_event));
    ev->data.fd=fd;
    ev->events=EPOLLIN;
    //setnoblocking(fd);
    return 0==epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,ev);	//添加到epollfd
}

bool addfdET(int epollfd, int fd, struct epoll_event* ev)
{
    memset(ev,0,sizeof(struct epoll_event));
    ev->data.fd=fd;
    ev->events=EPOLLIN|EPOLLET;
    setnoblocking(fd);
    return 0==epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,ev);	//添加到epollfd
}


int main(int argc,char** argv)
{
    int listenfd=InitServer(atoi(argv[1]));
    printf("listenfd=%d\n",listenfd);
    if(listenfd==-1)
    {
        printf("InitServer failed..\n");
        return -1;
    }

    //创建epoll句柄
    int epollfd=epoll_create(1);
    if(epollfd<0)   printf("epoll_create failed..\n");

    //epoll_event封装listenfd和监视类型为有数据可读(EPOLLIN)
    struct epoll_event ev;
    addfd(epollfd, listenfd, &ev);

    while(true)
    {
        struct epoll_event events[MAXEVENTS];	//epoll_wait返回时保存发生的事件
        int infds=epoll_wait(epollfd,events,MAXEVENTS,-1);
        if(infds<0) 
        {
            printf("epoll_wait failed..\n");
            break;
        }
        if(infds==0)
        {
            printf("timeout..\n");
            break;
        }
        
        //发生的事件再events中从index=0依次保存
        for(int i=0;i<infds;i++) 
        {
            //listenfd上有数据可读事件发生
            if((events[i].data.fd==listenfd) && (events[i].events&EPOLLIN))
            {
                struct sockaddr_in client_addr;
                socklen_t client_addr_size=sizeof(client_addr);
                int clientfd = accept(listenfd, (struct sockaddr*)&client_addr,&client_addr_size);
                if(clientfd<0)
                {
                    printf("accept failed..\n");
                    continue;
                }

                //连接上的客户端添加进监视
                //边缘触发
                addfdET(epollfd,clientfd,&ev);	
                continue;
            }
            else if(events[i].events&EPOLLIN)	//有数据可读事件
            {
                //边缘触发:这段代码不会被epoll_wait重复触发,所以需要手动循环读取直到数据读完。因为循环recv,不能设为阻塞
                while(true)
                {
                    char buf[1024];
                    memset(buf,0,sizeof(buf));
                    int isize = recv(events[i].data.fd,buf,sizeof(buf),0);
                    
                    if(isize==0)	//连接被对端关闭
                	{
                        printf("对端关闭连接\n");
                    	memset(&ev,0,sizeof(struct epoll_event));
                    	ev.data.fd=events[i].data.fd;
                    	ev.events=EPOLLIN;
                    	epoll_ctl(epollfd,EPOLL_CTL_DEL,events[i].data.fd,&ev);
                    	close(events[i].data.fd);
                    	printf("%ld client(%d) disconneted..\n",time(0),events[i].data.fd);
                    	break;
                	}
                    else if(isize==-1) //数据未读完或发生错误
                    {
                        if((errno==EAGAIN) || (errno==EWOULDBLOCK))	//数据未读完
                        {
                            
                            continue;	//继续循环执行recv
                        }
                        else
                        {
                            //错误,终止recv循环
                            break;
                        }

                    }
                    
                    printf("client(%d)recv msg:%s,size=%d\n",events[i].data.fd,buf,isize);	//recv返回值>0

                	//判断sockfd是否可写
                    fd_set tmpfd;
                    FD_ZERO(&tmpfd);
                    FD_SET(events[i].data.fd,&tmpfd);
                    if(select(events[i].data.fd+1,0,&tmpfd,0,0)<0)
                    {
                        printf("没有可写数据的fd\n");
                        break;
                    }
                    if(write(events[i].data.fd,buf,strlen(buf))<0)	//发送
                    {
                        printf("write failed..\n");
                        close(events[i].data.fd);
                        break;
                    }

                }
            }
            
       }
    }
    return 0;
}

int InitServer(const unsigned int port)
{
    int sockfd=-1;
    sockfd = socket(PF_INET, SOCK_STREAM, 0);
    if(sockfd == -1) return -1;

    int opt=1;
    unsigned int len=sizeof(opt);
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, len);
	
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family=AF_INET;
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);	
    server_addr.sin_port=htons(port);

    if(bind(sockfd, (struct sockaddr*) &server_addr, sizeof(server_addr)) == -1)
    {
        close(sockfd);
        sockfd=-1;
        return -1;
    }
    
    if(listen(sockfd, 5) == -1)
    {
        close(sockfd);
        sockfd=-1;
        return -1;
    }

    return sockfd;
}

标签:ev,epoll,int,编程,epollfd,fd,Linux,include
From: https://www.cnblogs.com/wk2522466153/p/18310241

相关文章

  • 操作系统发展简史(Unix/Linux 篇 + DOS/Windows 篇)+ Mac 与 Microsoft 之风云争霸
    操作系统发展简史(Unix/Linux篇)说到操作系统,大家都不会陌生。我们天天都在接触操作系统——用台式机或笔记本电脑,使用的是windows和macOS系统;用手机、平板电脑,则是android(安卓)和iOS系统。如果是从事信息通信行业,还经常会和ubuntu、CentOS、Fedora这样的Linux......
  • AMD R2000 Bilby 单板 串口在Grub和Linux下的使用
    Ubuntu20启动时,自动加载了UART驱动,系统启动信息含有UART的相关信息。[2.418748]printk:console[ttyS4]disabled[2.418757]AMDI0020:00:ttyS4atMMIO0xfedc9000(irq=3,base_baud=3000000)isa16550A[2.418820]printk:console[ttyS4]enabled[......
  • Linux基础命令
    1.查询目录中内容:ls2.查看日志:tail -f -n 日志文件名选项: -f:显示最新的追加打印的内容 -n 行数如:tail-f-n1000 a.log 3.查看所有正在运行的进程: ps-ef  如: 查看到进程号以后 ps-ef|grepjava 4.强制杀死进程:killpid......
  • linux发布项目
    1,在linux上配置.net环境https://learn.microsoft.com/zh-cn/dotnet/core/install/linux官方网址(1)找到安装系统的版本(2)选择安装的.net版本(安装SDk和运行时)2.在linux上创建一个文件夹,把发布的文件移动到文件夹中visualstudio运行api使用命令dotnet(api).dll文件配置任何......
  • Vim 高手指南:Linux 环境下的高级使用技巧
    Vim高手指南:Linux环境下的高级使用技巧前言Vim是一个功能强大的文本编辑器,广泛应用于Linux系统以及各种编程环境中。作为一个Vim高级用户和Linux系统管理员,你将在这里学到如何充分利用Vim的高级功能,提升你的工作效率。第1章:Vim编辑器的基本概念和模式1.1Vi......
  • 初写博客之自己的编程之路正式开启
    初写博客之自己的编程之路正式开启前言:本篇文章是第一次在CSDN上面写博客文章,为了让大家也能在我学习在编程的道路学习道路上获得一些属于自己的灵感。本篇文章将从以下方面铺展开来:①关于本人的自我介绍②以后自己在编程上的目标③自己将如何学习编程④自己每周花在编......
  • Shell编程速查手册(仅入门)
    一.Shell概述Shell是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言。Shell是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。Shell脚本(shellscript),是一种为shell编写的脚本程序......
  • linux系统和windows系统如何同步时间,服务器时间变动怎么同步
    一、Linux系统时间同步1.使用NTP(网络时间协议)NTP是最常用的Linux系统时间同步方式。NTP通过连接到外部时间服务器(如原子钟或GPS接收器)来获取高精度的时间信息,并校准本地系统时间。步骤:安装NTP客户端:在Ubuntu系统上,可以使用命令sudoapt-getinstallntp安装NTP客户端......
  • Linux入门---(二)shell命令
    1.1man获得帮助信息help只能查询内嵌命令,外部命令查询格式:命令--help1.2快捷键ctrl+u,清空当前已输入,但未执行的命令1.3文件目录类从根目录/开始的就是绝对路径,从当前文件夹开始的就是相对路径pwd显示当前工作目录的绝对路径ls列出目录的内容(ls-a列出全部文件)cd切......
  • Leetcoede编程基础0到1——1768. 交替合并字符串& 389. 找不同
    1768.交替合并字符串题目描述:给你两个字符串 word1 和 word2 。请你从 word1 开始,通过交替添加字母来合并字符串。如果一个字符串比另一个字符串长,就将多出来的字母追加到合并后字符串的末尾。返回 合并后的字符串 。输入输出实例:  示例1:输入:word1="ab......