Alexander Avery
9 months ago
1 changed files with 143 additions and 0 deletions
@ -0,0 +1,143 @@ |
|||||
|
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; |
||||
|
}; |
||||
|
|
||||
|
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 |
||||
|
}; |
||||
|
|
||||
|
for (true) { |
||||
|
match(poll::poll(fds, time::SECOND)) { |
||||
|
|
||||
|
case let n: uint => |
||||
|
if (n > 0) { |
||||
|
tryconn(fds); |
||||
|
tryread(fds); |
||||
|
} else { |
||||
|
write(fds); |
||||
|
}; |
||||
|
|
||||
|
case let err: poll::error => |
||||
|
log::fatal("poll failed"); |
||||
|
}; |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
}; |
Loading…
Reference in new issue