📄 event_tcp.c
字号:
/************************************************************************* event_tcp.c -- implementation of event-driven socket I/O.** Copyright (C) 2001 Roaring Penguin Software Inc.** This program may be distributed according to the terms of the GNU* General Public License, version 2 or (at your option) any later version.** LIC: GPL************************************************************************/static char const RCSID[] ="$Id: event_tcp.c,v 1.6 2002/05/08 13:54:24 dfs Exp $";#include "event_tcp.h"#include <unistd.h>#include <fcntl.h>#include <sys/types.h>#include <stdlib.h>#include <errno.h>#include <string.h>static void free_state(EventTcpState *state);typedef struct EventTcpConnectState_t { int fd; EventHandler *conn; EventTcpConnectFunc f; void *data;} EventTcpConnectState;/*********************************************************************** %FUNCTION: handle_accept* %ARGUMENTS:* es -- event selector* fd -- socket* flags -- ignored* data -- the accept callback function* %RETURNS:* Nothing* %DESCRIPTION:* Calls accept; if a connection arrives, calls the accept callback* function with connected descriptor***********************************************************************/static voidhandle_accept(EventSelector *es, int fd, unsigned int flags, void *data){ int conn; EventTcpAcceptFunc f; EVENT_DEBUG(("tcp_handle_accept(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); conn = accept(fd, NULL, NULL); if (conn < 0) return; f = (EventTcpAcceptFunc) data; f(es, conn);}/*********************************************************************** %FUNCTION: handle_connect* %ARGUMENTS:* es -- event selector* fd -- socket* flags -- ignored* data -- the accept callback function* %RETURNS:* Nothing* %DESCRIPTION:* Calls accept; if a connection arrives, calls the accept callback* function with connected descriptor***********************************************************************/static voidhandle_connect(EventSelector *es, int fd, unsigned int flags, void *data){ int error = 0; socklen_t len = sizeof(error); EventTcpConnectState *state = (EventTcpConnectState *) data; EVENT_DEBUG(("tcp_handle_connect(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); /* Cancel writable event */ Event_DelHandler(es, state->conn); state->conn = NULL; /* Timeout? */ if (flags & EVENT_FLAG_TIMEOUT) { errno = ETIMEDOUT; state->f(es, fd, EVENT_TCP_FLAG_TIMEOUT, state->data); free(state); return; } /* Check for pending error */ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { state->f(es, fd, EVENT_TCP_FLAG_IOERROR, state->data); free(state); return; } if (error) { errno = error; state->f(es, fd, EVENT_TCP_FLAG_IOERROR, state->data); free(state); return; } /* It looks cool! */ state->f(es, fd, EVENT_TCP_FLAG_COMPLETE, state->data); free(state);}/*********************************************************************** %FUNCTION: EventTcp_CreateAcceptor* %ARGUMENTS:* es -- event selector* socket -- listening socket* f -- function to call when a connection is accepted* data -- extra data to pass to f.* %RETURNS:* An event handler on success, NULL on failure.* %DESCRIPTION:* Sets up an accepting socket and calls "f" whenever a new* connection arrives.***********************************************************************/EventHandler *EventTcp_CreateAcceptor(EventSelector *es, int socket, EventTcpAcceptFunc f){ int flags; EVENT_DEBUG(("EventTcp_CreateAcceptor(es=%p, socket=%d)\n", es, socket)); /* Make sure socket is non-blocking */ flags = fcntl(socket, F_GETFL, 0); if (flags == -1) { return NULL; } if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) { return NULL; } return Event_AddHandler(es, socket, EVENT_FLAG_READABLE, handle_accept, (void *) f);}/*********************************************************************** %FUNCTION: free_state* %ARGUMENTS:* state -- EventTcpState to free* %RETURNS:* Nothing* %DESCRIPTION:* Frees all state associated with the TcpEvent.***********************************************************************/static voidfree_state(EventTcpState *state){ if (!state) return; EVENT_DEBUG(("tcp_free_state(state=%p)\n", state)); if (state->buf) free(state->buf); if (state->eh) Event_DelHandler(state->es, state->eh); free(state);}/*********************************************************************** %FUNCTION: handle_readable* %ARGUMENTS:* es -- event selector* fd -- the readable socket* flags -- ignored* data -- the EventTcpState object* %RETURNS:* Nothing* %DESCRIPTION:* Continues to fill buffer. Calls callback when done.***********************************************************************/static voidhandle_readable(EventSelector *es, int fd, unsigned int flags, void *data){ EventTcpState *state = (EventTcpState *) data; int done = state->cur - state->buf; int togo = state->len - done; int nread = 0; int flag; EVENT_DEBUG(("tcp_handle_readable(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); /* Timed out? */ if (flags & EVENT_FLAG_TIMEOUT) { errno = ETIMEDOUT; (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_TIMEOUT, state->data); free_state(state); return; } if (state->delim < 0) { /* Not looking for a delimiter */ /* togo had better not be zero here! */ nread = read(fd, state->cur, togo); if (nread <= 0) { /* Change connection reset to EOF if we have read at least one char */ if (nread < 0 && errno == ECONNRESET && done > 0) { nread = 0; } flag = (nread) ? EVENT_TCP_FLAG_IOERROR : EVENT_TCP_FLAG_EOF; /* error or EOF */ (state->f)(es, state->socket, state->buf, done, flag, state->data); free_state(state); return; } state->cur += nread; done += nread; if (done >= state->len) { /* Read enough! */ (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_COMPLETE, state->data); free_state(state); return; } } else { /* Looking for a delimiter */ while ( (togo > 0) && (nread = read(fd, state->cur, 1)) == 1) { togo--; done++; state->cur++; if (*(state->cur - 1) == state->delim) break; } if (nread <= 0) { /* Error or EOF -- check for EAGAIN */ if (nread < 0 && errno == EAGAIN) return; } /* Some other error, or EOF, or delimiter, or read enough */ if (nread < 0) { flag = EVENT_TCP_FLAG_IOERROR; } else if (nread == 0) { flag = EVENT_TCP_FLAG_EOF; } else { flag = EVENT_TCP_FLAG_COMPLETE; } (state->f)(es, state->socket, state->buf, done, flag, state->data); free_state(state); return; }}/*********************************************************************** %FUNCTION: handle_writeable* %ARGUMENTS:* es -- event selector* fd -- the writeable socket* flags -- ignored* data -- the EventTcpState object* %RETURNS:* Nothing* %DESCRIPTION:* Continues to fill buffer. Calls callback when done.***********************************************************************/static voidhandle_writeable(EventSelector *es, int fd, unsigned int flags, void *data){ EventTcpState *state = (EventTcpState *) data; int done = state->cur - state->buf; int togo = state->len - done; int n; /* Timed out? */ if (flags & EVENT_FLAG_TIMEOUT) { errno = ETIMEDOUT; (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_TIMEOUT, state->data); free_state(state); return; } /* togo had better not be zero here! */ n = write(fd, state->cur, togo); EVENT_DEBUG(("tcp_handle_writeable(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data)); if (n <= 0) { /* error */ if (state->f) { (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_IOERROR, state->data); } else { close(fd); } free_state(state); return; } state->cur += n; done += n; if (done >= state->len) { /* Written enough! */ if (state->f) { (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_COMPLETE, state->data); } else { close(fd); } free_state(state); return; }}/*********************************************************************** %FUNCTION: EventTcp_ReadBuf* %ARGUMENTS:* es -- event selector* socket -- socket to read from* len -- maximum number of bytes to read* delim -- delimiter at which to stop reading, or -1 if we should* read exactly len bytes* f -- function to call on EOF or when all bytes have been read* timeout -- if non-zero, timeout in seconds after which we cancel* operation.* data -- extra data to pass to function f.* %RETURNS:* A new EventTcpState token or NULL on error* %DESCRIPTION:* Sets up a handler to fill a buffer from a socket.***********************************************************************/EventTcpState *EventTcp_ReadBuf(EventSelector *es, int socket, int len, int delim, EventTcpIOFinishedFunc f, int timeout, void *data){ EventTcpState *state; int flags; struct timeval t; EVENT_DEBUG(("EventTcp_ReadBuf(es=%p, socket=%d, len=%d, delim=%d, timeout=%d)\n", es, socket, len, delim, timeout)); if (len <= 0) return NULL; if (socket < 0) return NULL; /* Make sure socket is non-blocking */ flags = fcntl(socket, F_GETFL, 0); if (flags == -1) { return NULL; } if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) { return NULL; } state = malloc(sizeof(EventTcpState)); if (!state) return NULL; memset(state, 0, sizeof(EventTcpState)); state->socket = socket; state->buf = malloc(len); if (!state->buf) { free_state(state); return NULL; } state->cur = state->buf; state->len = len; state->f = f; state->es = es; if (timeout <= 0) { t.tv_sec = -1; t.tv_usec = -1; } else { t.tv_sec = timeout; t.tv_usec = 0; } state->eh = Event_AddHandlerWithTimeout(es, socket, EVENT_FLAG_READABLE, t, handle_readable, (void *) state); if (!state->eh) { free_state(state); return NULL; } state->data = data; state->delim = delim; EVENT_DEBUG(("EventTcp_ReadBuf() -> %p\n", state)); return state;}/*********************************************************************** %FUNCTION: EventTcp_WriteBuf* %ARGUMENTS:* es -- event selector* socket -- socket to read from* buf -- buffer to write* len -- number of bytes to write* f -- function to call on EOF or when all bytes have been read* timeout -- timeout after which to cancel operation* data -- extra data to pass to function f.* %RETURNS:* A new EventTcpState token or NULL on error* %DESCRIPTION:* Sets up a handler to fill a buffer from a socket.***********************************************************************/EventTcpState *EventTcp_WriteBuf(EventSelector *es, int socket, char *buf, int len, EventTcpIOFinishedFunc f, int timeout, void *data){ EventTcpState *state; int flags; struct timeval t; EVENT_DEBUG(("EventTcp_WriteBuf(es=%p, socket=%d, len=%d, timeout=%d)\n", es, socket, len, timeout)); if (len <= 0) return NULL; if (socket < 0) return NULL; /* Make sure socket is non-blocking */ flags = fcntl(socket, F_GETFL, 0); if (flags == -1) { return NULL; } if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) { return NULL; } state = malloc(sizeof(EventTcpState)); if (!state) return NULL; memset(state, 0, sizeof(EventTcpState)); state->socket = socket; state->buf = malloc(len); if (!state->buf) { free_state(state); return NULL; } memcpy(state->buf, buf, len); state->cur = state->buf; state->len = len; state->f = f; state->es = es; if (timeout <= 0) { t.tv_sec = -1; t.tv_usec = -1; } else { t.tv_sec = timeout; t.tv_usec = 0; } state->eh = Event_AddHandlerWithTimeout(es, socket, EVENT_FLAG_WRITEABLE, t, handle_writeable, (void *) state); if (!state->eh) { free_state(state); return NULL; } state->data = data; state->delim = -1; EVENT_DEBUG(("EventTcp_WriteBuf() -> %p\n", state)); return state;}/*********************************************************************** %FUNCTION: EventTcp_Connect* %ARGUMENTS:* es -- event selector* fd -- descriptor to connect* addr -- address to connect to* addrlen -- length of address* f -- function to call with connected socket* data -- extra data to pass to f* %RETURNS:* Nothing* %DESCRIPTION:* Does a non-blocking connect on fd***********************************************************************/voidEventTcp_Connect(EventSelector *es, int fd, struct sockaddr const *addr, socklen_t addrlen, EventTcpConnectFunc f, int timeout, void *data){ int flags; int n; EventTcpConnectState *state; struct timeval t; /* Make sure socket is non-blocking */ flags = fcntl(fd, F_GETFL, 0); if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { f(es, fd, EVENT_TCP_FLAG_IOERROR, data); return; } n = connect(fd, addr, addrlen); if (n < 0) { if (errno != EINPROGRESS) { f(es, fd, EVENT_TCP_FLAG_IOERROR, data); return; } } if (n == 0) { /* Connect succeeded immediately */ f(es, fd, EVENT_TCP_FLAG_COMPLETE, data); return; } state = malloc(sizeof(*state)); if (!state) { f(es, fd, EVENT_TCP_FLAG_IOERROR, data); return; } state->f = f; state->fd = fd; state->data = data; if (timeout <= 0) { t.tv_sec = -1; t.tv_usec = -1; } else { t.tv_sec = timeout; t.tv_usec = 0; } state->conn = Event_AddHandlerWithTimeout(es, fd, EVENT_FLAG_WRITEABLE, t, handle_connect, (void *) state); if (!state->conn) { free(state); f(es, fd, EVENT_TCP_FLAG_IOERROR, data); return; }}/*********************************************************************** %FUNCTION: EventTcp_CancelPending* %ARGUMENTS:* s -- an EventTcpState* %RETURNS:* Nothing* %DESCRIPTION:* Cancels the pending event handler***********************************************************************/voidEventTcp_CancelPending(EventTcpState *s){ free_state(s);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -