From ba9f5dfd05b17f974bfcae0a07b002a4803ee531 Mon Sep 17 00:00:00 2001 From: dimgigov Date: Fri, 5 Jun 2026 20:32:10 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20add=20Std::Net=20=E2=80=94=20TCP=20sock?= =?UTF-8?q?ets=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- _test_net/bux.toml | 1 + _test_net/src/Main.bux | 5 +++ examples/tcp_client.bux | 36 ++++++++++++++++ examples/tcp_server.bux | 60 +++++++++++++++++++++++++++ library/runtime/runtime.c | 87 +++++++++++++++++++++++++++++++++++++++ library/std/Net.bux | 62 ++++++++++++++++++++++++++++ 6 files changed, 251 insertions(+) create mode 100644 _test_net/bux.toml create mode 100644 _test_net/src/Main.bux create mode 100644 examples/tcp_client.bux create mode 100644 examples/tcp_server.bux create mode 100644 library/std/Net.bux diff --git a/_test_net/bux.toml b/_test_net/bux.toml new file mode 100644 index 0000000..0bbf836 --- /dev/null +++ b/_test_net/bux.toml @@ -0,0 +1 @@ +[Package] diff --git a/_test_net/src/Main.bux b/_test_net/src/Main.bux new file mode 100644 index 0000000..4452ee5 --- /dev/null +++ b/_test_net/src/Main.bux @@ -0,0 +1,5 @@ +import Std::Net; +func Main() -> int { + let fd: int = Net::Create(); + return 0; +} diff --git a/examples/tcp_client.bux b/examples/tcp_client.bux new file mode 100644 index 0000000..9773a09 --- /dev/null +++ b/examples/tcp_client.bux @@ -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; +} diff --git a/examples/tcp_server.bux b/examples/tcp_server.bux new file mode 100644 index 0000000..7252412 --- /dev/null +++ b/examples/tcp_server.bux @@ -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; +} diff --git a/library/runtime/runtime.c b/library/runtime/runtime.c index ffc0a00..2ce0434 100644 --- a/library/runtime/runtime.c +++ b/library/runtime/runtime.c @@ -9,6 +9,10 @@ #include #include #include +#include +#include +#include +#include /* Command-line argument storage */ int g_argc = 0; @@ -1155,3 +1159,86 @@ char* bux_process_output(const char* cmd) { pclose(pipe); 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); +} diff --git a/library/std/Net.bux b/library/std/Net.bux new file mode 100644 index 0000000..2c01965 --- /dev/null +++ b/library/std/Net.bux @@ -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(); +} +}