If you need a high-performance TCP mock server that:
- Handles plain TCP connections (not HTTP).
- Reads incoming requests where the first 8 characters represent the length of the request.
- Efficiently handles thousands of connections with minimal overhead.
- Uses
epoll
for high TPS (Linux).
Optimized Approach
epoll
for Non-blocking I/O: Handles multiple clients efficiently.read()
loop: Ensures full request reception.send()
for fixed response: No complex logic, just echoing back.- No
fork
or threads: Single-threaded, event-driven for maximum TPS.
High-Performance Plain TCP Mock Server in C
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#define PORT 8080
#define MAX_EVENTS 1024
#define BUFFER_SIZE 8192
#define RESPONSE "MOCK_RESPONSE"
void set_nonblocking(int sock) {
int flags = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
}
int parse_length(const char *buffer) {
char len_str[9] = {0}; // 8 characters + null terminator
strncpy(len_str, buffer, 8);
return atoi(len_str);
}
int main() {
int server_fd, client_fd, epoll_fd;
struct sockaddr_in server_addr, client_addr;
struct epoll_event event, events[MAX_EVENTS];
socklen_t client_len = sizeof(client_addr);
char buffer[BUFFER_SIZE];
// Create TCP socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("Socket failed");
exit(EXIT_FAILURE);
}
// Allow immediate reuse of the port
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// Bind to port
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
// Start listening
listen(server_fd, SOMAXCONN);
set_nonblocking(server_fd);
// Setup epoll
epoll_fd = epoll_create1(0);
event.events = EPOLLIN;
event.data.fd = server_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event);
printf("Mock TCP server listening on port %d\n", PORT);
while (1) {
int num_events = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
for (int i = 0; i < num_events; i++) {
if (events[i].data.fd == server_fd) {
// Accept new connections
while ((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len)) > 0) {
set_nonblocking(client_fd);
event.events = EPOLLIN | EPOLLET;
event.data.fd = client_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
}
} else {
// Handle client request
client_fd = events[i].data.fd;
int total_read = 0, request_length = -1;
while (1) {
int bytes_read = read(client_fd, buffer + total_read, BUFFER_SIZE - total_read);
if (bytes_read <= 0) break; // No more data to read
total_read += bytes_read;
if (total_read >= 8 && request_length == -1) {
// Extract length from first 8 characters
request_length = parse_length(buffer);
}
// Ensure the full request is read
if (request_length != -1 && total_read >= request_length) {
send(client_fd, RESPONSE, sizeof(RESPONSE) - 1, 0);
close(client_fd); // Close connection after responding
break;
}
}
}
}
}
close(server_fd);
return 0;
}
How It Works
-
TCP Socket Setup:
- Creates a non-blocking server socket (
server_fd
). - Uses
SO_REUSEADDR
for fast restarts. - Uses
epoll
to efficiently handle multiple connections.
- Creates a non-blocking server socket (
-
Client Handling:
- Accepts connections and makes them non-blocking.
- Reads incoming data until full request is received.
- Extracts request length from the first 8 bytes.
- Waits until the full request is read before responding.
- Sends
"MOCK_RESPONSE"
and closes the connection.
Performance Enhancements
✅ epoll
for High TPS (scalable event loop).
✅ Non-blocking Sockets (handles thousands of clients).
✅ Minimal Memory Copying (only processes request length).
✅ No malloc()
or Dynamic Memory (avoids fragmentation).
✅ Auto-closes Connections After Response (mock behavior).
This should easily handle 100K+ TPS on a decent server. 🚀
You would consider multi-threading (SO_REUSEPORT
) or io_uring
for even higher performance