Skip to content

Instantly share code, notes, and snippets.

@eddiebergman
Created September 8, 2026 11:28
Show Gist options
  • Select an option

  • Save eddiebergman/adb68c85e05ae34dd70a9cd3323e9ec7 to your computer and use it in GitHub Desktop.

Select an option

Save eddiebergman/adb68c85e05ae34dd70a9cd3323e9ec7 to your computer and use it in GitHub Desktop.
Brief learning example of how to use macos libc calls to handle a network socket
---cargo
[dependencies]
libc = "0.2.*"
---
// Minimal but functional example of socket read/write loop on macos for ipv4, ~100 LoC
// Run standalone with: cargo +nightly -Zscript socket-listen.rs, delete frontmatter otherwise
// Can test with `nc 127.0.0.1 9999` for interactive, `printf "hello\n" | nc ...` one shot
// Hand-written, references from rust-lang/socket2, Gemini and various linux man pages
// Notably not handled, async, threading, any error recovery
use std::cmp::Ordering;
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
macro_rules! syscall { // rust-lang/socket2
($fn: ident ( $($arg: expr),* $(,)* ) ) => {{
#[allow(unused_unsafe)]
let res = unsafe { libc::$fn($($arg, )*) };
if res == -1 { Err(std::io::Error::last_os_error()) } else { Ok(res) }
}};
}
fn main() -> Result<(), std::io::Error> {
// https://stackoverflow.com/a/60425748
let socket = syscall!(socket(libc::AF_INET, libc::SOCK_STREAM, 0))?;
let fd = unsafe { OwnedFd::from_raw_fd(socket) }; // Will auto-close
let raw_fd = fd.as_raw_fd();
// macos: set CLOEXEC flag on the file descriptor (on linux, can set on creation)
let prev = syscall!(fcntl(raw_fd, libc::F_GETFD))?;
syscall!(fcntl(raw_fd, libc::F_SETFD, prev | libc::FD_CLOEXEC))?;
// https://man7.org/linux/man-pages/man7/socket.7.html
let set_opt = |opt: libc::c_int| -> std::io::Result<libc::c_int> {
let enable: libc::c_int = 1;
syscall!(setsockopt(
raw_fd,
libc::SOL_SOCKET,
opt,
(&raw const enable).cast(), // ptr to c_int as c_void
std::mem::size_of::<libc::c_int>() as _,
))
};
set_opt(libc::SO_NOSIGPIPE)?; // Prevent dead pipe causing SIGPIPE process interupt
set_opt(libc::SO_REUSEADDR)?; // Allow immediate re-bind on the port on server restart
// Very explicitly not making flexible, only ipv4
// Special note on ensuring big-endian (network-endian) with `to_be()`
let address: std::net::SocketAddrV4 = "0.0.0.0:9999".parse().expect("parsable address");
let sin = libc::sockaddr_in {
sin_len: std::mem::size_of::<libc::sockaddr_in>() as u8,
sin_family: libc::AF_INET as _,
sin_port: address.port().to_be() as _,
sin_addr: libc::in_addr {
s_addr: address.ip().to_bits().to_be(),
},
sin_zero: [0; 8],
};
// Bind and listen so it's ready to accept incoming connections
syscall!(bind(
raw_fd,
(&raw const sin).cast(),
std::mem::size_of::<libc::sockaddr_in>() as _,
))?;
syscall!(listen(raw_fd, 128))?;
// Loop accepting from our listening socket
'accept: loop {
let mut client_addr: libc::sockaddr_storage = unsafe { std::mem::zeroed() };
let mut client_addr_len = std::mem::size_of::<libc::sockaddr_storage>();
let client_raw = syscall!(accept(
fd.as_raw_fd(),
(&raw mut client_addr).cast(),
(&raw mut client_addr_len).cast(),
))?;
let _client_fd = unsafe { OwnedFd::from_raw_fd(client_raw) }; // auto-close on Drop
'session: loop {
let mut req: Vec<u8> = Vec::new();
let mut buffer = [0u8; 1024];
'read: loop {
let bytes_read =
unsafe { libc::read(client_raw, buffer.as_mut_ptr().cast(), buffer.len()) };
match bytes_read.cmp(&0) {
Ordering::Less => {
let err = std::io::Error::last_os_error();
eprintln!("Error reading: {err:?}");
break 'session;
}
Ordering::Equal => break 'session, // Disconnect
Ordering::Greater => match buffer.iter().position(|&b| b == b'\n') {
Some(i) => {
req.extend_from_slice(&buffer[..i]);
break 'read;
}
None => req.extend_from_slice(&buffer[..bytes_read as usize]),
},
}
}
let s = std::str::from_utf8(req.as_slice()).expect("valid utf8");
println!("Got {:?}", s);
match s {
"done" => break 'session,
"shutdown" => break 'accept,
_ => {
let mut buf = (if s == "hi" { "hello\n" } else { "type hi\n" }).as_bytes();
while !buf.is_empty() {
let bytes_written =
unsafe { libc::write(client_raw, buf.as_ptr().cast(), buf.len()) };
if bytes_written > 0 {
buf = &buf[bytes_written as usize..];
} else {
let err = std::io::Error::last_os_error();
eprintln!("Error writing: {err:?}");
break 'session;
}
}
}
}
}
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment