diff --git a/iocp-links.html b/iocp-links.html index b046f60e..1d012046 100644 --- a/iocp-links.html +++ b/iocp-links.html @@ -147,6 +147,22 @@ Marc Lehmann has written . +
shutdown(2), graceful close, half-duplex connections
+
+Graceful + Shutdown, Linger Options, and Socket Closure +
+DisconnectEx() + +
+ +
close(2)
+
+closesocket() +
+ The following are nearly same in Windows overlapped and UNIX non-blocking sockets. The only difference is that the UNIX variants @@ -351,4 +367,10 @@ Pipes:
  • ConnectNamedPipe + +Also useful: +Introduction + to Visual C++ for UNIX Users + diff --git a/ol.h b/ol.h index 430c435d..fd595e86 100644 --- a/ol.h +++ b/ol.h @@ -8,6 +8,16 @@ # include "ol_win.h" #endif +/** + * Error codes are not cross-platform, so we have our own. + */ +typedef enum { + OL_SUCCESS = 0, + OL_EAGAIN = -1, + OL_EPIPE = -2, + OL_EMEM = -3, +} ol_errno; + /** * Do not make assumptions about the order of the elements in this sturct. @@ -17,10 +27,19 @@ struct ol_buf; -typedef ol_read_cb void(*)(ol_buf *bufs, int bufcnt); -typedef ol_close_cb void(*)(int read, int write); -typedef ol_connect_cb void(*)(); -typedef ol_accept_cb void(*)(ol_handle *peer); +typedef enum { + OL_TCP, + OL_TCP6, + OL_NAMED_PIPE, + OL_FILE, + OL_TTY +} ol_handle_type; + + +typedef ol_read_cb void(*)(ol_handle* h, ol_buf *bufs, int bufcnt); +typedef ol_close_cb void(*)(ol_handle* h, int read, int write, ol_errno err); +typedef ol_connect_cb void(*)(ol_handle* h); +typedef ol_accept_cb void(*)(ol_handle* h, ol_handle *peer); /** @@ -70,10 +89,10 @@ int ol_bind(ol_handle* h, sockaddr* addr, sockaddr_len len); size_t ol_buffer_size(ol_handle* h); -int ol_pause(ol_handle* h); +int ol_read_stop(ol_handle* h); -int ol_resume(ol_handle* h); +int ol_read_start(ol_handle* h); /** @@ -85,6 +104,12 @@ int ol_resume(ol_handle* h); int ol_get_fd(ol_handle* h); +/** + * Returns the type of the handle. + */ +ol_handle_type ol_get_type(ol_handle* h); + + /** * Send data to h. User responsible for bufs until callback is made. * Multiple ol_handle_write() calls may be issued before the previous ones @@ -95,17 +120,25 @@ int ol_write(ol_handle* h, ol_buf* bufs, int bufcnt, /** - * Note: works on both named pipes and TCP handles. + * Works on both named pipes and TCP handles. */ int ol_listen(ol_handle* h, int backlog, ol_accept_cb cb); /** * Writes EOF or sends a FIN packet. + * Further calls to ol_write() result in OI_EPIPE error. When the send + * buffer is drained and the other side also terminates their writes, the + * handle is finally closed and ol_close_cb() made. There is no need to call + * ol_close() after this. */ -int ol_end(ol_handle* h); +int ol_graceful_close(ol_handle* h); +/** + * Immediately closes the handle. If there is data in the send buffer + * it will not be sent. + */ int ol_close(ol_handle* h); @@ -121,7 +154,7 @@ int ol_free(ol_handle* h); ol_loop* ol_loop_new(); -ol_loop* ol_associate(ol_handle* handle); +void ol_associate(ol_loop* loop, ol_handle* handle); void ol_loop_free(ol_loop* loop); diff --git a/ol_unix.c b/ol_unix.c index 6e1540ae..5ad9511f 100644 --- a/ol_unix.c +++ b/ol_unix.c @@ -2,7 +2,7 @@ ol_loop* ol_loop_new() { - ol_loop* loop = malloc(sizeof(ol_loop)); + ol_loop* loop = calloc(sizeof(ol_loop), 1); if (!loop) { return NULL; } @@ -16,7 +16,9 @@ ol_loop* ol_loop_new() { } -ol_loop* ol_associate(ol_handle* handle) { +void ol_associate(ol_loop* loop, ol_handle* handle) { + assert(!handle->loop); + handle->loop = loop; } @@ -26,7 +28,7 @@ void ol_run(ol_loop *loop) { ol_handle* ol_tcp_new(int v4, ol_read_cb read_cb, ol_close_cb close_cb) { - ol_handle *handle = malloc(sizeof(ol_handle)); + ol_handle *handle = calloc(sizeof(ol_handle), 1); if (!handle) { return NULL; } @@ -34,6 +36,8 @@ ol_handle* ol_tcp_new(int v4, ol_read_cb read_cb, ol_close_cb close_cb) { handle->read_cb = read_cb; handle->close_cb = close_cb; + handle->type = v4 ? OL_TCP : OL_TCP6; + int domain = v4 ? AF_INET : AF_INET6; handle->fd = socket(domain, SOCK_STREAM, 0); if (fd == -1) { @@ -46,32 +50,42 @@ ol_handle* ol_tcp_new(int v4, ol_read_cb read_cb, ol_close_cb close_cb) { } -void handle_tcp_io() { +static void tcp_io(EV_P_ ev_io *w, int revents) { + ol_handle* h = (ol_handle*)w->data; + if (h->connecting) { + tcp_check_connect_status(h); + } else { + + } } -int try_connect(ol_handle* h) { - int r = connect(h->fd, h->connect_addr, h->connect_addrlen); +static void tcp_check_connect_status(ol_handle* h) { + assert(h->connecting); - if (r != 0) { - if (errno == EINPROGRESS) { - /* Wait for fd to become writable. */ - h->connecting = 1; - ev_io_init(&h->write_watcher, handle_tcp_io, h->fd, EV_WRITE); - ev_io_start(h->loop, &h->write_watcher); - } - return got_error("connect", errno); + int error; + socklen_t len = sizeof(int); + getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &error, &len); + + if (error == 0) { + tcp_connected(h); + } else if (errno != EINPROGRESS) { + close(h->fd); + got_error("connect", errno); } - /* Connected */ + /* EINPROGRESS - unlikely. What to do? */ +} + + +static void tcp_connected(ol_handle* h) { + assert(h->connecting); if (h->connect_cb) { h->connect_cb(h); - h->connecting = 0; - h->connect_cb = NULL; } - - return 0; + h->connecting = 0; + h->connect_cb = NULL; } @@ -86,12 +100,27 @@ int ol_connect(ol_handle* h, sockaddr* addr, sockaddr_len addrlen, h->connect_addrlen = addrlen; if (buf) { + /* We're allowed to ol_write before the socket becomes connected. */ ol_write(h, buf, 1, bytes_sent, cb); } else { h->connect_cb = cb; } - return try_connect(h); + int r = connect(h->fd, h->connect_addr, h->connect_addrlen); + + if (r != 0) { + if (errno == EINPROGRESS) { + /* Wait for fd to become writable. */ + h->connecting = 1; + ev_io_init(&h->write_watcher, tcp_io, h->fd, EV_WRITE); + ev_io_start(h->loop, &h->write_watcher); + } + return got_error("connect", errno); + } + + /* Connected */ + tcp_connected(h); + return 0; } diff --git a/ol_unix.h b/ol_unix.h index 130a1ac3..0c9e3917 100644 --- a/ol_unix.h +++ b/ol_unix.h @@ -1,9 +1,7 @@ - - /** * Note can be cast to io_vec. */ -typedef struct _ol_buf { +typedef struct { char* buf; size_t len; ngx_queue_s write_queue; @@ -11,11 +9,16 @@ typedef struct _ol_buf { -typedef struct _ol_handle { +typedef struct { int fd; + ol_handle_type type; ol_read_cb read_cb; ol_close_cb close_cb; + ol_connect_cb connect_cb; + + ev_io read_watcher; + ev_io write_watcher; ngx_queue_s write_queue; ngx_queue_s all_handles;