You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
6.1 KiB
198 lines
6.1 KiB
#include <inttypes.h> |
|
/** |
|
* @file test_dummynet_simple.c |
|
* @brief Упрощённый тест модуля dummynet |
|
*/ |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
#include <errno.h> |
|
#include <sys/time.h> |
|
#include <pthread.h> |
|
|
|
#include "../lib/u_async.h" |
|
#include "../lib/socket_compat.h" |
|
#include "../lib/debug_config.h" |
|
#include "../src/dummynet.h" |
|
|
|
#define LISTEN_PORT 17000 |
|
#define PKT_COUNT 100 |
|
#define MAX_PKT_SIZE 1500 |
|
|
|
struct test_header { |
|
uint32_t seq_num; |
|
uint64_t send_time_us; |
|
} __attribute__((packed)); |
|
|
|
static int tests_passed = 0; |
|
static int tests_failed = 0; |
|
|
|
static inline uint64_t get_time_us(void) { |
|
struct timeval tv; |
|
gettimeofday(&tv, NULL); |
|
return (uint64_t)tv.tv_sec * 1000000ULL + tv.tv_usec; |
|
} |
|
|
|
static socket_t create_bound_socket(uint16_t port) { |
|
socket_t sock = socket_create_udp(AF_INET); |
|
if (sock == SOCKET_INVALID) return SOCKET_INVALID; |
|
|
|
struct sockaddr_in addr; |
|
memset(&addr, 0, sizeof(addr)); |
|
addr.sin_family = AF_INET; |
|
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); |
|
addr.sin_port = htons(port); |
|
|
|
int reuse = 1; |
|
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); |
|
|
|
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) != 0) { |
|
socket_close_wrapper(sock); |
|
return SOCKET_INVALID; |
|
} |
|
return sock; |
|
} |
|
|
|
/* Функция для запуска uasync в отдельном потоке */ |
|
static void* uasync_thread(void* arg) { |
|
struct UASYNC* ua = (struct UASYNC*)arg; |
|
printf(" [uasync] Event loop started\n"); |
|
|
|
/* Запускаем цикл на 5 секунд */ |
|
for (int i = 0; i < 500; i++) { |
|
uasync_poll(ua, 10); /* 10 timebase = 1ms */ |
|
usleep(1000); /* 1ms */ |
|
} |
|
|
|
printf(" [uasync] Event loop finished\n"); |
|
return NULL; |
|
} |
|
|
|
int main(void) { |
|
printf("========================================\n"); |
|
printf("Dummynet Simple Test\n"); |
|
printf("========================================\n\n"); |
|
|
|
srand((unsigned int)time(NULL)); |
|
debug_config_init(); |
|
debug_set_level(DEBUG_LEVEL_WARN); |
|
socket_platform_init(); |
|
|
|
struct UASYNC* ua = uasync_create(); |
|
if (!ua) { |
|
printf("[FAIL] Failed to create uasync\n"); |
|
return 1; |
|
} |
|
|
|
/* Создаём сокеты */ |
|
socket_t sock_a = create_bound_socket(LISTEN_PORT - 1); |
|
socket_t sock_b = create_bound_socket(LISTEN_PORT + 1); |
|
|
|
if (sock_a == SOCKET_INVALID || sock_b == SOCKET_INVALID) { |
|
printf("[FAIL] Failed to create sockets\n"); |
|
return 1; |
|
} |
|
|
|
/* Создаём dummynet с задержкой 50ms и jitter 20ms */ |
|
struct dummynet* dn = dummynet_create(ua, "127.0.0.1", LISTEN_PORT); |
|
if (!dn) { |
|
printf("[FAIL] Failed to create dummynet\n"); |
|
return 1; |
|
} |
|
|
|
dummynet_set_direction(dn, DUMMYNET_FORWARD, |
|
50, 20, /* delay: 50ms + 0..20ms */ |
|
0, /* unlimited bandwidth */ |
|
100, /* max queue */ |
|
0, /* no loss */ |
|
"127.0.0.1", LISTEN_PORT + 1); |
|
|
|
dummynet_set_direction(dn, DUMMYNET_BACKWARD, |
|
50, 20, |
|
0, |
|
100, |
|
0, |
|
"127.0.0.1", LISTEN_PORT - 1); |
|
|
|
printf("Configuration:\n"); |
|
printf(" Listen port: %d\n", LISTEN_PORT); |
|
printf(" Delay: 50ms + 0-20ms\n"); |
|
printf(" Bandwidth: unlimited\n"); |
|
printf(" Queue size: 100 packets\n"); |
|
printf(" Loss: 0%%\n\n"); |
|
|
|
/* Запускаем uasync в отдельном потоке */ |
|
pthread_t thread; |
|
pthread_create(&thread, NULL, uasync_thread, ua); |
|
|
|
/* Отправляем пакеты */ |
|
printf("Sending %d packets A->B and B->A...\n", PKT_COUNT); |
|
|
|
uint8_t buf[sizeof(struct test_header) + 100]; |
|
struct test_header* hdr = (struct test_header*)buf; |
|
|
|
for (int i = 0; i < PKT_COUNT; i++) { |
|
/* A -> B */ |
|
hdr->seq_num = i; |
|
hdr->send_time_us = get_time_us(); |
|
|
|
struct sockaddr_in dest; |
|
memset(&dest, 0, sizeof(dest)); |
|
dest.sin_family = AF_INET; |
|
dest.sin_addr.s_addr = inet_addr("127.0.0.1"); |
|
dest.sin_port = htons(LISTEN_PORT); |
|
|
|
socket_sendto(sock_a, buf, sizeof(buf), (struct sockaddr*)&dest, sizeof(dest)); |
|
|
|
/* B -> A */ |
|
hdr->seq_num = i + 1000; |
|
hdr->send_time_us = get_time_us(); |
|
socket_sendto(sock_b, buf, sizeof(buf), (struct sockaddr*)&dest, sizeof(dest)); |
|
|
|
usleep(1000); /* 1ms между пакетами */ |
|
} |
|
|
|
printf("Waiting for packets...\n"); |
|
|
|
/* Ждём завершения потока uasync */ |
|
pthread_join(thread, NULL); |
|
|
|
/* Получаем статистику */ |
|
const struct dummynet_stats* stats_fw = dummynet_get_stats(dn, DUMMYNET_FORWARD); |
|
const struct dummynet_stats* stats_bw = dummynet_get_stats(dn, DUMMYNET_BACKWARD); |
|
|
|
printf("\nResults:\n"); |
|
printf(" Forward (A->B): recv=%llu, sent=%llu, dropped=%llu\n", |
|
stats_fw->recv, stats_fw->sent, stats_fw->dropped); |
|
printf(" Backward (B->A): recv=%llu, sent=%llu, dropped=%llu\n", |
|
stats_bw->recv, stats_bw->sent, stats_bw->dropped); |
|
|
|
/* Проверяем результаты */ |
|
int passed = 1; |
|
|
|
if (stats_fw->recv < PKT_COUNT * 0.9 || stats_bw->recv < PKT_COUNT * 0.9) { |
|
printf("\n[FAIL] Expected >90%% packets received\n"); |
|
printf(" Forward: %llu/%d, Backward: %llu/%d\n", |
|
stats_fw->recv, PKT_COUNT, stats_bw->recv, PKT_COUNT); |
|
passed = 0; |
|
tests_failed++; |
|
} else { |
|
printf("\n[PASS] >90%% packets received successfully\n"); |
|
tests_passed++; |
|
} |
|
|
|
/* Очистка */ |
|
dummynet_destroy(dn); |
|
socket_close_wrapper(sock_a); |
|
socket_close_wrapper(sock_b); |
|
uasync_destroy(ua, 1); |
|
socket_platform_cleanup(); |
|
|
|
printf("\n========================================\n"); |
|
printf("Results: %d passed, %d failed\n", tests_passed, tests_failed); |
|
printf("========================================\n"); |
|
|
|
return tests_failed > 0 ? 1 : 0; |
|
}
|
|
|