add microphone server/client examples
[rofl0r-rocksock.git] / rocksockserver.c
blob39056e89ca39400af3a22ecb73fd1a8b71d29fb2
1 /*
3 * author: rofl0r
5 * License: LGPL 2.1+ with static linking exception
8 */
10 #include <string.h>
11 #include <netdb.h>
12 #include <unistd.h>
13 #include <stddef.h>
14 #include <sys/socket.h>
15 #include <sys/select.h>
16 #include <netinet/in.h>
17 #include <time.h>
18 #include <errno.h>
19 #include <limits.h>
20 #include <stdlib.h>
22 #include "rocksockserver.h"
24 #include "endianness.h"
26 #ifndef NO_LOG
27 #include "../lib/include/logger.h"
28 #define LOGP(X) log_perror(X)
29 #else
30 #define LOGP(X) do {} while(0)
31 #endif
33 #include "../lib/include/strlib.h"
34 #include "../lib/include/stringptr.h"
35 #include "../lib/include/timelib.h"
37 typedef struct {
38 const char* host;
39 unsigned short port;
40 #ifndef IPV4_ONLY
41 struct addrinfo* hostaddr;
42 #else
43 struct sockaddr_in hostaddr;
44 #endif
45 } rs_hostInfo;
47 int rocksockserver_resolve_host(rs_hostInfo* hostinfo) {
48 if (!hostinfo || !hostinfo->host || !hostinfo->port) return -1;
49 #ifndef IPV4_ONLY
50 char pbuf[8];
51 char* ports;
52 int ret;
53 struct addrinfo hints;
55 memset(&hints, 0, sizeof(hints));
56 hints.ai_family = AF_UNSPEC;
57 hints.ai_socktype = SOCK_STREAM;
58 hints.ai_flags = AI_PASSIVE;
59 if(!(ports = intToString(hostinfo->port, pbuf))) return -1;
61 ret = getaddrinfo(hostinfo->host, ports, &hints, &hostinfo->hostaddr);
62 if(!ret) {
63 return 0;
64 } else {
65 #ifndef NO_LOG
66 log_put(1, VARISL("error resolving: "), VARICC(gai_strerror(ret)), NULL);
67 #endif
68 return ret;
70 #else
71 memset(&hostinfo->hostaddr, 0, sizeof(struct sockaddr_in));
72 ipv4fromstring(hostinfo->host, (unsigned char*) &hostinfo->hostaddr.sin_addr);
73 hostinfo->hostaddr.sin_family = AF_INET;
74 hostinfo->hostaddr.sin_port = htons(hostinfo->port);
75 return 0;
76 #endif
79 int rocksockserver_init(rocksockserver* srv, const char* listenip, unsigned short port, void* userdata) {
80 int ret = 0;
81 int yes = 1;
82 rs_hostInfo conn;
83 if(!srv || !listenip || !port) return -1;
84 conn.host = listenip;
85 conn.port = port;
86 FD_ZERO(&srv->master);
87 srv->userdata = userdata;
88 srv->sleeptime_us = 20000; // set a reasonable default value. it's a compromise between throughput and cpu usage basically.
89 ret = rocksockserver_resolve_host(&conn);
90 if(ret) return ret;
91 #ifndef IPV4_ONLY
92 struct addrinfo* p;
93 for(p = conn.hostaddr; p != NULL; p = p->ai_next) {
94 srv->listensocket = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
95 if (srv->listensocket < 0) {
96 continue;
99 // lose the pesky "address already in use" error message
100 setsockopt(srv->listensocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
102 if (bind(srv->listensocket, p->ai_addr, p->ai_addrlen) < 0) {
103 close(srv->listensocket);
104 continue;
107 break;
109 if (!p) {
110 # ifndef NO_LOG
111 log_puts(1, SPLITERAL("selectserver: failed to bind\n"));
112 # endif
113 ret = -1;
115 freeaddrinfo(conn.hostaddr);
116 if(ret == -1) return -1;
117 #else
118 srv->listensocket = socket(AF_INET, SOCK_STREAM, 0);
119 if(srv->listensocket < 0) {
120 LOGP("socket");
121 return -1;
123 setsockopt(srv->listensocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
124 if(bind(srv->listensocket, (struct sockaddr*) &conn.hostaddr, sizeof(struct sockaddr_in)) < 0) {
125 close(srv->listensocket);
126 LOGP("bind");
127 return -1;
130 #endif
131 // listen
132 if (listen(srv->listensocket, 10) == -1) {
133 LOGP("listen");
134 ret = -2;
135 } else {
136 FD_SET(srv->listensocket, &srv->master);
137 srv->maxfd = srv->listensocket;
139 return ret;
142 int rocksockserver_disconnect_client(rocksockserver* srv, int client) {
143 if(client < 0 || client > USER_MAX_FD) return -1;
144 if(FD_ISSET(client, &srv->master)) {
145 close(client);
146 FD_CLR(client, &srv->master);
147 if(client == srv->maxfd)
148 srv->maxfd--;
149 srv->numfds--;
150 return 0;
152 return 1;
155 void rocksockserver_watch_fd(rocksockserver* srv, int newfd) {
156 FD_SET(newfd, &srv->master);
157 if (newfd > srv->maxfd)
158 srv->maxfd = newfd;
161 int rocksockserver_loop(rocksockserver* srv,
162 char* buf, size_t bufsize,
163 int (*on_clientconnect) (void* userdata, struct sockaddr_storage* clientaddr, int fd),
164 int (*on_clientread) (void* userdata, int fd, size_t nread),
165 int (*on_clientwantsdata) (void* userdata, int fd),
166 int (*on_clientdisconnect) (void* userdata, int fd)
168 fd_set read_fds, write_fds;
169 int newfd, k;
170 int lastfd = 3;
171 #ifdef IS_LITTLE_ENDIAN
172 int i;
173 size_t j;
174 #endif
175 ptrdiff_t nbytes;
176 struct sockaddr_storage remoteaddr; // client address
177 socklen_t addrlen;
178 char* fdptr;
179 fd_set* setptr;
181 for(;;) {
183 read_fds = srv->master;
184 write_fds = srv->master;
186 if ((srv->numfds = select(srv->maxfd+1, &read_fds, &write_fds, NULL, NULL)) && srv->numfds == -1)
187 LOGP("select");
189 if(!srv->numfds) continue;
191 // optimization for the case searched_fd = lastfd, when we only have to handle one connection.
192 // i guess that should be the majority of cases.
193 k = lastfd;
194 setptr = &write_fds;
195 if(FD_ISSET(k, setptr)) goto gotcha;
196 setptr = &read_fds;
197 if(FD_ISSET(k, setptr)) goto gotcha;
199 nextfd:
200 setptr = &write_fds;
201 loopstart:
202 fdptr = (char*) setptr;
203 #ifdef IS_LITTLE_ENDIAN
204 for(i = 0; i * CHAR_BIT <= srv->maxfd; i+= sizeof(size_t)) { // we assume that sizeof(fd_set) is a multiple of sizeof(size_t)
205 if( *(size_t*)(fdptr + i)) {
206 for(j = 0; j <= sizeof(size_t); j++) {
207 if(fdptr[i + j]) {
208 for(k = (i + j) * CHAR_BIT; k <= srv->maxfd; k++) {
209 #else
210 for(k = 0; k <= srv->maxfd; k++) {
211 #endif
212 if(FD_ISSET(k, setptr)) {
213 gotcha:
214 srv->numfds--;
215 FD_CLR(k, setptr);
216 if(setptr == &write_fds)
217 goto handlewrite;
218 else
219 goto handleread;
222 #ifdef IS_LITTLE_ENDIAN
228 #endif
230 if(setptr == &write_fds) {
231 setptr = &read_fds;
232 goto loopstart;
233 } else {
234 #ifndef NO_LOG
235 log_puts(2, SPLITERAL("FATAL"));
236 #endif
238 printf("maxfd %d, k %d, numfds %d, set %d\n", srv->maxfd, k, srv->numfds, *(int*)(fdptr));
239 for(k = 0; k < USER_MAX_FD; k++)
240 if(FD_ISSET(k, setptr))
241 printf("bit set: %d\n", k);
243 #ifndef NO_ABORT
244 abort();
245 #else
246 exit(111);
247 #endif
250 handleread:
251 //printf("read_fd %d\n", k);
252 if (k == srv->listensocket) {
253 // new connection available
254 addrlen = sizeof(remoteaddr);
255 newfd = accept(srv->listensocket, (struct sockaddr *)&remoteaddr, &addrlen);
257 if (newfd == -1) {
258 LOGP("accept");
259 } else {
260 if(newfd >= USER_MAX_FD)
261 close(newfd); // only USER_MAX_FD connections can be handled.
262 else {
263 FD_SET(newfd, &srv->master);
264 if (newfd > srv->maxfd)
265 srv->maxfd = newfd;
266 if(on_clientconnect) on_clientconnect(srv->userdata, &remoteaddr, newfd);
269 } else {
270 if(buf && k != srv->signalfd) {
271 if ((nbytes = recv(k, buf, bufsize, 0)) <= 0) {
272 if (nbytes == 0) {
273 if(on_clientdisconnect) on_clientdisconnect(srv->userdata, k);
274 } else {
275 LOGP("recv");
277 rocksockserver_disconnect_client(srv, k);
278 } else {
279 if(on_clientread) on_clientread(srv->userdata, k, nbytes);
281 } else {
283 if(on_clientread) on_clientread(srv->userdata, k, 0);
286 goto zzz;
288 handlewrite:
290 //printf("write_fd %d\n", k);
291 if(on_clientwantsdata) on_clientwantsdata(srv->userdata, k);
293 zzz:
294 if(srv->numfds > 0) goto nextfd;
295 lastfd = k;
296 microsleep(srv->sleeptime_us);
298 return 0;