【无标题】

c 建立socket,绑定监听套接字,开始监听

    int i_listenfd;
    struct sockaddr_in st_sersock;
    i_listenfd = socket(AF_INET, SOCK_STREAM, 0);	//建立socket套接字
    memset(&st_sersock, 0, sizeof(st_sersock));
    st_sersock.sin_family = AF_INET;  //IPv4协议
    st_sersock.sin_addr.s_addr = htonl(INADDR_ANY);	//htonl, host to net,主机ip数转网络数,网络流为大端存储,统一转换
    st_sersock.sin_port = htons(IP_PORT);

    bind(i_listenfd,(struct sockaddr*)&st_sersock, sizeof(st_sersock));  //将套接字绑定IP和端口用于监听
    listen(i_listenfd, 20) ;//就绪队列最大20
    int fd = accept(i_listenfd, nullptr, nullptr);// 空指针表示关系连接对象ip和port

select

fd_set为要查询的fd,结构为bitmap

FD_SET() // 加入(对应位置1)

FD_CLR() // 删除(对应位置0)

FD_ISSET() // 判断(对应位为1)

FD_ZORE() // 清空

select(max_fd, fd_set, nullptr, nullptr, timeout)

max_fd -> 要监听的最大的fd
timeout -> 没事件的最大阻塞时间,没有返回0

fd_set *now = new fd_set ;
FD_SET(fd, now);
select(10, now, nullptr, nullptr, NULL);// 返回fd_set,其中1表示有事件
if(FD_ISSET(fd, now)){
    char buff[100];
    int op = read(fd, buff, 100);
}
close(fd);

epoll

线程安全(自旋锁+互斥锁)
epoll = 红黑树 + 就绪队列(list)(共用节点,一个红黑树节点也当作就绪队列节点)

epoll_create(MAXSIZE) //创建epoll, maxsize大于0,没啥用

epoll_ctl(epfd, EPOLL_CTL_ADD, i_listenfd, &ev)// 操作epoll增加,删除,改

EPOLL_CTL_ADD:在文件描述符epfd所引用的epoll实例上注册目标文件描述符fd,并将事件事件与内部文件链接到fd。

EPOLL_CTL_MOD:更改与目标文件描述符fd相关联的事件事件。

EPOLL_CTL_DEL:从epfd引用的epoll实例中删除(注销)目标文件描述符fd。该事件将被忽略,并且可以为NULL。

epoll_wait(int epfd,struct epoll_event * events, int maxevents,int timeout)

epoll_wait()系统调用等待文件描述符epfd引用的epoll实例上的事件。事件所指向的存储区域将包含可供调用者使用的事件。 epoll_wait()最多返回最大事件。 maxevents参数必须大于零。 timeout参数指定epoll_wait()将阻止的最小毫秒数。 (此间隔将四舍五入为系统时钟的粒度,并且内核调度延迟意味着阻塞间隔可能会少量溢出。)指定超时值为-1会导致epoll_wait()无限期阻塞,而指定的超时时间等于零导致epoll_wait()立即返回,即使没有可用事件。maxenvents不够epoll返回的话,epoll就绪队列会保存下来,下次调用返回

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <ctype.h>
#include <sys/epoll.h>	//epoll头文件

#define MAXSIZE 80000
#define IP_ADDR "127.0.0.1"
#define IP_PORT 8888

int main()
{
    int i_listenfd, i_connfd;
    struct sockaddr_in st_sersock;
    char msg[MAXSIZE];
    int nrecvSize = 0;
    struct epoll_event ev, events[MAXSIZE];
    int epfd, nCounts;	//epfd:epoll实例句柄, nCounts:epoll_wait返回值
    if((i_listenfd = socket(AF_INET, SOCK_STREAM, 0) ) < 0)	//建立socket套接字
    {
        printf("socket Error: %s (errno: %d)\n", strerror(errno), errno);
        exit(0);
    }
    memset(&st_sersock, 0, sizeof(st_sersock));
    st_sersock.sin_family = AF_INET;  //IPv4协议
    st_sersock.sin_addr.s_addr = htonl(INADDR_ANY);	//INADDR_ANY转换过来就是0.0.0.0,泛指本机的意思,也就是表示本机的所有IP,因为有些机子不止一块网卡,多网卡的情况下,这个就表示所有网卡ip地址的意思。
    st_sersock.sin_port = htons(IP_PORT);
    if(bind(i_listenfd,(struct sockaddr*)&st_sersock, sizeof(st_sersock)) < 0) //将套接字绑定IP和端口用于监听
    {
        printf("bind Error: %s (errno: %d)\n", strerror(errno), errno);
        exit(0);
    }

    if(listen(i_listenfd, 20) < 0)	//设定可同时排队的客户端最大连接个数
    {
        printf("listen Error: %s (errno: %d)\n", strerror(errno), errno);
        exit(0);
    }

    if((epfd = epoll_create(MAXSIZE)) < 0)	//创建epoll实例
    {
        printf("epoll_create Error: %s (errno: %d)\n", strerror(errno), errno);
        exit(-1);
    }

    ev.events = EPOLLIN;
    ev.data.fd = i_listenfd;
    if(epoll_ctl(epfd, EPOLL_CTL_ADD, i_listenfd, &ev) < 0)
    {
        printf("epoll_ctl Error: %s (errno: %d)\n", strerror(errno), errno);
        exit(-1);
    }
    printf("======waiting for client's request======\n");
    //准备接受客户端连接
    while(1)
    {
        if((nCounts = epoll_wait(epfd, events, MAXSIZE, -1)) < 0)
        {
            printf("epoll_ctl Error: %s (errno: %d)\n", strerror(errno), errno);
            exit(-1);
        }
        else if(nCounts == 0)
        {
            printf("time out, No data!\n");
        }
        else
        {
            for(int i = 0; i < nCounts; i++)
            {
                int tmp_epoll_recv_fd = events[i].data.fd;
                if(tmp_epoll_recv_fd == i_listenfd)	//有客户端连接请求
                {
                    if((i_connfd = accept(i_listenfd, (struct sockaddr*)NULL, NULL)) < 0)	//阻塞等待客户端连接
                    {
                        printf("accept Error: %s (errno: %d)\n", strerror(errno), errno);
                        //	continue;
                    }
                    else
                    {
                        printf("Client[%d], welcome!\n", i_connfd);
                    }

                    ev.events = EPOLLIN;
                    ev.data.fd = i_connfd;
                    if(epoll_ctl(epfd, EPOLL_CTL_ADD, i_connfd, &ev) < 0)
                    {
                        printf("epoll_ctl Error: %s (errno: %d)\n", strerror(errno), errno);
                        exit(-1);
                    }
                }
                else	//若是已连接的客户端发来数据请求
                {
                    //接受客户端发来的消息并作处理(小写转大写)后回写给客户端
                    memset(msg, 0 ,sizeof(msg));
                    if((nrecvSize = read(tmp_epoll_recv_fd, msg, MAXSIZE)) < 0)
                    {
                        printf("read Error: %s (errno: %d)\n", strerror(errno), errno);
                        continue;
                    }
                    else if( nrecvSize == 0)	//read返回0代表对方已close断开连接。
                    {
                        printf("client has disconnected!\n");
                        epoll_ctl(epfd, EPOLL_CTL_DEL, tmp_epoll_recv_fd, NULL);
                        close(tmp_epoll_recv_fd);  //
                        continue;
                    }
                    else
                    {
                        printf("recvMsg:%s", msg);
                        if(write(tmp_epoll_recv_fd, msg, MAXSIZE) < 0)
                        {
                            printf("write Error: %s (errno: %d)\n", strerror(errno), errno);
                        }
                        if(write(tmp_epoll_recv_fd, msg, MAXSIZE) < 0)
                        {
                            printf("write Error: %s (errno: %d)\n", strerror(errno), errno);
                        }

                    }
                }
            }
        }
    }
    close(i_listenfd);
    close(epfd);
    return 0;

Epoll回调

Epoll 的回调函数何时执行,此部分需要与 Tcp 的协议栈一起来阐述。Tcp 协议栈的时序图如
下图所示,epoll 从协议栈回调的部分从下图的编号 1,2,3,4。具体 Tcp 协议栈的实现,后续
从另外的文章中表述出来。下面分别对四个步骤详细描述
编号 1:是 tcp 三次握手,对端反馈 ack 后,socket 进入 rcvd 状态。需要将监听 socket 的
event 置为 EPOLLIN,此时标识可以进入到 accept 读取 socket 数据。
编号 2:在 established 状态,收到数据以后,需要将 socket 的 event 置为 EPOLLIN 状态。
编号 3:在 established 状态,收到 fin 时,此时 socket 进入到 close_wait。需要 socket 的 event
置为 EPOLLIN。读取断开信息。
编号 4:检测 socket 的 send 状态,如果对端 cwnd>0 是可以,发送的数据。故需要将 socket
置为 EPOLLOUT。
所以在此四处添加 EPOLL 的回调函数,即可使得 epoll 正常接收到 io 事件。

LT与ET

LT(水平触发)与 ET(边沿触发)是电子信号里面的概念

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值