You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
3.4 KiB
168 lines
3.4 KiB
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");
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|