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
) и логирование.
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
.
Чтобы использовать однопоточную модель 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 )