libxev
libxev copied to clipboard
Need more examples: C Server
Hi @mitchellh,
Nice hard work (for you and all your collaborators) to make this library.
I have been playing with the xev api for C code on linux today. However, some trouble was encountered when running the same code with or without xev.
Client: curl 127.0.0.1:8000.
Without xev
C code - server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 8000 // The port number for the server to listen on
int main() {
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);
// Create a socket
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("Socket creation failed");
exit(1);
}
// Bind the socket to an IP address and port
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // Listen on all available network interfaces
server_addr.sin_port = htons(PORT);
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("Socket binding failed");
close(server_socket);
exit(1);
}
// Listen for incoming connections
if (listen(server_socket, 10) == -1) {
perror("Listen failed");
close(server_socket);
exit(1);
}
printf("Server listening on port %d...\n", PORT);
while (1) {
// Accept a client connection
client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_len);
if (client_socket == -1) {
perror("Accept failed");
continue; // Continue listening for other connections
}
printf("Connection accepted from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// Handle the client connection (in this example, we simply echo back received data)
char buffer[1024];
ssize_t bytes_received;
while ((bytes_received = recv(client_socket, buffer, sizeof(buffer), 0)) > 0) {
buffer[bytes_received] = '\0'; // Null-terminate the received data
printf("Received: %s", buffer);
// Echo back to the client
send(client_socket, buffer, bytes_received, 0);
}
// Close the client socket
close(client_socket);
printf("Connection closed by %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
}
// Close the server socket (this part will not be reached in this example)
close(server_socket);
return 0;
}
Output
$> ./server_example
Server listening on port 8000...
Connection accepted from 127.0.0.1:42220
Received: GET / HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: curl/8.2.1
Accept: */*
Connection closed by 127.0.0.1:42220
With xev
C code - server
#include "xev.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 8000 // The port number for the server to listen on
void on_connection(xev_loop* loop, xev_completion* c, int result, void* userdata) {
int server_socket = *(int*)userdata;
int client_socket = accept(server_socket, NULL, NULL);
if (client_socket == -1) {
perror("Accept failed");
return;
}
char buffer[1024];
ssize_t bytes_received;
while ((bytes_received = recv(client_socket, buffer, sizeof(buffer), 0)) > 0) {
buffer[bytes_received] = '\0'; // Null-terminate the received data
printf("Received: %s", buffer);
// Echo back to the client
send(client_socket, buffer, bytes_received, 0);
}
close(client_socket);
}
int main() {
xev_loop loop;
if (xev_loop_init(&loop) != 0) {
fprintf(stderr, "Failed to initialize event loop\n");
return 1;
}
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);
// Create a socket
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("Socket creation failed");
exit(1);
}
// Bind the socket to an IP address and port
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // Listen on all available network interfaces
server_addr.sin_port = htons(PORT);
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("Socket binding failed");
close(server_socket);
exit(1);
}
// Listen for incoming connections
if (listen(server_socket, 10) == -1) {
perror("Listen failed");
close(server_socket);
exit(1);
}
printf("Server listening on port %d...\n", PORT);
// Pass the server_socket as user data to the connection handler
int user_data = server_socket;
xev_completion completion;
xev_completion_zero(&completion);
xev_threadpool_task task;
xev_threadpool_task_init(&task, (xev_task_cb)on_connection);
xev_threadpool_batch batch;
xev_threadpool_batch_init(&batch);
xev_threadpool_batch_push_task(&batch, &task);
xev_threadpool threadpool;
xev_threadpool_init(&threadpool, NULL);
xev_threadpool_schedule(&threadpool, &batch);
xev_threadpool_deinit(&threadpool);
xev_loop_run(&loop, XEV_RUN_UNTIL_DONE);
xev_loop_deinit(&loop);
close(server_socket);
return 0;
}
Output
$> ./server_example
Server listening on port 8000...
Accept failed: Bad file descriptor
hold client, needed Ctrl+C to stopping.
Note: gcc get errors on data array on header.
include/xev.h:36:44: error: variably modified ‘data’ at file scope
36 | typedef struct { XEV_ALIGN_T _pad; uint8_t data[XEV_SIZEOF_LOOP - sizeof(XEV_ALIGN_T)]; } xev_loop;
| ^~~~
include/xev.h:37:44: error: variably modified ‘data’ at file scope
37 | typedef struct { XEV_ALIGN_T _pad; uint8_t data[XEV_SIZEOF_COMPLETION - sizeof(XEV_ALIGN_T)]; } xev_completion;
| ^~~~
include/xev.h:38:44: error: variably modified ‘data’ at file scope
38 | typedef struct { XEV_ALIGN_T _pad; uint8_t data[XEV_SIZEOF_WATCHER - sizeof(XEV_ALIGN_T)]; } xev_watcher;
| ^~~~
include/xev.h:39:44: error: variably modified ‘data’ at file scope
39 | typedef struct { XEV_ALIGN_T _pad; uint8_t data[XEV_SIZEOF_THREADPOOL - sizeof(XEV_ALIGN_T)]; } xev_threadpool;
| ^~~~
include/xev.h:40:44: error: variably modified ‘data’ at file scope
40 | typedef struct { XEV_ALIGN_T _pad; uint8_t data[XEV_SIZEOF_THREADPOOL_BATCH - sizeof(XEV_ALIGN_T)]; } xev_threadpool_batch;
| ^~~~
include/xev.h:41:44: error: variably modified ‘data’ at file scope
41 | typedef struct { XEV_ALIGN_T _pad; uint8_t data[XEV_SIZEOF_THREADPOOL_TASK - sizeof(XEV_ALIGN_T)]; } xev_threadpool_task;
| ^~~~
include/xev.h:42:44: error: variably modified ‘data’ at file scope
42 | typedef struct { XEV_ALIGN_T _pad; uint8_t data[XEV_SIZEOF_THREADPOOL_CONFIG - sizeof(XEV_ALIGN_T)]; } xev_threadpool_config;
Thanks, the C API is admittedly anemic right now and not well maintained beyond extremely basic tested usage. I'm sure there are many improvements to be made here.