use bufio; use io; use log; use net; use net::ip; use net::tcp; use strings; use time; use unix::poll; // our message to send to clients as a constant def tick: [5]u8 = ['t', 'i', 'c', 'k', '\n']; // tryconn attempts to add a client to a pollfd slice. fn tryconn(fds: []poll::pollfd) void = { let socket = fds[0]; if (socket.revents & poll::event::POLLIN > 0) { // try to accept the connection. let conn = match(net::accept(socket.fd)) { case let err: net::error => log::println("failed to accept connection"); return; case let sock: net::socket => yield sock; }; // the events we want to listen for let events = (poll::event::POLLIN | poll::event::POLLPRI); for (let i = 1z; i < len(fds); i += 1) { // empty slot is indicated by a // file descriptor equal to 0 if (fds[i].fd == 0) { fds[i].fd = conn; fds[i].events = events; return; }; }; // close the connection if there is no room io::close(conn)!; }; }; // tryread will attempt to read from all connected clients. fn tryread(fds: []poll::pollfd) void = { for (let i = 1z; i < len(fds); i += 1) { let revents = fds[i].revents; // just close the file descriptor // on error or hangup events if (revents & poll::event::POLLERR & poll::event::POLLHUP > 0) { closefd(&fds[i]); continue; }; // if there is data to read... if (revents & poll::event::POLLIN > 0) { let scanner = bufio::newscanner(fds[i].fd, 1024z); defer bufio::finish(&scanner); match(bufio::scan_line(&scanner)) { case let msg: const str => log::printfln("client said: {}", msg); case => // io::EOF, io::error, or utf8::invalid closefd(&fds[i]); }; }; }; }; // write sends a 'tick' message to all connected clients. fn write(fds: []poll::pollfd) void = { for (let i = 1z; i < len(fds); i += 1) { if (fds[i].fd > 0) { io::writeall(fds[i].fd, tick)!; }; }; }; // closefd closes a provided file descriptor // and resets all fields to 0. fn closefd(fd: *poll::pollfd) void = { match(io::close(fd.fd)) { case void => void; case io::error => log::println("failed to close socket"); }; fd.fd = 0; fd.events = 0; fd.revents = 0; }; // shouldtick returns a boolean representing if a tick // message should be sent and a time::duration indicating // when the next tick should be. fn shouldtick(next: time::instant) (bool, time::duration) = { let now = time::now(time::clock::MONOTONIC); if (time::compare(now, next) >= 0) { return (true, time::SECOND); }; let diff = time::diff(now, next); return (false, diff); }; export fn main() void = { let socket = tcp::listen(ip::LOCAL_V4, 8080)!; // initialize all pollfds with zero values let fds: [15]poll::pollfd = [ poll::pollfd { fd = 0, events = 0, revents = 0, } ... ]; // overwrite the first pollfd with our socket fds[0] = poll::pollfd { fd = socket, events = (poll::event::POLLIN | poll::event::POLLPRI), revents = 0 }; let start = time::now(time::clock::MONOTONIC); let nexttick = time::add(start, time::SECOND); let wait = time::SECOND; for (true) { match(poll::poll(fds, wait)) { case let n: uint => if (n > 0) { tryconn(fds); tryread(fds); }; let (shouldtick, d) = shouldtick(nexttick); if (shouldtick) { let now = time::now(time::clock::MONOTONIC); nexttick = time::add(now, d); write(fds); }; wait = d; case let err: poll::error => log::fatal("poll failed"); }; }; };