Анализ проектирования и реализации сетевого коммуникационного модуля Redis

Redis

Коммуникационный модуль Redis очень прост и удобен в использовании, его можно напрямую использовать в ваших собственных проектах, и его также очень полезно изучить.

Эта статья написана на основе исходного кода Redis 4.0.1, загрузка исходного кода Redis:GitHub.com/Антиэнтузиазм/Горячие…

файловая структура

Сетевой коммуникационный модуль Redis состоит из 8 файлов.

Эффект следующий

документ эффект
ae.c Унифицированный epoll, evport, kqueue, выбор интерфейса обработки сетевых событий, реализация функций
ae.h Унифицированный epoll, evport, kqueue, выбор интерфейса обработки сетевых событий, прототип функции, определение общей структуры
ae_epoll.c Инкапсулируйте библиотеку обработки сетевых событий epoll в унифицированный интерфейс.
ae_evport.c Инкапсулируйте библиотеку обработки сетевых событий evport в унифицированный интерфейс.
ae_kqueue.c Инкапсулирует библиотеку обработки сетевых событий kqueue в единый интерфейс.
ae_select.c Инкапсулируйте выбранную библиотеку обработки сетевых событий в единый интерфейс

Нижний интерфейс унифицированной сетевой библиотеки

Унифицированный интерфейс обработки сетевых событий выглядит следующим образом: ae_epoll.c, ae_evport.c, ae_kqueue.c, ae_select.c.

интерфейс эффект
aeApiState Структуры обмена данными, такие как fd и события, требуемые базовой сетевой библиотекой.
aeApiCreate Создайте сетевой дескриптор, например, дескриптор epoll, на этой основе для мониторинга и обработки сетевых событий.
aeApiResize Изменение размера контейнера события хранения сетевой библиотеки заключается в изменении размера массива событий структуры aeApiState.
aeApiFree Удалить общую структуру aeApiState
aeApiAddEvent Передайте операции чтения и записи сети fd в сетевую библиотеку для обработки, например, обработки этого epoll
aeApiDelEvent Когда вы удалите, будет использоваться для мониторинга сетевых операций с библиотеки FD, общий сервер к клиенту после написания данных, инициатива отключить клиента
aeApiPoll Опрос для получения события о том, что в сети происходят события чтения и записи ввода-вывода.
aeApiName Получите имя базовой сетевой библиотеки, такой как epool, kqueue и т. д.

Верхний интерфейс единой сетевой библиотеки

Обратитесь к ae.h, ae.c, чтобы инкапсулировать общие структуры и функции.

Структура сетевых событий чтения и записи aeFileEvent

/* File event structure */
typedef struct aeFileEvent {
    int mask; /* one of AE_(READABLE|WRITABLE) */
    aeFileProc *rfileProc;
    aeFileProc *wfileProc;
    void *clientData;
} aeFileEvent;

Эта структура инкапсулирует функцию обработки Prototype сетевых данных чтения и записи и записи и данных с клиента.

Обработчик AefileProc - это прототип определений макроса, макроспределения относятся к следующему:

typedef void aeFileProc(
    struct aeEventLoop *eventLoop,
    int fd,
    void *clientData,
    int mask
);

При фактическом использовании функция обработки сетевых событий чтения и записи требуетНапишите свой собственный

Структура временного события aeTimeEvent

/* Time event structure */
typedef struct aeTimeEvent {
    long long id; /* time event identifier. */
    long when_sec; /* seconds */
    long when_ms; /* milliseconds */
    aeTimeProc *timeProc;
    aeEventFinalizerProc *finalizerProc;
    void *clientData;
    struct aeTimeEvent *next;
} aeTimeEvent;

Это синхронизированное событие используется Redis для обработки фоновых синхронизированных задач, таких как обработка времени ожидания подключения клиента и операции отключения сервера.

активный фд

/* A fired event */
typedef struct aeFiredEvent {
    int fd;
    int mask;
} aeFiredEvent;

Это сеть fd, ожидающая чтения и записи, обычно представленная в виде массива.

Поле маски указывает, ждать ли чтения или записи, и соответствующие значения следующие

#define AE_READABLE 1
#define AE_WRITABLE 2

Основная структура верхнего интерфейса сетевой библиотеки aeEventLoop

/* State of an event based program */
typedef struct aeEventLoop {
    int maxfd;   /* highest file descriptor currently registered */
    int setsize; /* max number of file descriptors tracked */
    long long timeEventNextId;
    time_t lastTime;     /* Used to detect system clock skew */
    aeFileEvent *events; /* Registered events */
    aeFiredEvent *fired; /* Fired events */
    aeTimeEvent *timeEventHead;
    int stop;
    void *apidata; /* This is used for polling API specific data */
    aeBeforeSleepProc *beforesleep;
    aeBeforeSleepProc *aftersleep;
} aeEventLoop;

Основные поля следующие *Список событий*events, индекс указывает fd, указывает функцию обратного вызова, которая будет выполняться при возникновении события чтения и записи сети fd * *fired ожидает чтения и записи списка fd, полученного опросом, например epoll, полученного epoll_wait * beforesleep выполняется до epoll_wait * aftersleep выполняется после epoll_wait

Работа с функциями, связанными с aeEventLoop

В основном для работы со структурой aeEventLoop, вот несколько основных функций, например

aeCreateEventLoop()

Эта функция в основном создает структуру aeEventLoop и выделяет пространство содержимого для событий и запускает их в соответствии с параметром setsize. Затем вызовите aeApiCreate для управления базовой сетевой библиотекой, такой как epoll_create() из epoll, и сохраните ее в атрибуте apidata aeEventLoop.

aeMain()

В основном, чтобы запустить бесконечный цикл, опросить активный fd, если epoll, нижний уровень вызывает epoll_wait, чтобы получить доступный для чтения и записи fd: ae.c::aeMain() -> ae.c::aeProcessEvents() -> ae_epoll.c :: aeApiPool() -> ae_epoll.c::epoll_wait()

aeCeateFileEvent()

Зарегистрируйте network fd в базовой сетевой библиотеке и зарегистрируйте бизнес-функцию обратного вызова, которая должна выполняться при возникновении события чтения и записи. Возьмите epoll в качестве примера, шаги для вызова epoll: ae.c::aeCreateFileEvent() -> ae_epoll.c::aeApiAddEvent() -> epoll_ctl()

anet.h & anet.c

Он в основном инкапсулирует операции сокетов, скрывает различия в базовых операциях сокетов в системе и предоставляет улучшенные API, такие как создание серверов tcp и т. д.

Давайте посмотрим на процесс создания tcp-сервера здесь.

anetTcpServer()

anet.c::anetTcpServer() -> anet.c::_anetTcpServer() -> <sys/socket.h>::socket() -> anet.c::anetListen() -> <sys/socket.h>::listen()

Основной код заключается в вызове функции прослушивания библиотеки системных сокетов для установки tcp-сервера.

Пример

Здесь используйте ae.h и anet.h для создания простого tcp-сервера и отправки клиенту «Hello World».

код показывает, как показано ниже:

#include <stdio.h>
#include <zconf.h>
#include "../src/ae.h"
#include "../src/anet.h"

char myerr[ANET_ERR_LEN] = {0};
void acceptFd(struct aeEventLoop *eventLoop, int fd, void *clientdata, int mask) {
    char myerr[ANET_ERR_LEN] = {0};
    printf("获取客户端连接的fd.\n");
    char ip[20] = {0};
    int port = 0;
    int clientfd = anetTcpAccept(myerr, fd, ip, sizeof(ip), &port);

    if (clientfd == AE_ERR) {
        printf("获取客户端连接的fd异常!! \n");
        return;
    }

    printf("客户端ip %s port %d \n", ip, port);

    int ret = anetNonBlock(myerr, clientfd);
    if (ret == ANET_OK) {
        printf("客户端事件非阻塞处理设置成功\n\n");
    }
    anetEnableTcpNoDelay(myerr, clientfd);
    write(clientfd,"Hello Client!\n",14);
}

int main() {
    aeEventLoop *eventLoop = aeCreateEventLoop(1024);
    if(!eventLoop){
        return 1;
    }
    int fd = anetTcpServer(myerr, 8080, "0.0.0.0", 511);

    if (fd != ANET_ERR) {
        anetNonBlock(NULL, fd);
        if (aeCreateFileEvent(eventLoop, fd, AE_READABLE, acceptFd, NULL)) {
            printf("注册tcp服务器接收客户端连接的事件处理函数异常");
        }
    }

    printf("服务器在 0.0.0.0:8080 端口监听了! \n");
    aeMain(eventLoop);
    aeDeleteEventLoop(eventLoop);
    return 0;
}

Затем компилируем и запускаем, при компиляции можно использовать gcc, эффект следующий

gcc -o myserver src/ae.c src/anet.c src/zmalloc.c myserver.c

Можно проверить через телнет

Вы можете видеть, что клиент успешно получил сообщение Hello World, возвращенное сервером.

некоторые замечания

Если стандартную библиотеку c не удается найти в Mac OS 10.14.5, необходимо установить этот файл: /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg

использованная литература

  1. blog.51CTO.com/LeeHome/1021…
  2. blog.csdn.net/yu Si Gu Юань / ...