feat: add Std::Net — TCP sockets module
- C runtime: socket(), bind(), listen(), accept(), connect(), send(), recv(), close() - library/std/Net.bux: Net_Create, Net_Bind, Net_Listen, Net_Accept, Net_Connect, Net_Send, Net_Recv, Net_Close, Net_SetReuse, Net_LastError - examples/tcp_server.bux: echo server on 127.0.0.1:9000 - examples/tcp_client.bux: client that sends 'Hello from Bux!' and prints reply
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
[Package]
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import Std::Net;
|
||||||
|
func Main() -> int {
|
||||||
|
let fd: int = Net::Create();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// TCP Client — connects to 127.0.0.1:9000, sends a message, prints the reply
|
||||||
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
|
import Std::Net::{Net_Create, Net_Connect, Net_Send, Net_Recv, Net_Close};
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let fd: int = Net_Create();
|
||||||
|
if fd < 0 {
|
||||||
|
PrintLine("Failed to create socket");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if Net_Connect(fd, "127.0.0.1", 9000) == false {
|
||||||
|
PrintLine("Failed to connect");
|
||||||
|
Net_Close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintLine("Connected to 127.0.0.1:9000");
|
||||||
|
|
||||||
|
let msg: String = "Hello from Bux!";
|
||||||
|
let sent: int = Net_Send(fd, msg);
|
||||||
|
if sent < 0 {
|
||||||
|
PrintLine("Send failed");
|
||||||
|
Net_Close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
PrintLine("Message sent");
|
||||||
|
|
||||||
|
let reply: String = Net_Recv(fd, 1024);
|
||||||
|
PrintLine("Server reply:");
|
||||||
|
PrintLine(reply);
|
||||||
|
|
||||||
|
Net_Close(fd);
|
||||||
|
PrintLine("Disconnected");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// TCP Echo Server — binds to 127.0.0.1:9000 and echoes back received data
|
||||||
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
|
import Std::Net::{Net_Create, Net_SetReuse, Net_Bind, Net_Listen, Net_Accept, Net_Send, Net_Recv, Net_Close};
|
||||||
|
import Std::String::{String_Eq};
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let fd: int = Net_Create();
|
||||||
|
if fd < 0 {
|
||||||
|
PrintLine("Failed to create socket");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Net_SetReuse(fd) {
|
||||||
|
PrintLine("Warning: failed to set SO_REUSEADDR");
|
||||||
|
}
|
||||||
|
|
||||||
|
if Net_Bind(fd, "127.0.0.1", 9000) == false {
|
||||||
|
PrintLine("Failed to bind");
|
||||||
|
Net_Close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if Net_Listen(fd, 5) == false {
|
||||||
|
PrintLine("Failed to listen");
|
||||||
|
Net_Close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintLine("Echo server listening on 127.0.0.1:9000");
|
||||||
|
|
||||||
|
let client: int = Net_Accept(fd);
|
||||||
|
if client < 0 {
|
||||||
|
PrintLine("Failed to accept");
|
||||||
|
Net_Close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintLine("Client connected");
|
||||||
|
|
||||||
|
var running: bool = true;
|
||||||
|
while running {
|
||||||
|
let msg: String = Net_Recv(client, 1024);
|
||||||
|
if String_Eq(msg, "") {
|
||||||
|
running = false;
|
||||||
|
} else {
|
||||||
|
PrintLine("Received:");
|
||||||
|
PrintLine(msg);
|
||||||
|
let sent: int = Net_Send(client, msg);
|
||||||
|
if sent < 0 {
|
||||||
|
PrintLine("Send failed");
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Net_Close(client);
|
||||||
|
Net_Close(fd);
|
||||||
|
PrintLine("Server shut down");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -9,6 +9,10 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <ucontext.h>
|
#include <ucontext.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
/* Command-line argument storage */
|
/* Command-line argument storage */
|
||||||
int g_argc = 0;
|
int g_argc = 0;
|
||||||
@@ -1155,3 +1159,86 @@ char* bux_process_output(const char* cmd) {
|
|||||||
pclose(pipe);
|
pclose(pipe);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Network / Socket primitives
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
int bux_socket_create(void) {
|
||||||
|
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bux_socket_reuse(int fd) {
|
||||||
|
int opt = 1;
|
||||||
|
return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||||
|
}
|
||||||
|
|
||||||
|
int bux_socket_bind(int fd, const char* addr, int port) {
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_port = htons((uint16_t)port);
|
||||||
|
if (addr == NULL || addr[0] == '\0') {
|
||||||
|
sa.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
} else {
|
||||||
|
if (inet_pton(AF_INET, addr, &sa.sin_addr) <= 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bind(fd, (struct sockaddr*)&sa, sizeof(sa));
|
||||||
|
}
|
||||||
|
|
||||||
|
int bux_socket_listen(int fd, int backlog) {
|
||||||
|
return listen(fd, backlog);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bux_socket_accept(int fd) {
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
socklen_t len = sizeof(sa);
|
||||||
|
return accept(fd, (struct sockaddr*)&sa, &len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bux_socket_connect(int fd, const char* addr, int port) {
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_port = htons((uint16_t)port);
|
||||||
|
if (inet_pton(AF_INET, addr, &sa.sin_addr) <= 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return connect(fd, (struct sockaddr*)&sa, sizeof(sa));
|
||||||
|
}
|
||||||
|
|
||||||
|
int bux_socket_send(int fd, const char* data, int len) {
|
||||||
|
if (!data || len <= 0) return 0;
|
||||||
|
return (int)send(fd, data, (size_t)len, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
BuxString bux_socket_recv(int fd, int max_len) {
|
||||||
|
BuxString result;
|
||||||
|
if (max_len <= 0) {
|
||||||
|
result.data = "";
|
||||||
|
result.len = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
char* buf = (char*)bux_alloc((size_t)max_len + 1);
|
||||||
|
ssize_t n = recv(fd, buf, (size_t)max_len, 0);
|
||||||
|
if (n <= 0) {
|
||||||
|
result.data = "";
|
||||||
|
result.len = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
buf[n] = '\0';
|
||||||
|
result.data = buf;
|
||||||
|
result.len = (size_t)n;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bux_socket_close(int fd) {
|
||||||
|
return close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* bux_socket_error(void) {
|
||||||
|
return strerror(errno);
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
module Std::Net {
|
||||||
|
extern func bux_socket_create() -> int;
|
||||||
|
extern func bux_socket_reuse(fd: int) -> int;
|
||||||
|
extern func bux_socket_bind(fd: int, addr: String, port: int) -> int;
|
||||||
|
extern func bux_socket_listen(fd: int, backlog: int) -> int;
|
||||||
|
extern func bux_socket_accept(fd: int) -> int;
|
||||||
|
extern func bux_socket_connect(fd: int, addr: String, port: int) -> int;
|
||||||
|
extern func bux_socket_send(fd: int, data: String, len: int) -> int;
|
||||||
|
extern func bux_socket_recv(fd: int, maxLen: int) -> String;
|
||||||
|
extern func bux_socket_close(fd: int) -> int;
|
||||||
|
extern func bux_socket_error() -> String;
|
||||||
|
|
||||||
|
/* Create a TCP socket. Returns -1 on error. */
|
||||||
|
func Net_Create() -> int {
|
||||||
|
return bux_socket_create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable SO_REUSEADDR on a socket. */
|
||||||
|
func Net_SetReuse(fd: int) -> bool {
|
||||||
|
return bux_socket_reuse(fd) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bind a socket to an address and port. */
|
||||||
|
func Net_Bind(fd: int, addr: String, port: int) -> bool {
|
||||||
|
return bux_socket_bind(fd, addr, port) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start listening for connections. */
|
||||||
|
func Net_Listen(fd: int, backlog: int) -> bool {
|
||||||
|
return bux_socket_listen(fd, backlog) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accept a connection. Returns new fd or -1 on error. */
|
||||||
|
func Net_Accept(fd: int) -> int {
|
||||||
|
return bux_socket_accept(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect to a remote address and port. */
|
||||||
|
func Net_Connect(fd: int, addr: String, port: int) -> bool {
|
||||||
|
return bux_socket_connect(fd, addr, port) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send data. Returns bytes sent or -1 on error. */
|
||||||
|
func Net_Send(fd: int, data: String) -> int {
|
||||||
|
return bux_socket_send(fd, data, String_Len(data) as int);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Receive up to maxLen bytes. Returns empty string on error/EOF. */
|
||||||
|
func Net_Recv(fd: int, maxLen: int) -> String {
|
||||||
|
return bux_socket_recv(fd, maxLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close a socket. */
|
||||||
|
func Net_Close(fd: int) -> bool {
|
||||||
|
return bux_socket_close(fd) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get last socket error as a string. */
|
||||||
|
func Net_LastError() -> String {
|
||||||
|
return bux_socket_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user