zoukankan      html  css  js  c++  java
  • C语言实现简单epoll服务器(二)

    共有的头文件

    /*
    utility.h
    */
    #ifndef __UTILITY_H_INCLUDED
    #define __UTILITY_H_INCLUDED
    
    #include<iostream>
    #include<list>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<sys/epoll.h>
    #include<fcntl.h>
    #include<errno.h>
    #include<unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    using namespace std;
    
    //存储所有客户端的sockfd
    list<int> client_list;
    
    #define SERVER_IP "127.0.0.1"
    #define SERVER_PORT 8888
    #define EPOLL_SIZE 5000
    #define BUF_SIZE 0xFFFF
    
    #define SERVER_WELCOME "Welcome you join to chat room! Your chat ID is: Clent %d"
    #define SERVER_MESSAGE "ClientID %d say >> %s"
    
    #define EXIT "EXIT"
    #define CAUTION "There is only one int the char room!"
    
    int setnonblocking(int sockfd)
    {
        fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0) | O_NONBLOCK);
        return 0;
    }
    
    void addfd(int epollfd, int fd, bool enable_et)
    {
        struct epoll_event ev;
        ev.data.fd = fd;
        ev.events= EPOLLIN;
        if (enable_et)
        {
            ev.events = EPOLLIN | EPOLLET;
        }
        epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
        setnonblocking(fd);
        printf("fd added to epoll!
    
    ");
    }
    
    int sendBroadcasemessage(int clientfd)
    {
        //buf接收新的聊天信息
        //message保存格式信息
        char buf[BUF_SIZE], message[BUF_SIZE];
        bzero(buf, BUF_SIZE);
        bzero(buf, BUF_SIZE);
    
        printf("read from client(ClientID = %d)
    ", clientfd);
        int len = recv(clientfd, buf, BUF_SIZE, 0);
    
        //len等于0表示客户端关闭了链接
        if (0 == len)
        {
            close(clientfd);
            client_list.remove(clientfd);
            printf("Clientfd = %d closed.
     now there are %d client int the chat room
    ",
                    clientfd, (int)client_list.size());
        }
        else    //广播信息
        {
            if (client_list.size() == 1)
            {
                send(clientfd, CAUTION, strlen(CAUTION), 0);
                return len;
            }
    
            sprintf(message, SERVER_MESSAGE, clientfd, buf);
    
            list<int>::iterator it;
            for (it = client_list.begin(); it != client_list.end(); ++it)
            {
                if (*it != clientfd)
                {
                    if (send (*it, message, BUF_SIZE, 0) < 0)
                    {
                        perror("error");
                        exit(-1);
                    }
                }
            }
        }
        return len;
    }
    
    #endif
    /*
    server.h
    */
    #include"utility.h"
    
    int main()
    {
    
        struct sockaddr_in serverAddr;
        serverAddr.sin_family = PF_INET;
        serverAddr.sin_port = htons(SERVER_PORT);
        serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP);
    
        //创建监听socket
        int listener = socket(PF_INET, SOCK_STREAM, 0);
        if (listener < 0)
        {
            perror("listener");
            exit(-1);
        }
    
        printf("listen socket created 
    ");
    
        //绑定地址
        if (bind(listener, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
        {
            perror("bind error");
            exit(-1);
        }
    
        //监听
        int ret = listen(listener, 5);
        if (ret < 0)
        {
            perror("listen error");
            exit(-1);
        }
        printf("Start to listen: %s
    ", SERVER_IP);
    
        //在内核中创建事件表
        int epfd = epoll_create(EPOLL_SIZE);
        if (epfd < 0)
        {
            perror("epfd error");
            exit(-1);
        }
        printf("epoll created, epollfd = %d
    ", epfd);
    
        static struct epoll_event events[EPOLL_SIZE];
        
        //往内核事件中添加事件
        addfd(epfd, listener, true);
    
        while (1)
        {
            //表示就绪的事件的数目
            int epoll_events_count = epoll_wait(epfd, events, EPOLL_SIZE, -1);
            
            if (epoll_events_count < 0)
            {
                perror("epoll failure");
                break;
            }
    
            printf("epoll_events_count = %d
    ", epoll_events_count);
    
            //处理这epoll_event_count个就绪事件
            for (int i = 0; i < epoll_events_count; ++i)
            {
                int sockfd = events[i].data.fd;
                
                //表示新用户连接
                if (sockfd == listener)
                {
                    struct sockaddr_in client_address;
                    socklen_t client_addrLength = sizeof(struct sockaddr_in);
                    int clientfd = accept(listener, (struct sockaddr*) &client_address, &client_addrLength);
    
                    printf("client connection from: %s : %d(IP : port) client = %d
    ", 
                            inet_ntoa(client_address.sin_addr),
                            ntohs(client_address.sin_port),
                            clientfd);
    
                    addfd(epfd, clientfd, true);
    
                    //服务端用list保存用户连接
                    client_list.push_back(clientfd);
                    printf("Add new clientfd = %d to epoll
    ", clientfd);
                    printf("Now there are %d clients int the chat room
    ", (int)client_list.size());
    
                    //服务器发送欢迎消息
                    printf("welcome message
    ");
                    char message[BUF_SIZE];
                    bzero(message, BUF_SIZE);
    
                    sprintf(message, SERVER_WELCOME, clientfd);
    
                    int ret = send(clientfd, message, BUF_SIZE, 0);
                    if (ret < 0)
                    {
                        perror("send error");
                        exit(-1);
                    }
                }
    
                //处理用户发来的小心,并广播,使其他用户收到小心
                else
                {
                    int ret = sendBroadcasemessage(sockfd);
                    if (ret < 0)
                    {
                        perror("error");
                        exit(-1);
                    }
                }            
            }
        }
        
        close(listener);
        close(epfd);
        return 0;
    }

    客户端

    /*
    client.h
    */
    #include"utility.h"
    
    int main(int argc, char *argv[])
    {
        struct sockaddr_in serverAddr;
        serverAddr.sin_family = PF_INET;
        serverAddr.sin_port = htons(SERVER_PORT);
        serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP);
    
        //创建socket
        int sock = socket(PF_INET, SOCK_STREAM, 0);
        if (sock < 0)
        {
            perror("error");
            exit(-1);
        }
    
        //连接服务器
        if (connect(sock, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0)
        {
            perror("connect
    ");
            exit(-1);
        }
    
        //创建管道,其中fd[0]表示父进程读,fd[1]表示子进程写
        int pipe_fd[2];
        if (pipe(pipe_fd) < 0)
        {
            perror("pipe error");
            exit(-1);
        }
        
        //创建epoll
        int epfd = epoll_create(EPOLL_SIZE);
        if (epfd < 0)
        {
            perror("epfd error");
            exit(-1);
        }
    
        static struct epoll_event events[2];
        
        //将sock和管道读端描述符都添加到内核事件中
        addfd(epfd, sock, true);
        addfd(epfd, pipe_fd[0], true);
    
        bool isClientwork = true;
        char message[BUF_SIZE];
    
        int pid = fork();
        if (pid < 0)
        {
            perror("fork error");
            exit(-1);
        }
        else if (0 == pid)  //子进程
        {
            //子进程负责写入管道,因此先关闭读端
           close(pipe_fd[0]);
           printf("Please input 'exit' to exit the chat room
    ");
    
           while (isClientwork)
           {
               bzero(&message, BUF_SIZE);
               fgets(message, BUF_SIZE, stdin);
    
               if (strncasecmp(message, EXIT, strlen(EXIT)) == 0)
               {
                   isClientwork = 0;
               }
               else        //子进程将信息写入管道
               {
                   if (write(pipe_fd[1], message, strlen(message) - 1) < 0)
                   {
                       perror("fork error");
                       exit(-1);
                   }
               }           
           }      
        }
        else    //父进程
        {
            //父进程负责都管道数据,因此先关闭写端
            close(pipe_fd[1]);
    
            while(isClientwork)
            {
                int epoll_event_count = epoll_wait(epfd, events, 2, -1);
                for (int i = 0; i < epoll_event_count; i++)
                {
                    bzero(&message, BUF_SIZE);
                    
                    //处理就绪事件
                    if (events[i].data.fd == sock)
                    {
                        int ret = recv(sock, message, BUF_SIZE, 0);
    
                        if (0 == ret)
                        {
                            printf("Server closed connection: %d
    ", sock);
                            close(sock);
                            isClientwork = 0;
                        }
                        else
                        {
                            printf("%s
    ", message);
                        }                    
                    }
                    else    //子进程写入事件发生,父进程处理并发送服务端
                    {
                        int ret = read(events[i].data.fd, message, BUF_SIZE);
    
                        if (0 == ret)
                        {
                            isClientwork = 0;
                        }
                        else
                        {
                            send(sock, message, BUF_SIZE, 0);
                        }                    
                    }                
                }
            }
        }
        
        if (pid)
        {
            close(pipe_fd[0]);
            close(sock);
        }
        else
        {
            close(pipe_fd[1]);
        }
        return 0;
    }
  • 相关阅读:
    Oracle Words Spelling Error
    原创 分布式锁与应用场景
    原创 分布式事务简单实现思路
    原创 使用redis位图实现布隆过滤器
    原创 redis实现接口限流
    原创 jwt-security简单实现
    原创 抢购秒杀之redis高性能实现
    原创 springcloud feign优化配置
    原创 派单、抢单业务简单实现
    原创 微信公众号推送图片实现
  • 原文地址:https://www.cnblogs.com/wanghao-boke/p/12159561.html
Copyright © 2011-2022 走看看