1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/calvinwilliams-tcpdaemon

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
README.md 23 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 16.03.2025 10:19 ae44fbb

tcpdaemon

0. Введение

Tcpdaemon — это библиотека, реализующая сервер TCP с использованием callback-функций. Она позволяет легко создавать TCP-серверы, отправляющие и принимающие данные. Для демонстрации работы tcpdaemon можно использовать простой пример C-кода test_callback_echo.c.

$ vi test_callback_echo.c
#include "tcpdaemon.h"
_WINDLL_FUNC int tcpmain(struct TcpdaemonServerEnvironment *p_env, int sock, void *p_addr)
{
    char buffer[4096];
    int len;
    len = recv(sock, buffer, sizeof(buffer), 0);
    if(len == 0)
        return TCPMAIN_RETURN_CLOSE;
    else if(len < 0)
        return TCPMAIN_RETURN_ERROR;
    len = send(sock, buffer, len, 0);
    if(len < 0)
        return TCPMAIN_RETURN_ERROR;
    return TCPMAIN_RETURN_WAITINGFOR_NEXT;
}

Для компиляции этого файла в объектный модуль test_callback_echo.so, который будет использоваться tcpdaemon, выполните следующую команду:

$ gcc -shared -o test_callback_echo.so test_callback_echo.c

Запустите tcpdaemon с помощью следующей команды:

$ tcpdaemon -m IF -l 0:9527 -s test_callback_echo.so -c OnClickListener 10 --tcp-nodelay --logfile $HOME/log/test_callback_echo.log

Этот пример демонстрирует основные возможности tcpdaemon, такие как использование различных режимов работы (-m IF или -m LF) и логирование.

1. Архитектурные особенности

Tcpdaemon поддерживает несколько архитектурных моделей для организации работы TCP-сервера, таких как Forking, Leader-Follower, IO Multiplexing и Windows Threads Leader-Follower.| Архитектурная модель | Описание | | --- | --- | | Forking | Каждый новый запрос приводит к форке процесса, создающего новый экземпляр TCP-сервера. Это обеспечивает параллелизм, но может вызывать проблемы при большом количестве одновременных соединений. | | Leader-Follower | Один лидер управляет несколькими последователями, каждый из которых обслуживает запросы. Это позволяет эффективно распределять нагрузку между процессами. | | IO Multiplexing | Использует механизмы мультиплексирования ввода-вывода для управления множеством соединений. Это позволяет более эффективно использовать системные ресурсы. | | Windows Threads Leader-Follower | Аналогично Leader-Follower, но использует потоки Windows вместо процессов. |Tcpdaemon также поддерживает различные модели запуска, зависящие от операционной системы (например, .exe для Windows и .so для UNIX/Linux).

Запускная модель Описание
Standalone model tcpdaemon.exe + user.so(tcpmain)

Tcpdaemon предоставляет гибкий способ создания и управления TCP-серверами, адаптированный к различным требованиям и условиям использования.

exe(main,tcpmain)+libtcpdaemon.a(tcpdaemon) | запускается программа `user.exe`, которая использует библиотеку `libtcpdaemon.a`. Программа `user.exe` начинается с функции `main(user.exe)` и вызывает `tcpdaemon` из библиотеки `libtcpdaemon.a`. Затем она вызывает `tcpmain`, которая в свою очередь вызывает `tcpdaemon(libtcpdaemon.so)`. Это позволяет программе использовать TCP/IP протокол и модель ввода/вывода. В случае использования модели многопоточности, программа должна создать отдельный поток для выполнения `tcpmain`.

Однопоточная и многопоточная модели

Пример однопоточной модели: user.exe(main)+libtcpdaemon.a(tcpdaemon) + user.so(tcpmain) Здесь основная программа user.exe вызывает функцию main, которая затем вызывает tcpmain из библиотеки user.so. Эта модель позволяет основной программе user.exe использовать tcpdaemon и tcpmain внутри одного процесса.Многопоточная модель требует создания отдельного потока для выполнения tcpmain. Для этого может потребоваться создание отдельного файла user.so, который будет содержать реализацию tcpmain. Таким образом, основная программа user.exe будет использовать tcpdaemon и tcpmain в рамках одной программы, но каждый из этих компонентов будет выполняться в отдельном потоке.

Установка

Для установки на Linux распакуйте архив tcpdaemon-x.y.z.tar.gz в нужную директорию:

$ tar xvzf tcpdaemon-x.y.z.tar.gz
...
$ cd tcpdaemon
$ cd src
$ make -f makefile.Linux install
rm -f LOGC.o
rm -f tcpdaemon_lib.o
rm -f tcpdaemon_main.o
rm -f tcpdaemon
rm -f libtcpdaemon.a
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/home/calvin/include -c tcpdaemon_lib.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/home/calvin/include -c LOGC.c
ar rv libtcpdaemon.a tcpdaemon_lib.o LOGC.o
ar: создает архив libtcpdaemon.a
a - tcpdaemon_lib.o
a - LOGC.o
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/home/calvin/include -c tcpdaemon_main.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -o tcpdaemon tcpdaemon_main.o tcpdaemon_lib.o LOGC.o -L. -L/home/calvin/lib -lpthread -ldl 
cp -rf tcpdaemon /home/calvin/bin/
cp -rf libtcpdaemon.a /home/calvin/lib/
cp -rf tcpdaemon.h /home/calvin/include/tcpdaemon/

Это позволит вам установить tcpdaemon в $HOME/bin, библиотеку libtcpdaemon.a в $HOME/lib и заголовочные файлы в $HOME/include/tcpdaemon.

Использование

3.1. Однопоточная модель Forking

Чтобы использовать однопоточную модель Forking, вам нужно будет скомпилировать и установить отдельный модуль tcpmain, который будет использоваться для HTTP запросов и ответов.``` $ vi test_callback_http_echo.c #include "tcpdaemon.h" _WINDLL_FUNC int tcpmain(struct TcpdaemonServerEnvironment *p_env, int sock, void *p_addr) { char http_buffer[4096 + 1]; long http_len; long len;

``````markdown
### Обработка HTTP запроса

```c
    /* Инициализация буфера HTTP */
    memset( http_buffer , 0x00 , sizeof(http_buffer) );
    http_len = 0 ;
    while( sizeof(http_buffer) - 1 - http_len > 0 )
    {
        len = RECV( sock , http_buffer + http_len , sizeof(http_buffer) - 1 - http_len , 0 ) ;
        if( len == 0 )
            return TCPMAIN_RETURN_CLOSE;
        if( len == -1 )
            return TCPMAIN_RETURN_ERROR;
        if( strstr( http_buffer , "\r\n\r\n" ) )
            break;
        http_len += len ;
    }
    if( sizeof(http_buffer) - 1 - http_len <= 0 )
    {
        return TCPMAIN_RETURN_ERROR;
    }
    /* Отправка ответа HTTP */
    memset( http_buffer , 0x00 , sizeof(http_buffer) );
    http_len = 0 ;
    http_len = sprintf( http_buffer , "HTTP/1. 0 200 OK\r\nContent-length: 17\r\n\r\nHello Tcpdaemon\r\n" ) ;
    SEND( sock , http_buffer , http_len , 0 );
    return TCPMAIN_RETURN_CLOSE;
}

Сборка объектного файла test_callback_http_echo.so

$ gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I.  -I/home/calvin/include/tcpdaemon  -c test_callback_http_echo.c
$ gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -shared -o test_callback_http_echo.so test_callback_http_echo.o -L.  -L/home/calvin/lib -lpthread -ldl 

Запуск демона tcpdaemon с использованием модуля test_callback_http_echo.so

$ tcpdaemon -m IF -l 0:9527 -s test_callback_http_echo.so -c 10 --tcp-nodelay --logfile $HOME/log/test_callback_http_echo.log --loglevel-debug

Проверка работы tcpdaemon с помощью curl

$ curl "http://localhost:9527/"
Hello Tcpdaemon

Исходный код для тестового примера находится в папке test

Пример основной функции `main`
```c
#include "tcpdaemon.h"
extern int tcpmain(struct TcpdaemonServerEnvironment *p_env, int sock, void *p_addr);
int main()
{
    struct TcpdaemonEntryParameter ep;
    memset(&ep, 0x00, sizeof(struct TcpdaemonEntryParameter));
    snprintf(ep.log_pathfilename, sizeof(ep.log_pathfilename), "%s/log/test_main_IOMP.log", getenv("HOME"));
    ep.log_level = LOGLEVEL_DEBUG;
    strcpy(ep.server_model, "IOMP");
    ep.timeout_seconds = 60;
    strcpy(ep.ip, "0");
    ep.port = 9527;
    ep.tcp_nodelay = 1;
    ep.process_count = 1;
    ep.pfunc_tcpmain = &tcpmain;
    ep.param_tcpmain = NULL;
    return -tcpdaemon(&ep);
}
```### Описание структуры `TcpdaemonEntryParameter`

Структура `TcpdaemonEntryParameter` используется в заголовочном файле `tcpdaemon.h`. Она содержит следующие поля:

```c
struct TcpdaemonEntryParameter {
    int daemon_level; // Уровень демона: 1 — запустить, 0 — не запускать (по умолчанию)
    char log_pathfilename[256 + 1]; // Путь к лог-файлу
    int log_level; // Уровень логирования
    char server_model[10 + 1]; // Модель сервера
                                // LF: локальный файловый сервер для UNIX/Linux
                                // IF: интерактивный файловый сервер для UNIX/Linux
                                // WIN-TLF: локальный файловый сервер для Windows
                                // IOMP: интерактивный файловый сервер с мультиплексированием для UNIX/Linux
    int process_count; // Количество процессов
    int max_requests_per_process; // Максимальное количество запросов на процесс
    char ip[20 + 1]; // IP адрес
    int port; // Номер порта
    char so_pathfilename[256 + 1]; // Путь к исполняемому файлу
    char work_user[64 + 1]; // Имя пользователя для работы
    char work_path[256 + 1]; // Путь к рабочей директории
    func_tcpmain *pfunc_tcpmain; // Функция TCP-обработки
    void *param_tcpmain; // Дополнительные параметры для функции TCP-обработки
    int tcp_nodelay; // Включение/отключение TCP_NODELAY: 1 — включено, 0 — отключено
    int tcp_linger; // Время ожидания закрытия соединения: >=1 — время ожидания, 0 — отключено
    int timeout_seconds; // Время ожидания операций IO-Multiplexing
    int install_winservice; // Установка службы Windows
    int uninstall_winservice; // Удаление службы Windows
};
```### Пример использования

Приведённый ниже пример показывает использование структуры `TcpdaemonEntryParameter` при вызове функции `tcpdaemon`.```c
memset(&ep, 0x00, sizeof(struct TcpdaemonEntryParameter));
snprintf(ep.log_pathfilename, sizeof(ep.log_pathfilename), "%s/log/test_main_IOMP.log", getenv("HOME"));
ep.log_level = LOGLEVEL_DEBUG;
strcpy(ep.server_model, "IOMP");
ep.timeout_seconds = 60;
strcpy(ep.ip, "0");
ep.port = 9527;
ep.tcp_nodelay = 1;
ep.process_count = 1;
ep.pfunc_tcpmain = &tcpmain;
ep.param_tcpmain = NULL;
return -tcpdaemon(&ep);
```
```sh
$ vi test_callback_http_echo_nonblock.c
```c
#include "tcpdaemon.h"
struct AcceptedSession {
   int sock; /* сокет */
   struct sockaddr addr; /* адрес сокета */
   char http_buffer[4096 + 1]; /* буфер для HTTP данных */
   int read_len; /* количество прочитанных данных */
   int write_len; /* количество данных для записи */
   int wrote_len; /* количество записанных данных */
};
_WINDLL_FUNC int tcpmain(struct TcpdaemonServerEnvironment *p_env, int sock, void *p_addr)
{
   struct AcceptedSession *p_accepted_session = NULL;
   int len;
   switch(TDGetIoMultiplexEvent(p_env))
   {
      case IOMP_ON_ACCEPTING_SOCKET :
         p_accepted_session = (struct AcceptedSession *) malloc(sizeof(struct AcceptedSession));
         if (p_accepted_session == NULL)
            return TCPMAIN_RETURN_ERROR;
         memset(p_accepted_session, 0x00, sizeof(struct AcceptedSession));
         p_accepted_session->sock = sock;
         memcpy(&p_accepted_session->addr, p_addr, sizeof(struct sockaddr));
         TDSetIoMultiplexDataPtr(p_env, p_accepted_session);
         return TCPMAIN_RETURN_WAITINGFOR_RECEIVING;
      case IOMP_ON_CLOSING_SOCKET :
         p_accepted_session = (struct AcceptedSession *) p_addr;
         free(p_accepted_session);
         return TCPMAIN_RETURN_WAITINGFOR_NEXT;
      case IOMP_ON_RECEIVING_SOCKET :
         p_accepted_session = (struct AcceptedSession *) p_addr;
         len = RECV(p_accepted_session->sock, p_accepted_session->http_buffer + p_accepted_session->read_len,
                    sizeof(p_accepted_session->http_buffer) - 1 - p_accepted_session->read_len, 0);
         if(len == 0)
            return TCPMAIN_RETURN_CLOSE;
``````markdown
                else if (len == -1)
                     return TCPMAIN_RETURN_ERROR;
                  p_accepted_session->read_len += len;
                  if (strstr(p_accepted_session->http_buffer, "\r\n\r\n"))
                 {
                     p_accepted_session->write_len = sprintf(p_accepted_session->http_buffer, "HTTP/1. 0 200 OK\r\n"
                                                                                                  "Content-length: 17\r\n"
                                                                                                  "\r\n");
                 }
                  return TCPMAIN_RETURN_OK;
         }
     }
     ```
```markdown
      "Привет Tcpdaemon\r\n" );
       return TCPMAIN_RETURN_WAITINGFOR_SENDING;
     }
     /* Обработка принятого сессионного соединения */
     if (p_accepted_session->read_len == sizeof(p_accepted_session->http_buffer) - 1)
         return TCPMAIN_RETURN_ERROR;
     /* Переходим в следующее состояние */
     return TCPMAIN_RETURN_WAITINGFOR_NEXT;
     /* Ожидание отправки данных */
 case IOMP_ON_SENDING_SOCKET :
     p_accepted_session = (struct AcceptedSession *) p_addr ;
     /* Отправляем данные через сокет */
     len = SEND(p_accepted_session->sock, p_accepted_session->http_buffer + p_accepted_session->wrote_len, p_accepted_session->write_len - p_accepted_session->wrote_len, 0);
     if (len == -1)
         return TCPMAIN_RETURN_ERROR;
     /* Увеличиваем счетчик отправленных байтов */
     p_accepted_session->wrote_len += len;
     /* Если все данные были отправлены */
     if (p_accepted_session->wrote_len == p_accepted_session->write_len)
         return TCPMAIN_RETURN_CLOSE;
     /* Переходим в следующее состояние */
     return TCPMAIN_RETURN_WAITINGFOR_NEXT;
 default :
     return TCPMAIN_RETURN_ERROR;
 }
 }
 #### Тестирование main_IOMP
```Компиляция тестового файла `test_main_IOMP`:

```
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/home/calvin/include/tcpdaemon -c test_main_IOMP.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -I. -I/home/calvin/include/tcpdaemon -c test_callback_http_echo_nonblock.c
gcc -g -fPIC -O2 -Wall -Werror -fno-strict-aliasing -o test_main_IOMP test_main_IOMP.o test_callback_http_echo_nonblock.o -L. -L/home/calvin/lib -lpthread -ldl -ltcpdaemon
```

Запуск тестового файла `test_main_IOMP`:

```
$ ./test_main_IOMP
```

Логирование выводится в директорию `$HOME/log`.

Проверка работы сервера с помощью `curl`:

```
$ curl "http://localhost:9527/"
Привет Tcpdaemon
```

**Информация о сборке проекта**

# 4. Информация

## 4.1. Описание tcpmain

Функция `tcpmain` является основной функцией демона `tcpdaemon`, которая управляет процессами приема и передачи данных. Она может работать в различных режимах, таких как Forking, Leader-Follower и Windows Threads. В случае ошибки или завершения соединения, она возвращает значения `TCPMAIN_RETURN_CLOSE` и `TCPMAIN_RETURN_ERROR`.
```## Описание работы с IO-Multiplex

Когда используется конфигурация IO-Multiplex, основной поток `tcpmain` периодически проверяет состояние соединений. В зависимости от состояния соединения, основной поток может вернуть одно из следующих значений:

- **TCPMAIN_RETURN_CLOSE** — если соединение должно быть закрыто;
- **TCPMAIN_RETURN_ERROR** — если произошла ошибка;
- **TCPMAIN_RETURN_WAITINGFOR_RECEIVING** — если поток ждет получения данных;
- **TCPMAIN_RETURN_WAITINGFOR_SENDING** — если поток ждет отправки данных;
- **TCPMAIN_RETURN_WAITINGFOR_NEXT** — если поток ждет следующего действия.## Описание работы с IO-Multiplex

Когда используется конфигурация IO-Multiplex, основной поток `tcpmain` периодически проверяет состояние соединений. В зависимости от состояния соединения, основной поток может вернуть одно из следующих значений:

- **TCPMAIN_RETURN_CLOSE** — если соединение должно быть закрыто;
- **TCPMAIN_RETURN_ERROR** — если произошла ошибка;
- **TCPMAIN_RETURN_WAITINGFOR_RECEIVING** — если поток ждет получения данных;
- **TCPMAIN_RETURN_WAITINGFOR_SENDING** — если поток ждет отправки данных;
- **TCPMAIN_RETURN_WAITINGFOR_NEXT** — если поток ждет следующего действия.При необходимости изменения поведения основного потока, вызывается функция `TDSetIoMultiplexDataPtr`, которая позволяет указать новую функцию для управления соединениями.### 4.2. TcpdaemonServerEnvironment

| Функция | Описание |
| --- | --- |
| **TDGetTcpmainParameter** | Получает параметры для основного потока `tcpmain`. |
| **TDGetListenSocket** | Возвращает сокет прослушивания. |
| **TDGetListenSocketPtr** | Возвращает указатель на сокет прослушивания. |
| **TDGetListenAddress** | Возвращает адрес прослушивания в виде структуры `sockaddr_in`. |
| **TDGetListenAddressPtr** | Возвращает указатель на адрес прослушивания в виде структуры `sockaddr_in`. |
| **TDGetProcessCount** | Возвращает количество запущенных процессов. |
| **TDGetEpollArrayBase** | Возвращает базовый массив epoll. |
| **TDGetThisEpoll** | Возвращает текущий epoll. |

```markdown
| Параметр | `struct TcpdaemonServerEnvironment *p_env` tcpdaemon среда |
| --- | --- |
| Текущее значение | Текущий IO мультиплексор epoll |
| Функция | `TDGetIoMultiplexEvent` |
| --- | --- |
| Описание | `int TDGetIoMultiplexEvent(struct TcpdaemonServerEnvironment *p_env);` |
| Параметр | `struct TcpdaemonServerEnvironment *p_env` tcpdaemon среда |
| Возвращаемое значение | Текущий IO мультиплексор epoll события: IOMP_ON_ACCEPTING_SOCKET — событие принятия сокета; IOMP_ON_CLOSING_SOCKET — событие закрытия сокета; IOMP_ON_RECEIVING_SOCKET — событие получения данных; IOMP_ON_SENDING_SOCKET — событие отправки данных |
```

# 5. Пример использования

Пример использования tcpdaemon позволяет легко создать TCP сервер. Этот пример использует HTTP сервер [fasterhttp](http://git.oschina.net/calvinwilliams/fasterhttp) в качестве демонстрационной реализации. Также можно использовать [coconut](http://git.oschina.net/calvinwilliams/coconut) как пример Web сервера.Исходный код tcpdaemon доступен по адресу [GitHub](http://git.oschina.net/calvinwilliams/tcpdaemon). Для связи с автором вы можете обратиться по электронной почте [calvinwilliams@163.com].

Удачи в использовании tcpdaemon!

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/calvinwilliams-tcpdaemon.git
git@api.gitlife.ru:oschina-mirror/calvinwilliams-tcpdaemon.git
oschina-mirror
calvinwilliams-tcpdaemon
calvinwilliams-tcpdaemon
release