Initial commit: dpdk-ws-client source code

This commit is contained in:
wxj
2026-06-30 16:33:29 +00:00
parent 0d57633d23
commit 5e57d06b1f
21 changed files with 6070 additions and 0 deletions
+289
View File
@@ -0,0 +1,289 @@
/**
* bench_tcp.c — TCP stack throughput benchmark (standalone, no DPDK)
*
* Tests the critical data path: ring buffer + fragment alloc + memcpy
* This is the CPU-bound portion of the TCP stack.
*
* Compile: gcc -O2 -o bench_tcp bench_tcp.c -lpthread
* Run: ./bench_tcp
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#include <sys/time.h>
/* ── Simulated ring buffer (same logic as DPDK rte_ring) ── */
#define RING_SIZE 1024
#define RING_MASK (RING_SIZE - 1)
typedef struct {
void *buf[RING_SIZE];
uint32_t head __attribute__((aligned(64)));
uint32_t tail __attribute__((aligned(64)));
} ring_t;
static inline int ring_enqueue(ring_t *r, void *obj) {
uint32_t next = (r->head + 1) & RING_MASK;
if (next == r->tail) return -1;
r->buf[r->head] = obj;
__sync_synchronize();
r->head = next;
return 0;
}
static inline int ring_dequeue(ring_t *r, void **obj) {
if (r->head == r->tail) return -1;
__sync_synchronize();
*obj = r->buf[r->tail];
r->tail = (r->tail + 1) & RING_MASK;
return 0;
}
/* ── TCP fragment (same as production code) ── */
typedef struct {
uint16_t sport, dport;
uint32_t seqnum, acknum;
uint8_t hdrlen_off, tcp_flags;
uint16_t windows, cksum, tcp_urp;
int optlen;
uint32_t option[10];
unsigned char *data;
int length;
} tcp_fragment_t;
/* ── Simple hash table (256 buckets, linked list) ── */
#define HT_SIZE 256
#define HT_MASK 255
typedef struct ht_entry {
uint32_t key;
int val;
struct ht_entry *next;
} ht_entry_t;
static ht_entry_t *htable[HT_SIZE];
static inline uint32_t ht_hash(uint32_t k) { return (k ^ (k >> 16)) & HT_MASK; }
static void ht_insert(uint32_t key, int val) {
uint32_t h = ht_hash(key);
ht_entry_t *e = malloc(sizeof(*e));
e->key = key; e->val = val;
e->next = htable[h];
htable[h] = e;
}
static int ht_lookup(uint32_t key) {
uint32_t h = ht_hash(key);
ht_entry_t *e;
for (e = htable[h]; e; e = e->next)
if (e->key == key) return e->val;
return -1;
}
/* ── Timer helpers ── */
static double now_us(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000.0 + tv.tv_usec;
}
/* ═══════════════════════════════════════════════════════════════ */
static void bench_ring_sp_sc(void) {
printf("\n=== Ring Buffer SP/SC Throughput ===\n");
ring_t *rx = calloc(1, sizeof(ring_t));
ring_t *tx = calloc(1, sizeof(ring_t));
/* Pre-allocate fragments */
#define N_FRAGS 512
tcp_fragment_t *frags[N_FRAGS];
for (int i = 0; i < N_FRAGS; i++) {
frags[i] = calloc(1, sizeof(tcp_fragment_t));
frags[i]->data = malloc(1460);
frags[i]->length = 1460;
memset(frags[i]->data, 'A', 1460);
}
int iterations = 1000000;
double start = now_us();
/* Producer-Consumer: enqueue N, dequeue N, repeat */
int produced = 0, consumed = 0;
int fi = 0;
for (int i = 0; i < iterations; i++) {
if (ring_enqueue(rx, frags[fi % N_FRAGS]) == 0) {
produced++;
fi++;
}
void *obj = NULL;
if (ring_dequeue(rx, &obj) == 0) {
consumed++;
/* Simulate processing: checksum + copy */
tcp_fragment_t *f = (tcp_fragment_t *)obj;
volatile uint32_t cksum = 0;
for (int j = 0; j < f->length; j += 4)
cksum += *(uint32_t*)(f->data + j);
(void)cksum;
ring_enqueue(tx, f);
}
void *tx_obj = NULL;
if (ring_dequeue(tx, &tx_obj) == 0) {
/* TX done */
}
}
double end = now_us();
double sec = (end - start) / 1000000.0;
uint64_t total_bytes = (uint64_t)produced * 1460;
printf(" Produced: %d fragments\n", produced);
printf(" Consumed: %d fragments\n", consumed);
printf(" Total data: %.1f MB\n", total_bytes / 1e6);
printf(" Time: %.2f ms\n", sec * 1000.0);
printf(" Throughput: %.2f Gbps\n", (total_bytes * 8.0) / (sec * 1e9));
printf(" PPS: %.0f Kpps\n", produced / (sec * 1000.0));
printf(" Latency: %.0f ns/frag\n", sec * 1e9 / produced);
for (int i = 0; i < N_FRAGS; i++) {
free(frags[i]->data);
free(frags[i]);
}
free(rx); free(tx);
}
static void bench_hash(void) {
printf("\n=== Hash Table Lookup (256 buckets, 1000 entries) ===\n");
/* Populate */
for (int i = 0; i < 1000; i++)
ht_insert((uint32_t)i * 2654435761U, i);
int iterations = 10000000;
int found = 0;
double start = now_us();
for (int i = 0; i < iterations; i++) {
uint32_t key = (uint32_t)(i % 1000) * 2654435761U;
if (ht_lookup(key) >= 0) found++;
}
double end = now_us();
double ns_per = (end - start) * 1000.0 / iterations;
printf(" Entries: 1000\n");
printf(" Lookups: %d (all found)\n", found);
printf(" Time: %.1f ns/lookup\n", ns_per);
printf(" Rate: %.0f Mlookups/sec\n", 1000.0 / ns_per);
/* Cleanup */
for (int i = 0; i < HT_SIZE; i++) {
ht_entry_t *e = htable[i];
while (e) { ht_entry_t *n = e->next; free(e); e = n; }
htable[i] = NULL;
}
}
static void bench_ws_frame(void) {
printf("\n=== WebSocket Frame Encode/Decode ===\n");
uint8_t payload[1460];
uint8_t frame[2048];
uint8_t mask[4] = {0x12, 0x34, 0x56, 0x78};
memset(payload, 'X', 1460);
int iterations = 500000;
double start = now_us();
for (int i = 0; i < iterations; i++) {
/* Encode (client→server, MASKED) */
int pos = 0;
frame[pos++] = 0x80 | 0x02; /* FIN=1, BINARY */
frame[pos++] = 0x80 | 126; /* MASK=1, 16-bit length */
frame[pos++] = (1460 >> 8) & 0xFF;
frame[pos++] = 1460 & 0xFF;
memcpy(frame + pos, mask, 4); pos += 4;
for (int j = 0; j < 1460; j++)
frame[pos + j] = payload[j] ^ mask[j % 4];
pos += 1460;
/* Decode (server→client, verify) */
uint8_t op = frame[0] & 0x0F;
int m = (frame[1] >> 7) & 1;
int plen = frame[1] & 0x7F;
if (plen == 126) plen = (frame[2] << 8) | frame[3];
uint8_t *mk = &frame[plen == 126 ? 4 : 2];
int data_off = plen == 126 ? 8 : 6;
/* Server frames shouldn't be masked, but we check anyway */
if (m) for (int j = 0; j < plen; j++)
frame[data_off + j] ^= mk[j % 4];
volatile int check = op + plen;
(void)check;
}
double end = now_us();
double sec = (end - start) / 1e6;
uint64_t total = (uint64_t)iterations * 1460 * 2; /* encode + decode */
printf(" Iterations: %d\n", iterations);
printf(" Total data: %.1f MB\n", total / 1e6);
printf(" Time: %.2f ms\n", sec * 1000);
printf(" Throughput: %.2f Gbps\n", (total * 8.0) / (sec * 1e9));
}
static void bench_memcpy(void) {
printf("\n=== memcpy Throughput (baseline) ===\n");
int sizes[] = {64, 256, 1460, 4096, 65536};
for (int s = 0; s < 5; s++) {
int sz = sizes[s];
char *src = malloc(sz);
char *dst = malloc(sz);
memset(src, 'A', sz);
int iterations = (sz < 1024) ? 10000000 : (sz < 10000 ? 1000000 : 100000);
double start = now_us();
for (int i = 0; i < iterations; i++)
memcpy(dst, src, sz);
double end = now_us();
double ns_per = (end - start) * 1000.0 / iterations;
double gbps = (sz * 8.0) / (ns_per);
printf(" %5d bytes: %6.1f ns/copy → %.1f Gbps\n", sz, ns_per, gbps);
free(src); free(dst);
}
}
/* ═══════════════════════════════════════════════════════════════ */
int main(void) {
printf("╔══════════════════════════════════════════════╗\n");
printf("║ DPDK TCP Stack — Data Path Benchmark ║\n");
printf("║ (CPU-only, no NIC hardware needed) ║\n");
printf("╚══════════════════════════════════════════════╝\n");
bench_memcpy();
bench_hash();
bench_ring_sp_sc();
bench_ws_frame();
printf("\n╔══════════════════════════════════════════════╗\n");
printf("║ Expected DPDK Wire-Speed (with NIC HW): ║\n");
printf("║ ║\n");
printf("║ 10 Gbps line rate: ~9.5 Gbps TCP goodput║\n");
printf("║ 25 Gbps line rate: ~23.8 Gbps TCP ║\n");
printf("║ Single-core PPS: 5-8 Mpps (64B) ║\n");
printf("║ RTT (local): 5-15 us ║\n");
printf("╚══════════════════════════════════════════════╝\n");
return 0;
}
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
import socket, time, random, json, base64
latencies = []
for run in range(10):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
t0 = time.time()
s.connect(('192.168.123.132', 8080))
t_conn = time.time() - t0
key = base64.b64encode(bytes([random.randint(0,255) for _ in range(16)])).decode()
req = (f'GET /ws HTTP/1.1\r\n'
f'Host: 192.168.123.132:8080\r\n'
f'Upgrade: websocket\r\n'
f'Connection: Upgrade\r\n'
f'Sec-WebSocket-Key: {key}\r\n'
f'Sec-WebSocket-Version: 13\r\n'
f'\r\n')
s.sendall(req.encode())
s.recv(4096)
t_hs = time.time() - t0
msg = json.dumps({'action':'ping'})
frame = bytearray([0x81, 0x80 | len(msg)])
mk = bytes([random.randint(0,255) for _ in range(4)])
frame.extend(mk)
for i,b in enumerate(msg.encode()):
frame.append(b ^ mk[i%4])
t1 = time.time()
s.sendall(bytes(frame))
s.recv(4096)
t_echo = time.time() - t1
cl = bytearray([0x88, 0x80, 0x00, 0x00, 0x00, 0x00])
s.sendall(cl)
s.close()
latencies.append((t_conn*1000, t_hs*1000, t_echo*1000))
print(f' run{run+1}: connect={t_conn*1000:.2f}ms hs={t_hs*1000:.2f}ms echo={t_echo*1000:.2f}ms')
time.sleep(0.15)
a_c = sum(x[0] for x in latencies)/10
a_h = sum(x[1] for x in latencies)/10
a_e = sum(x[2] for x in latencies)/10
min_c = min(x[0] for x in latencies)
min_h = min(x[1] for x in latencies)
min_e = min(x[2] for x in latencies)
max_c = max(x[0] for x in latencies)
max_h = max(x[1] for x in latencies)
max_e = max(x[2] for x in latencies)
print(f'\nKernel TCP + C Server (10 runs):')
print(f' connect: min={min_c:.2f}ms avg={a_c:.2f}ms max={max_c:.2f}ms')
print(f' handshake: min={min_h:.2f}ms avg={a_h:.2f}ms max={max_h:.2f}ms')
print(f' echo RTT: min={min_e:.2f}ms avg={a_e:.2f}ms max={max_e:.2f}ms')
+322
View File
@@ -0,0 +1,322 @@
/**
* test_ws_protocol.c — WebSocket 协议层单元测试
*
* 使用标准 Linux socket 替代 DPDK 数据面,验证:
* 1. WebSocket 握手 (HTTP Upgrade → 101)
* 2. WS 帧编码/解码 (masked send / unmasked recv)
* 3. 订阅/推送 消息交互
*
* 编译: gcc -o test_ws_protocol test_ws_protocol.c -lpthread
* 运行: ./test_ws_protocol [server_ip] [port]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <time.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
/* ═══════════════════════════════════════════════════════
* WebSocket 常量 (RFC 6455)
* ═══════════════════════════════════════════════════════ */
#define WS_OPCODE_TEXT 0x1
#define WS_OPCODE_BINARY 0x2
#define WS_OPCODE_CLOSE 0x8
#define WS_OPCODE_PING 0x9
#define WS_OPCODE_PONG 0xA
#define WS_MASK_KEY_SIZE 4
#define WS_MAX_FRAME_SIZE (64 * 1024)
/* ── Base64 ─────────────────────────────────────────── */
static const char b64_table[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static int base64_encode(const unsigned char *src, int len, char *dst) {
int i, j = 0;
for (i = 0; i < len; i += 3) {
unsigned int val = (unsigned char)src[i] << 16;
unsigned int val2 = (i + 1 < len) ? ((unsigned char)src[i + 1] << 8) : 0;
unsigned int val3 = (i + 2 < len) ? (unsigned char)src[i + 2] : 0;
val |= val2 | val3;
dst[j++] = b64_table[(val >> 18) & 0x3F];
dst[j++] = b64_table[(val >> 12) & 0x3F];
dst[j++] = (i + 1 < len) ? b64_table[(val >> 6) & 0x3F] : '=';
dst[j++] = (i + 2 < len) ? b64_table[val & 0x3F] : '=';
}
dst[j] = '\0';
return j;
}
/* ── WebSocket 握手 ─────────────────────────────────── */
static int ws_handshake(int fd, const char *host, int port, const char *path) {
/* Generate random key */
uint8_t key_bytes[16];
char ws_key[32];
srand(time(NULL));
for (int i = 0; i < 16; i++) key_bytes[i] = rand() % 256;
base64_encode(key_bytes, 16, ws_key);
/* Build HTTP Upgrade request */
char req[2048];
int len = snprintf(req, sizeof(req),
"GET %s HTTP/1.1\r\n"
"Host: %s:%d\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: %s\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n",
path, host, port, ws_key);
printf("[WS] Sending handshake:\n%s", req);
if (send(fd, req, len, 0) != len) {
printf("[WS] ERROR: send handshake failed\n");
return -1;
}
/* Receive response */
char resp[4096];
memset(resp, 0, sizeof(resp));
int total = 0;
while (total < (int)sizeof(resp) - 1) {
int n = recv(fd, resp + total, sizeof(resp) - 1 - total, 0);
if (n <= 0) { printf("[WS] recv failed: %d\n", n); return -1; }
total += n;
resp[total] = '\0';
if (strstr(resp, "\r\n\r\n")) break;
}
printf("[WS] Response:\n%s\n", resp);
if (!strstr(resp, "101")) {
printf("[WS] ERROR: not 101\n");
return -1;
}
printf("[WS] Handshake OK!\n");
return 0;
}
/* ── WebSocket 帧编码 (client → server, MASKED) ─────── */
static int ws_send_frame(int fd, int opcode, const uint8_t *payload, int len) {
uint8_t frame[WS_MAX_FRAME_SIZE];
int pos = 0;
frame[pos++] = 0x80 | (opcode & 0x0F); /* FIN=1 */
uint8_t mask[4];
for (int i = 0; i < 4; i++) mask[i] = rand() % 256;
if (len < 126) {
frame[pos++] = 0x80 | (uint8_t)len;
} else if (len < 65536) {
frame[pos++] = 0x80 | 126;
frame[pos++] = (len >> 8) & 0xFF;
frame[pos++] = len & 0xFF;
} else {
frame[pos++] = 0x80 | 127;
for (int i = 7; i >= 0; i--)
frame[pos++] = (len >> (i * 8)) & 0xFF;
}
memcpy(&frame[pos], mask, 4);
pos += 4;
for (int i = 0; i < len; i++)
frame[pos + i] = payload[i] ^ mask[i % 4];
pos += len;
int sent = send(fd, frame, pos, 0);
return sent == pos ? sent : -1;
}
static int ws_send_text(int fd, const char *text) {
return ws_send_frame(fd, WS_OPCODE_TEXT, (const uint8_t *)text, strlen(text));
}
/* ── WebSocket 帧解码 (server → client, UNMASKED) ──── */
static int ws_recv_frame(int fd, int *opcode, uint8_t *buf, int *buflen) {
uint8_t header[10];
int total = 0;
while (total < 2) {
int n = recv(fd, header + total, 2 - total, 0);
if (n <= 0) return n;
total += n;
}
uint8_t byte0 = header[0], byte1 = header[1];
*opcode = byte0 & 0x0F;
int masked = (byte1 >> 7) & 0x01;
uint64_t payload_len = byte1 & 0x7F;
if (payload_len == 126) {
while (total < 4) { int n = recv(fd, header + total, 4 - total, 0); if (n <= 0) return n; total += n; }
payload_len = ((uint64_t)header[2] << 8) | header[3];
} else if (payload_len == 127) {
while (total < 10) { int n = recv(fd, header + total, 10 - total, 0); if (n <= 0) return n; total += n; }
payload_len = 0;
for (int i = 2; i < 10; i++) payload_len = (payload_len << 8) | header[i];
}
uint8_t mask_key[4] = {0};
if (masked) {
uint8_t mk[4]; int mk_total = 0;
while (mk_total < 4) { int n = recv(fd, mk + mk_total, 4 - mk_total, 0); if (n <= 0) return n; mk_total += n; }
memcpy(mask_key, mk, 4);
}
if (*opcode == WS_OPCODE_CLOSE) { printf("[WS] CLOSE frame\n"); return 0; }
if (*opcode == WS_OPCODE_PING) {
uint8_t pd[128]; int pl = payload_len < 128 ? (int)payload_len : 128;
int rt = 0; while (rt < pl) { int n = recv(fd, pd + rt, pl - rt, 0); if (n <= 0) return n; rt += n; }
if (masked) for (int i = 0; i < pl; i++) pd[i] ^= mask_key[i % 4];
ws_send_frame(fd, WS_OPCODE_PONG, pd, pl);
printf("[WS] PING → PONG\n");
*buflen = 0; return 0;
}
if (*opcode == WS_OPCODE_PONG) {
uint8_t pd[128]; int pl = payload_len < 128 ? (int)payload_len : 128;
int rt = 0; while (rt < pl) { int n = recv(fd, pd + rt, pl - rt, 0); if (n <= 0) return n; rt += n; }
printf("[WS] PONG\n");
*buflen = 0; return 0;
}
if (payload_len > (uint64_t)(*buflen)) {
printf("[WS] payload too large: %lu > %d\n", payload_len, *buflen);
return -1;
}
int rt = 0;
while (rt < (int)payload_len) {
int n = recv(fd, buf + rt, (int)payload_len - rt, 0);
if (n <= 0) return n;
rt += n;
}
if (masked) for (int i = 0; i < (int)payload_len; i++) buf[i] ^= mask_key[i % 4];
*buflen = (int)payload_len;
return *buflen;
}
/* ═══════════════════════════════════════════════════════
* Main test
* ═══════════════════════════════════════════════════════ */
static int test_case = 1;
static void test_send_recv(int fd) {
printf("\n=== Test %d: echo ===\n", test_case++);
ws_send_text(fd, "hello dpdk websocket!");
uint8_t buf[4096]; int opcode, len = sizeof(buf);
int n = ws_recv_frame(fd, &opcode, buf, &len);
if (n > 0) {
buf[len] = '\0';
printf("[OK] echo reply: %s\n", buf);
} else {
printf("[FAIL] no response\n");
}
}
static void test_subscribe(int fd) {
printf("\n=== Test %d: subscribe ===\n", test_case++);
ws_send_text(fd, "{\"action\":\"subscribe\",\"topic\":\"dpdk_test\"}");
/* First response: subscription confirmation */
uint8_t buf[4096]; int opcode, len = sizeof(buf);
int n = ws_recv_frame(fd, &opcode, buf, &len);
if (n > 0) {
buf[len] = '\0';
printf("[OK] subscribe response: %s\n", buf);
}
/* Second response: push data with welcome message */
len = sizeof(buf);
n = ws_recv_frame(fd, &opcode, buf, &len);
if (n > 0) {
buf[len] = '\0';
printf("[OK] push data: %s\n", buf);
}
}
static void test_periodic_push(int fd, int wait_sec) {
printf("\n=== Test %d: periodic push (wait %ds) ===\n", test_case++, wait_sec);
sleep(wait_sec);
/* Check for push data (non-blocking poll) */
/* For simplicity, just wait and try one recv with timeout */
struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
uint8_t buf[4096]; int opcode, len = sizeof(buf);
int n = ws_recv_frame(fd, &opcode, buf, &len);
if (n > 0) {
buf[len] = '\0';
printf("[OK] periodic push: %s\n", buf);
} else {
printf("[INFO] no periodic data received yet\n");
}
}
int main(int argc, char *argv[]) {
const char *server_ip = argc > 1 ? argv[1] : "192.168.159.129";
int port = argc > 2 ? atoi(argv[2]) : 8080;
printf("╔══════════════════════════════════════════════╗\n");
printf("║ WebSocket Protocol Unit Test ║\n");
printf("╠══════════════════════════════════════════════╣\n");
printf("║ Server: %s:%d \n", server_ip, port);
printf("╚══════════════════════════════════════════════╝\n\n");
/* 1. TCP connect */
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) { perror("socket"); return 1; }
/* Set TCP_NODELAY for lower latency */
int opt = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(port),
};
inet_pton(AF_INET, server_ip, &addr.sin_addr);
printf("[TCP] Connecting to %s:%d...\n", server_ip, port);
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("connect");
close(fd);
return 1;
}
printf("[TCP] Connected!\n");
/* 2. WebSocket handshake */
if (ws_handshake(fd, server_ip, port, "/ws") < 0) {
printf("[FAIL] WebSocket handshake failed\n");
close(fd);
return 1;
}
/* 3. Run tests */
test_send_recv(fd);
test_subscribe(fd);
test_periodic_push(fd, 5);
printf("\n╔══════════════════════════════════════════════╗\n");
printf("║ All tests passed! ║\n");
printf("╚══════════════════════════════════════════════╝\n");
close(fd);
return 0;
}
+69
View File
@@ -0,0 +1,69 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/uio.h>
#include <netinet/tcp.h>
static int r_all(int fd, void *buf, int n) {
int t = 0; unsigned char *p = buf;
while (t < n) { int r = read(fd, p + t, n - t); if (r <= 0) return -1; t += r; }
return 0;
}
int main(int argc, char *argv[]) {
int port = argc > 1 ? atoi(argv[1]) : 8080;
setbuf(stderr, NULL);
int srv = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1; setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in a = {.sin_family = AF_INET, .sin_port = htons(port)};
bind(srv, (struct sockaddr *)&a, sizeof(a)); listen(srv, 1);
fprintf(stderr, "C WS Fast Server on :%d\n", port);
while (1) {
socklen_t sl = sizeof(a); int fd = accept(srv, (struct sockaddr *)&a, &sl);
int one = 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
fprintf(stderr, "[+] %s:%d\n", inet_ntoa(a.sin_addr), ntohs(a.sin_port));
char buf[4096]; int t = 0;
while (t < 4095 && !strstr(buf, "\r\n\r\n")) {
int r = read(fd, buf + t, 4095 - t); if (r <= 0) goto close; t += r; buf[t] = 0;
}
static const char *resp =
"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\n"
"Connection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n";
write(fd, resp, strlen(resp));
unsigned char frame[65536]; int count = 0;
while (1) {
unsigned char hdr[2];
if (r_all(fd, hdr, 2) < 0) break;
int op = hdr[0] & 0x0F, masked = (hdr[1] >> 7) & 1;
int plen = hdr[1] & 0x7F;
if (plen == 126) { unsigned char e[2]; r_all(fd, e, 2); plen = (e[0]<<8)|e[1]; }
else if (plen == 127) { unsigned char e[8]; r_all(fd, e, 8); plen=0; for(int i=0;i<8;i++)plen=(plen<<8)|e[i]; }
unsigned char mk[4] = {0};
if (masked) r_all(fd, mk, 4);
if (plen > 0) r_all(fd, frame, plen);
if (masked) for (int i = 0; i < plen; i++) frame[i] ^= mk[i % 4];
if (op == 0x08) { unsigned char cl[] = {0x88, 0x00}; write(fd, cl, 2); break; }
if (op == 0x09) { unsigned char po[6] = {0x8A, 0x80}; write(fd, po, 2); continue; }
if (plen > 0) {
unsigned char h[10]; int hl = 2; h[0] = 0x80 | op;
if (plen < 126) h[1] = plen;
else { h[1] = 126; h[2] = (plen>>8)&0xFF; h[3] = plen&0xFF; hl = 4; }
struct iovec iov[2] = {{h, hl}, {frame, plen}};
writev(fd, iov, 2); /* single syscall = single TCP segment */
count++;
}
}
fprintf(stderr, "[-] %d msgs echoed\n", count);
close: close(fd);
}
return 0;
}
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""Simple WebSocket echo server for testing DPDK WebSocket client."""
import asyncio
import websockets
import json
import sys
import time
connected_clients = set()
async def handler(websocket):
client_addr = websocket.remote_address
path = websocket.request.path if hasattr(websocket, 'request') else "/"
print(f"[CONNECT] {client_addr}, path: {path}")
connected_clients.add(websocket)
try:
async for message in websocket:
print(f"[RECV] {message}")
try:
data = json.loads(message)
action = data.get("action", "")
topic = data.get("topic", "")
if action == "subscribe":
resp = json.dumps({"status":"ok","action":"subscribed","topic":topic,"timestamp":int(time.time())})
await websocket.send(resp)
print(f"[SEND] {resp}")
push = json.dumps({"type":"push","topic":topic,"data":f"Welcome to {topic}!","timestamp":int(time.time())})
await websocket.send(push)
print(f"[SEND] {push}")
continue
except (json.JSONDecodeError, TypeError):
pass
response = f"ECHO: {message}"
await websocket.send(response)
print(f"[SEND] {response}")
except websockets.exceptions.ConnectionClosed:
print(f"[DISCONNECT] {client_addr}")
finally:
connected_clients.discard(websocket)
async def push_loop(interval=10):
count = 0
while True:
await asyncio.sleep(interval)
if connected_clients:
count += 1
msg = json.dumps({"type":"push","data":f"Periodic update #{count}","timestamp":int(time.time())})
websockets.broadcast(connected_clients, msg)
print(f"[PUSH] {msg}")
async def main(port=8080):
print(f"WebSocket Test Server on ws://0.0.0.0:{port}")
async with websockets.serve(handler, "0.0.0.0", port):
asyncio.create_task(push_loop(10))
print(f"[READY] Waiting for DPDK client...")
await asyncio.Future()
if __name__ == "__main__":
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8080
asyncio.run(main(port))