Compiles and passes some very simple unit tests.
[ebb.git] / evtcp_server.c
blob7458eb3f6637a8ca80f7a1d0cb71476a4433a23e
1 #include <unistd.h>
2 #include <fcntl.h>
3 #include <sys/types.h>
4 #include <arpa/inet.h>
5 #include <netdb.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <errno.h>
12 #include <ev.h>
13 #include <glib.h>
15 #include <assert.h>
17 #include "ev_tcp_socket.h"
19 #define EV_TCP_CHUNKSIZE (16*1024)
22 int misc_lookup_host(char *address, struct in_addr *addr)
24 struct hostent *host;
25 host = gethostbyname(address);
26 if (host && host->h_addr_list[0]) {
27 memmove(addr, host->h_addr_list[0], sizeof(struct in_addr));
28 return 0;
30 return -1;
33 /* Returns the number of bytes remaining to write */
34 int ev_tcp_client_write(ev_tcp_client *client, const char *data, int length)
36 int sent = send(client->fd, data, length, 0);
37 if(sent < 0) {
38 client->error_cb(EV_TCP_ERROR, strerror(errno));
39 ev_tcp_client_close(client);
40 return;
42 return sent;
45 void ev_tcp_client_on_readable( struct ev_loop *loop
46 , struct ev_io *watcher
47 , int revents
50 ev_tcp_client *client = (ev_tcp_client*)(watcher->data);
51 char buffer[EV_TCP_CHUNKSIZE];
52 int length;
54 if(client->read_cb == NULL) return;
56 length = recv(client->fd, buffer, EV_TCP_CHUNKSIZE, 0);
58 if(length < 0) {
59 client->error_cb(EV_TCP_ERROR, strerror(errno));
60 ev_tcp_client_close(client);
61 return;
64 /* User needs to copy the data out of the buffer or process it before
65 * leaving this function.
67 client->read_cb(client, buffer, length);
70 ev_tcp_client* ev_tcp_client_new(ev_tcp_server *server)
72 socklen_t len;
73 ev_tcp_client *client;
75 client = g_new0(ev_tcp_client, 1);
76 client->error_cb = server->error_cb;
77 client->parent = server;
78 client->fd = accept(server->fd, (struct sockaddr*)&(client->sockaddr), &len);
79 if(client->fd < 0) {
80 client->error_cb(EV_TCP_ERROR, "Could not get client socket");
81 ev_tcp_client_free(client);
82 return NULL;
85 int r = fcntl(client->fd, F_SETFL, O_NONBLOCK);
86 if(r < 0) {
87 server->error_cb(EV_TCP_WARNING, "setting nonblock mode failed");
90 client->read_watcher = g_new0(struct ev_io, 1);
91 client->read_watcher->data = client;
92 ev_init (client->read_watcher, ev_tcp_client_on_readable);
93 ev_io_set (client->read_watcher, client->fd, EV_READ);
94 ev_io_start (server->loop, client->read_watcher);
96 return client;
99 void ev_tcp_client_free(ev_tcp_client *client)
101 ev_tcp_client_close(client);
102 free(client);
105 void ev_tcp_client_close(ev_tcp_client *client)
107 if(client->read_watcher) {
108 printf("killing read watcher\n");
109 ev_io_stop(client->parent->loop, client->read_watcher);
110 free(client->read_watcher);
111 client->read_watcher = NULL;
113 close(client->fd);
116 ev_tcp_server* ev_tcp_server_new(ev_tcp_error_cb error_cb)
118 int r;
119 ev_tcp_server *server = g_new0(ev_tcp_server, 1);
121 server->fd = socket(PF_INET, SOCK_STREAM, 0);
122 server->error_cb = error_cb;
123 r = fcntl(server->fd, F_SETFL, O_NONBLOCK);
124 if(r < 0) {
125 server->error_cb(EV_TCP_WARNING, "setting nonblock mode failed");
127 // set SO_REUSEADDR?
129 server->loop = ev_default_loop(0);
131 return server;
134 void ev_tcp_server_free(ev_tcp_server *server)
136 ev_tcp_server_close(server);
137 free(server);
140 void ev_tcp_server_close(ev_tcp_server *server)
142 if(server->accept_watcher) {
143 printf("killing accept watcher\n");
144 ev_io_stop(server->loop, server->accept_watcher);
145 free(server->accept_watcher);
146 server->accept_watcher = NULL;
148 close(server->fd);
151 void ev_tcp_server_accept( struct ev_loop *loop
152 , struct ev_io *watcher
153 , int revents
156 ev_tcp_server *server = (ev_tcp_server*)(watcher->data);
157 ev_tcp_client *client;
159 assert(server->loop == loop);
161 client = ev_tcp_client_new(server);
162 //g_queue_push_head(server->children, (gpointer)client);
164 if(server->accept_cb != NULL)
165 server->accept_cb(server, client);
167 return;
170 void ev_tcp_server_listen ( ev_tcp_server *server
171 , char *address
172 , int port
173 , int backlog
174 , ev_tcp_server_accept_cb accept_cb
177 int r;
179 server->sockaddr.sin_family = AF_INET;
180 server->sockaddr.sin_port = htons(port);
181 //inet_aton(address, server->sockaddr.sin_addr);
182 misc_lookup_host(address, &(server->sockaddr.sin_addr));
184 /* Other socket options. This could probably be fine tuned.
185 * SO_SNDBUF set buffer size for output
186 * SO_RCVBUF set buffer size for input
187 * SO_SNDLOWAT set minimum count for output
188 * SO_RCVLOWAT set minimum count for input
189 * SO_SNDTIMEO set timeout value for output
190 * SO_RCVTIMEO set timeout value for input
192 r = bind(server->fd, (struct sockaddr*)&(server->sockaddr), sizeof(server->sockaddr));
193 if(r < 0) {
194 server->error_cb(EV_TCP_ERROR, "bind failed");
195 close(server->fd);
196 return;
199 r = listen(server->fd, backlog);
200 if(r < 0) {
201 server->error_cb(EV_TCP_ERROR, "listen failed");
202 return;
205 server->accept_watcher = g_new0(struct ev_io, 1);
206 server->accept_watcher->data = server;
207 server->accept_cb = accept_cb;
209 ev_init (server->accept_watcher, ev_tcp_server_accept);
210 ev_io_set (server->accept_watcher, server->fd, EV_READ);
211 ev_io_start (server->loop, server->accept_watcher);
212 ev_loop (server->loop, 0);
213 return;