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!\n\n");
}

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)\n", clientfd);
    int len = recv(clientfd, buf, BUF_SIZE, 0);

    //len等于0表示客户端关闭了链接
    if (0 == len)
    {
        close(clientfd);
        client_list.remove(clientfd);
        printf("Clientfd = %d closed.\n now there are %d client int the chat room\n",
                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 \n");

    //绑定地址
    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\n", SERVER_IP);

    //在内核中创建事件表
    int epfd = epoll_create(EPOLL_SIZE);
    if (epfd < 0)
    {
        perror("epfd error");
        exit(-1);
    }
    printf("epoll created, epollfd = %d\n", 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\n", 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\n", 
                        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\n", clientfd);
                printf("Now there are %d clients int the chat room\n", (int)client_list.size());

                //服务器发送欢迎消息
                printf("welcome message\n");
                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\n");
        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\n");

       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\n", sock);
                        close(sock);
                        isClientwork = 0;
                    }
                    else
                    {
                        printf("%s\n", 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;
}
posted @ 2020-01-07 08:59  王清河  阅读(1113)  评论(0编辑  收藏  举报