单线程 epoll 服务器

本文介绍了一个简单的单线程服务器实现方案,通过 epoll 进行事件管理,利用自定义的数据结构进行内存池管理,实现了客户端连接接收及数据读取功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     尝试写了个稍微大一点的东西 。。遇到最坑爹的 BUG 就是 read 和 recv 的行为不一样 使用 read 就没事。。。无解  以后再也不用 recv 了。。。。单线程的好处就是不需要使用锁  所有操作全都为异步  不能作为异步的部分 应该使用队列来和异步操作的部分做桥接。

  

/*
实现一个单线程的 服务器
*/
#define __FD_SETSIZE 65535
#include "main.h"
#include "ss.h"
#include "buffer.h"

#define dbg_msg(fmt,msg...) do{\
        printf( "%s::%d::%s():" fmt,__FILE__,__LINE__, __FUNCTION__,##msg ); \
    }while(0);

struct buffer_list
{
    struct list_head list;
    buffer_context buffer;
};

struct client_list
{
    struct list_head list;
    int fd;
    struct sockaddr_in addr;
    socklen_t addr_len;
};

struct server_context
{
    u_int16_t port;
    int fd;//监听的 fd
    int epoll_fd;
    struct sockaddr_in addr;
    int addr_len;
    struct buffer_list buffer_pool;
    struct client_list clients;
};
/*
*/
buffer_context *server_alloc_buffer(struct server_context *context)
{
    buffer_context *buffer;
    struct list_head *p, *n;
    if (list_empty(&context->buffer_pool.list))
    {
        //链表是空的
        buffer = &((struct buffer_list *)malloc(sizeof(struct buffer_list)))->buffer;
        buffer_init(buffer);
    }
    else
    {
        list_for_each_safe(p, n, &context->buffer_pool.list)
        {
            buffer = &list_entry(p, struct buffer_list, list)->buffer;
            buffer_clean(buffer);
            list_del_init(&context->buffer_pool.list); // <--- 貌似 init 很有必要啊
            break;
        }
    }
    dbg_msg("get buffer : %x \n", (u_int32_t)buffer);
    return buffer;
}

/*
把这个 buffer 放回 pool 里
*/
void server_free_buffer(struct server_context *server, buffer_context *buffer)
{
    struct buffer_list *list = list_entry(buffer, struct buffer_list, buffer);
    dbg_msg("try to free buffer : %x \n", (uint32_t)buffer);
    assert(&list->buffer == buffer);
    list_add(&list->list, &server->buffer_pool.list);
}

void server_insert_client(struct server_context *server, struct client_list *client)
{
    list_add(&client->list, &server->clients.list);
}

int server_remove_client(struct server_context *server, struct client_list *client)
{
    int ret = -1;
    struct list_head *p, *n;
    dbg_msg("remove socket %d \n", client->fd);
    list_for_each_safe(p, n, &server->clients.list)
    {
        struct client_list *tmp = list_entry(p, struct client_list, list);
        if (tmp == client)
        {
            list_del_init(p);
            free(tmp);
            ret = 1;
            break;
        }
    }
    if (-1 == ret)
    {
        dbg_msg("do not find client %x !!!!!\n", (int)client);
    }
    return ret;
}

int readall(int fd, buffer_context *buffer)
{
    int err;
    for (;;)
    {
        unsigned char buff[8192];
        err = read(fd , buff, 8192);//MSG_WAITALL);
        if (err < 0)
        {
            if (errno == EAGAIN )
                break;
            dbg_msg("cause error : %d %s \n", errno, strerror(errno));
        }
        else if (err > 0)
        {
            buffer_write(buffer, buff, err);
        }
        else
        {
            // == 0 ??  此时是断开了 。。
            dbg_msg("recv == 0 %d %s \n", errno, strerror(errno));
            break;
        }
    }
    return err;
}

void set_fd_noblock(int fd)
{
    int flags;
    flags = fcntl(fd, F_GETFL, 0);
    flags |= O_NONBLOCK;
    fcntl(fd, F_SETFL, flags);
}

int  add2epoll(int epoll_fd, int fd, void *ptr)
{
    struct epoll_event ev;
    dbg_msg("epoll add fd %d \n", fd);
    //ev.data.fd = fd;
    ev.data.ptr = ptr;
    ev.events = EPOLLIN ;
    return epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
}

int del_from_epoll(int fd, int epoll_fd)
{
    return epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
}

int server_start(struct server_context *context, uint16_t port)
{
    int ret = -1;
    int fd = socket(AF_INET, SOCK_STREAM , IPPROTO_TCP);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = 0x0;
    if (-1 != bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)))
    {
        int epoll_fd;
      /*  int yes = 1;
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
        {
            perror("setsockopt");
            exit(1);
        }
        */
        set_fd_noblock(fd);
        if (-1 != (epoll_fd = epoll_create(100)))
        {
            add2epoll(epoll_fd, fd, context);
            context->epoll_fd = epoll_fd;
            context->fd = fd;
            context->port = port;
            INIT_LIST_HEAD(&context->clients.list);
            INIT_LIST_HEAD(&context->buffer_pool.list);
            listen(fd, 1);
            ret = 1;
        }
        else
        {
            close(fd);
            dbg_msg("epoll %d %s ", errno, strerror(errno));
        }
    }
    else
    {
        dbg_msg("bind failed %d:%s \n", errno, strerror(errno));
        close(fd);
        ret = -1;
    }
    return ret;
}

int server_stop()
{
    return -1;
}

int server_loop(struct server_context *context)
{
    int nr_events;
    struct epoll_event events;

    while (1)
    {
        int i = 0;
        memset(&events, 0, sizeof(struct epoll_event));
        dbg_msg("enter wait.................\n");
        nr_events = epoll_wait (context->epoll_fd, &events, 1, -1);
        if (errno != 0)
            dbg_msg("epoll_wait return errno : %d %s\n", errno, strerror(errno));
        if (nr_events < 0)
        {
            dbg_msg("epoll_wait \n");
            getchar();
        }
        if (events.events & EPOLLERR)
        {
            if (context == events.data.ptr)
            {
                dbg_msg("EPOLLERR on server socket !!!!!!!!!!! \n");
                getchar();
            }
            else
            {
                struct client_list *client = events.data.ptr;
                dbg_msg("recv EPOLLERR remote close ?! \n");
                //close(client->fd);
                //del_from_epoll(client->fd, context->epoll_fd);
                //server_remove_client(context, client);
            }
            //continue;
        }
        if (events.events & EPOLLHUP) //这个事件  一直存在在集合中 不需要特别设置
        {
            dbg_msg("recv event EPOLLHUP !!!!! \n");
        }
        if (events.events & EPOLLIN)
        {
            //dbg_msg("epoll event EPOLLIN on fd : %x \n", events.data.fd);
            /*
            */
            if (events.data.ptr == context)
            {
                struct client_list *client = (struct client_list *)malloc(sizeof(struct client_list));
                client->addr_len = sizeof(struct sockaddr_in);
                dbg_msg("EPOLLIN on listen socket \n");
                client->fd = accept(context->fd,
                                    (struct sockaddr *)&client->addr,
                                    &client->addr_len);
                if (-1 == client->fd)
                {
                    dbg_msg("accept failed : %d %s \n", errno, strerror(errno));
                }
                else
                {
                    dbg_msg("accept a new client %d \n", client->fd);
                    server_insert_client(context, client);
                    set_fd_noblock(client->fd);
                    if (-1 == add2epoll(context->epoll_fd, client->fd, client))
                    {
                        dbg_msg("add 2 epoll failed %d %s \n", errno, strerror(errno));
                        getchar();
                    }
                }
            }
            else
            {
                int ret = 0;
                struct client_list *client = events.data.ptr;
                dbg_msg("EPOLLIN on fd : %d \n", client->fd);
                //客户端发来消息了//
                buffer_context *buffer = server_alloc_buffer(context);
                ret = readall(client->fd,
                              buffer);
                if (buffer_get_length(buffer))
                {
                    int end = 0;
                    buffer_write(buffer, (unsigned char *)&end, 4);
                    dbg_msg("recv %ld bytes\n", buffer_get_length(buffer));//, buffer_getat(buffer, 0));
                    server_free_buffer(context, buffer);
                }
                if (0 == ret)
                {
                    del_from_epoll(client->fd, context->epoll_fd);
                    close(client->fd);
                    server_remove_client(context, client);
                }
            }
        }
    }
}

int main(int arc, char **argv)
{
    struct server_context context;
    server_start(&context, 1234);
    server_loop(&context);
    return 0;
}

转载于:https://my.oschina.net/sincoder/blog/135197

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值