Linux下网络编程-简易epoll服务器和客户端

Linux下网络编程-简易epoll服务器和客户端

简易epoll服务器水平模式:
//epoll的水平工作模式
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>

int main(int argc, char *argv[])
{
	 // 1.创建套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(lfd == -1)
    {
        perror("socket");
        exit(0);
    }

    //设置端口复用
    int opt=1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 2. 绑定 ip, port
    struct sockaddr_in addr;
    addr.sin_port = htons(9527);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    if(bind(lfd, (struct sockaddr*)&addr, sizeof(addr)) == -1)
    {
        perror("bind");
        exit(0);
    }

    // 3. 监听
    if(listen(lfd, 100) == -1)
    {
        perror("listen");
        exit(0);
    }

    //现在只有监听的文件描述符
    //所有的文件描述符对应读写缓冲区状态都是委托内核进行检测的epoll
    //创建一个epoll模型
    int epfd=epoll_create(100);
    if(epfd==-1)
    {
        perror("epoll_create");
        exit(0);
    }

    //往epoll示例中添加需要检测的节点,现在只有监听的文件描述符
    struct epoll_event ev;
    ev.events=EPOLLIN;//检测lfd都缓冲区是否有数据
    ev.data.fd=lfd;
    if(epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev)==-1)
    {
        perror("epoll_ctl");
        exit(0);
    }

    struct epoll_event evs[1024];
    int size=sizeof(evs)/sizeof(epoll_event);
    //持续监测
    while(1)
    {
        //调用一次,检测一次
        int num=epoll_wait(epfd, evs, size, -1);
        for(int i=0;i<num;i++)
        {
            //取出当前的文件描述符
            int curfd=evs[i].data.fd;
            //判断这个文件描述符是不是用于监听的
            if(curfd==lfd)
            {
                //建立新的连接
                int cfd=accept(curfd, NULL, NULL);
                //新得到的文件描述符添加到epoll模型中,下一轮循环的时候就可以被检测了
                ev.events=EPOLLIN;//检测lfd都缓冲区是否有数据
                ev.data.fd=cfd;
                if(epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev)==-1)
                {
                    perror("epoll_ctl_accept");
                    exit(0);
                }
            }
            else 
            {
                //处理通信的描述符
                //接收数据
                char buf[1024];
                memset(buf,0,sizeof(buf));
                int len=recv(curfd,buf,sizeof(buf),0);
                if(len<=0)
                {
                    printf("客户端已经断开了连接\n");
                    // 将这个文件描述符从epoll模型中删除
                    epoll_ctl(epfd, EPOLL_CTL_DEL, curfd, NULL);
                    close(curfd);
                }   
                else
                {
                    printf("客户端say: %s\n", buf);
                    memset(buf,0,sizeof(buf));
                    strcpy(buf,"ok");
                    send(curfd, buf, len, 0);
                } 
            }
        }
    }
    close(epfd);
    close(lfd);
    return 0;
}
简易epoll服务器边沿模式:
//epoll的边沿工作模式
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>

int main(int argc, char *argv[])
{
	 // 1.创建套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(lfd == -1)
    {
        perror("socket");
        exit(0);
    }

    //设置端口复用
    int opt=1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 2. 绑定 ip, port
    struct sockaddr_in addr;
    addr.sin_port = htons(9527);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    if(bind(lfd, (struct sockaddr*)&addr, sizeof(addr)) == -1)
    {
        perror("bind");
        exit(0);
    }

    // 3. 监听
    if(listen(lfd, 100) == -1)
    {
        perror("listen");
        exit(0);
    }

    //现在只有监听的文件描述符
    //所有的文件描述符对应读写缓冲区状态都是委托内核进行检测的epoll
    //创建一个epoll模型
    int epfd=epoll_create(100);
    if(epfd==-1)
    {
        perror("epoll_create");
        exit(0);
    }

    //往epoll示例中添加需要检测的节点,现在只有监听的文件描述符
    struct epoll_event ev;
    ev.events=EPOLLIN|EPOLLET;//检测lfd都缓冲区是否有数据,设置边沿模式
    ev.data.fd=lfd;
    if(epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev)==-1)
    {
        perror("epoll_ctl");
        exit(0);
    }

    struct epoll_event evs[1024];
    int size=sizeof(evs)/sizeof(epoll_event);
    //持续监测
    while(1)
    {
        //调用一次,检测一次
        int num=epoll_wait(epfd, evs, size, -1);
        printf("==== num: %d\n", num);

        for(int i=0;i<num;i++)
        {
            //取出当前的文件描述符
            int curfd=evs[i].data.fd;
            //判断这个文件描述符是不是用于监听的
            if(curfd==lfd)
            {
                //建立新的连接
                int cfd=accept(curfd, NULL, NULL);
                //将文件描述符设置为非阻塞
                //得到文件描述符的属性
                int flag=fcntl(cfd,F_GETFL);
                flag|=O_NONBLOCK;
                fcntl(cfd, F_SETFL,flag);
                //新得到的文件描述符添加到epoll模型中,下一轮循环的时候就可以被检测了3
                // 通信的文件描述符检测读缓冲区数据的时候设置为边沿模式
                ev.events=EPOLLIN|EPOLLET;//检测lfd都缓冲区是否有数据,设置边沿模式
                ev.data.fd=cfd;
                if(epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev)==-1)
                {
                    perror("epoll_ctl_accept");
                    exit(0);
                }
            }
            else 
            {
                //处理通信的描述符
                //接收数据
                char buf[1024];
                memset(buf,0,sizeof(buf));
                //循环读数据
                while(1)
                {
                    int len=recv(curfd,buf,sizeof(buf),0);
                    if(len==0)
                    {
                        printf("客户端已经断开了连接\n");
                        // 将这个文件描述符从epoll模型中删除
                        epoll_ctl(epfd, EPOLL_CTL_DEL, curfd, NULL);
                        close(curfd);
                    }   
                    else if(len>0)
                    {
                        write(STDOUT_FILENO,buf,len);
                        memset(buf,0,sizeof(buf));
                        strcpy(buf,"ok");
                        send(curfd, buf, len, 0);
                    } 
                    else//len==-1 
                    {
                        if(errno==EAGAIN)
                        {
                            printf("数据读完了...\n");
                            break;
                        }
                        else 
                        {
                            perror("recv");
                            exit(0);
                        }
                    }
                }
            }
        }
    }
    close(epfd);
    close(lfd);
    return 0;
}
简易客户端:
//编译命令:g++ -g xxx.cpp -o xxx
#include <netinet/in.h>
#include <cstdio>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/select.h>
using namespace std;

int main()
{
    //创建socket对象
    int Client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (Client == -1)//判断是否创建成功
    {
        cout << "创建失败" << endl;
        close(Client);
        return -1;
    }

    const char* ip = "127.0.0.1";//保存ip地址
    unsigned short port=9527;//保存端口

    //设置服务端
    sockaddr_in addrServer;
    addrServer.sin_addr.s_addr = inet_addr(ip);
    addrServer.sin_port = htons(port);
    addrServer.sin_family = AF_INET;

    //连接
    if (connect(Client, (sockaddr*)&addrServer, sizeof(addrServer))== -1)
    {
        cout << "连接失败" << endl;
        close(Client);
        return -1;
    }
    cout << "连接服务器成功" << endl;

    //接收和发送数据
    char rec[1024];
    char se[1024];

    while(1)
    {
        memset(rec, 0, sizeof(rec));
        memset(se, 0, sizeof(se));
        
        scanf("%s", se);
        if (strcmp(se, "quit") == 0 || strcmp(se, "exit") == 0)
            break;

        if (send(Client, se, sizeof(se), 0) < 0)
            break;
        cout << "发送:" << se << endl;

        if (recv(Client, rec, sizeof(rec), 0) < 0)
            break;
        cout << "接收:" << rec << endl;
    }

    cout << "已与服务端断开连接" << endl;

    close(Client);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
网络编程中,可以使用epoll来实现多个客户端转发到多个服务器的功能。epollLinux下的一种高性能I/O事件通知机制,可以有效地处理大量的并发连接。 以下是使用epoll实现多个客户端转发到多个服务器的基本步骤: 1. 创建一个监听套接字,用于接收客户端连接。将该套接字添加到epoll事件集合中,监听读事件。 2. 创建一个epoll实例,通过epoll_create函数进行创建。 3. 将监听套接字添加到epoll实例中,使用epoll_ctl函数进行添加。 4. 进入事件循环,通过epoll_wait函数等待事件的发生。该函数会阻塞,直到有事件发生或超时。 5. 当有事件发生时,通过遍历事件数组,处理相应的事件。 6. 如果是监听套接字上有读事件发生,说明有新的客户端连接请求。使用accept函数接受连接,并将新的客户端套接字添加到epoll实例中。 7. 如果是客户端套接字上有读事件发生,说明有客户端发送数据到服务器。可以读取数据,并根据转发策略选择目标服务器,将数据转发给相应的服务器。 8. 如果是服务器套接字上有读事件发生,说明有服务器返回数据给客户端。可以读取数据,并将数据发送给相应的客户端。 9. 处理完事件后,将相应的套接字从epoll实例中删除。 10. 重复步骤4-9,直到事件循环结束。 通过以上步骤,可以实现多个客户端同时连接到服务器,并进行数据转发的功能。需要注意的是,epoll是一种边缘触发模式,需要在处理事件时确保将所有数据都读取完毕或发送完毕,以免遗漏数据或造成阻塞。另外,需要根据具体的业务需求来确定转发策略和服务器选择算法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值