small clean up in read_cb api
[ebb.git] / tcp_server.c
blobe6483b76ea201d41f8fbbf5fcf02a5ee47435f8a
1 /* Evented TCP Server
2 * Copyright (c) 2007 Ry Dahl <ry.d4hl@gmail.com>
3 * All rights reserved.
4 */
6 /* TODO: add timeouts for clients */
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <sys/types.h>
10 #include <arpa/inet.h>
11 #include <netdb.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <errno.h>
18 #include <ev.h>
19 #include <glib.h>
21 #include <assert.h>
23 #include "tcp_server.h"
25 #define TCP_CHUNKSIZE (16*1024)
27 int misc_lookup_host(char *address, struct in_addr *addr)
29 struct hostent *host;
30 host = gethostbyname(address);
31 if (host && host->h_addr_list[0]) {
32 memmove(addr, host->h_addr_list[0], sizeof(struct in_addr));
33 return 0;
35 return -1;
38 /* Returns the number of bytes remaining to write */
39 int tcp_client_write(tcp_client *client, const char *data, int length)
41 int sent = send(client->fd, data, length, 0);
42 if(sent < 0) {
43 client->error_cb(ERROR_CB_ERROR, strerror(errno));
44 tcp_client_close(client);
45 return 0;
47 return sent;
50 void tcp_client_on_readable( struct ev_loop *loop
51 , struct ev_io *watcher
52 , int revents
55 tcp_client *client = (tcp_client*)(watcher->data);
56 char buffer[TCP_CHUNKSIZE];
57 int length;
59 if(client->read_cb == NULL) return;
61 length = recv(client->fd, buffer, TCP_CHUNKSIZE, 0);
63 if(length < 0) {
64 client->error_cb(ERROR_CB_ERROR, strerror(errno));
65 tcp_client_close(client);
66 return;
69 /* User needs to copy the data out of the buffer or process it before
70 * leaving this function.
72 client->read_cb(buffer, length, client->read_cb_data);
75 tcp_client* tcp_client_new(tcp_server *server)
77 socklen_t len;
78 tcp_client *client;
80 client = g_new0(tcp_client, 1);
81 client->error_cb = server->error_cb;
82 client->parent = server;
83 client->fd = accept(server->fd, (struct sockaddr*)&(client->sockaddr), &len);
84 if(client->fd < 0) {
85 client->error_cb(ERROR_CB_ERROR, "Could not get client socket");
86 tcp_client_free(client);
87 return NULL;
90 int r = fcntl(client->fd, F_SETFL, O_NONBLOCK);
91 if(r < 0) {
92 server->error_cb(ERROR_CB_WARNING, "setting nonblock mode failed");
95 client->read_watcher = g_new0(struct ev_io, 1);
96 client->read_watcher->data = client;
97 ev_init (client->read_watcher, tcp_client_on_readable);
98 ev_io_set (client->read_watcher, client->fd, EV_READ);
99 ev_io_start (server->loop, client->read_watcher);
101 return client;
104 void tcp_client_free(tcp_client *client)
106 tcp_client_close(client);
107 free(client);
110 void tcp_client_close(tcp_client *client)
112 if(client->read_watcher) {
113 printf("killing read watcher\n");
114 ev_io_stop(client->parent->loop, client->read_watcher);
115 free(client->read_watcher);
116 client->read_watcher = NULL;
118 close(client->fd);
121 tcp_server* tcp_server_new(error_cb_t error_cb)
123 int r;
124 tcp_server *server = g_new0(tcp_server, 1);
126 server->fd = socket(PF_INET, SOCK_STREAM, 0);
127 server->error_cb = error_cb;
128 r = fcntl(server->fd, F_SETFL, O_NONBLOCK);
129 if(r < 0) {
130 server->error_cb(ERROR_CB_WARNING, "setting nonblock mode failed");
132 // set SO_REUSEADDR?
134 server->loop = ev_default_loop(0);
136 return server;
139 void tcp_server_free(tcp_server *server)
141 tcp_server_close(server);
142 free(server);
145 void tcp_server_close(tcp_server *server)
147 if(server->accept_watcher) {
148 printf("killing accept watcher\n");
149 ev_io_stop(server->loop, server->accept_watcher);
150 free(server->accept_watcher);
151 server->accept_watcher = NULL;
153 close(server->fd);
156 void tcp_server_accept( struct ev_loop *loop
157 , struct ev_io *watcher
158 , int revents
161 tcp_server *server = (tcp_server*)(watcher->data);
162 tcp_client *client;
164 assert(server->loop == loop);
166 client = tcp_client_new(server);
167 //g_queue_push_head(server->children, (gpointer)client);
169 if(server->accept_cb != NULL)
170 server->accept_cb(server, client, server->accept_cb_data);
172 return;
175 void tcp_server_listen ( tcp_server *server
176 , char *address
177 , int port
178 , int backlog
179 , tcp_server_accept_cb_t accept_cb
180 , void *accept_cb_data
183 int r;
185 server->sockaddr.sin_family = AF_INET;
186 server->sockaddr.sin_port = htons(port);
187 //inet_aton(address, server->sockaddr.sin_addr);
188 misc_lookup_host(address, &(server->sockaddr.sin_addr));
190 /* Other socket options. This could probably be fine tuned.
191 * SO_SNDBUF set buffer size for output
192 * SO_RCVBUF set buffer size for input
193 * SO_SNDLOWAT set minimum count for output
194 * SO_RCVLOWAT set minimum count for input
195 * SO_SNDTIMEO set timeout value for output
196 * SO_RCVTIMEO set timeout value for input
198 r = bind(server->fd, (struct sockaddr*)&(server->sockaddr), sizeof(server->sockaddr));
199 if(r < 0) {
200 server->error_cb(ERROR_CB_ERROR, "bind failed");
201 close(server->fd);
202 return;
205 r = listen(server->fd, backlog);
206 if(r < 0) {
207 server->error_cb(ERROR_CB_ERROR, "listen failed");
208 return;
211 server->accept_watcher = g_new0(struct ev_io, 1);
212 server->accept_watcher->data = server;
213 server->accept_cb = accept_cb;
214 server->accept_cb_data = accept_cb_data;
216 ev_init (server->accept_watcher, tcp_server_accept);
217 ev_io_set (server->accept_watcher, server->fd, EV_READ);
218 ev_io_start (server->loop, server->accept_watcher);
219 ev_loop (server->loop, 0);
220 return;