5 * License: LGPL 2.1+ with static linking exception
14 #include <sys/socket.h>
15 #include <sys/select.h>
16 #include <netinet/in.h>
22 #include "rocksockserver.h"
24 #include "endianness.h"
27 #include "../lib/include/logger.h"
29 #include "../lib/include/strlib.h"
30 #include "../lib/include/stringptr.h"
31 #include "../lib/include/timelib.h"
37 struct addrinfo
* hostaddr
;
39 struct sockaddr_in hostaddr
;
43 int rocksockserver_resolve_host(rs_hostInfo
* hostinfo
) {
44 if (!hostinfo
|| !hostinfo
->host
|| !hostinfo
->port
) return -1;
49 struct addrinfo hints
;
51 memset(&hints
, 0, sizeof(hints
));
52 hints
.ai_family
= AF_UNSPEC
;
53 hints
.ai_socktype
= SOCK_STREAM
;
54 hints
.ai_flags
= AI_PASSIVE
;
55 if(!(ports
= intToString(hostinfo
->port
, pbuf
))) return -1;
57 ret
= getaddrinfo(hostinfo
->host
, ports
, &hints
, &hostinfo
->hostaddr
);
62 log_put(1, VARISL("error resolving: "), VARICC(gai_strerror(ret
)), NULL
);
67 memset(&hostinfo
->hostaddr
, 0, sizeof(struct sockaddr_in
));
68 ipv4fromstring(hostinfo
->host
, (unsigned char*) &hostinfo
->hostaddr
.sin_addr
);
69 hostinfo
->hostaddr
.sin_family
= AF_INET
;
70 hostinfo
->hostaddr
.sin_port
= htons(hostinfo
->port
);
75 int rocksockserver_init(rocksockserver
* srv
, char* listenip
, unsigned short port
, void* userdata
) {
79 if(!srv
|| !listenip
|| !port
) return -1;
82 FD_ZERO(&srv
->master
);
83 srv
->userdata
= userdata
;
84 srv
->sleeptime_us
= 20000; // set a reasonable default value. it's a compromise between throughput and cpu usage basically.
85 ret
= rocksockserver_resolve_host(&conn
);
89 for(p
= conn
.hostaddr
; p
!= NULL
; p
= p
->ai_next
) {
90 srv
->listensocket
= socket(p
->ai_family
, p
->ai_socktype
, p
->ai_protocol
);
91 if (srv
->listensocket
< 0) {
95 // lose the pesky "address already in use" error message
96 setsockopt(srv
->listensocket
, SOL_SOCKET
, SO_REUSEADDR
, &yes
, sizeof(int));
98 if (bind(srv
->listensocket
, p
->ai_addr
, p
->ai_addrlen
) < 0) {
99 close(srv
->listensocket
);
107 log_puts(1, SPLITERAL("selectserver: failed to bind\n"));
111 freeaddrinfo(conn
.hostaddr
);
112 if(ret
== -1) return -1;
114 srv
->listensocket
= socket(AF_INET
, SOCK_STREAM
, 0);
115 if(srv
->listensocket
< 0) {
117 log_perror("socket");
121 setsockopt(srv
->listensocket
, SOL_SOCKET
, SO_REUSEADDR
, &yes
, sizeof(int));
122 if(bind(srv
->listensocket
, (struct sockaddr
*) &conn
.hostaddr
, sizeof(struct sockaddr_in
)) < 0) {
123 close(srv
->listensocket
);
132 if (listen(srv
->listensocket
, 10) == -1) {
134 log_perror("listen");
138 FD_SET(srv
->listensocket
, &srv
->master
);
139 srv
->maxfd
= srv
->listensocket
;
144 int rocksockserver_disconnect_client(rocksockserver
* srv
, int client
) {
145 if(client
< 0 || client
> USER_MAX_FD
) return -1;
146 if(FD_ISSET(client
, &srv
->master
)) {
148 FD_CLR(client
, &srv
->master
);
149 if(client
== srv
->maxfd
)
157 void rocksockserver_watch_fd(rocksockserver
* srv
, int newfd
) {
158 FD_SET(newfd
, &srv
->master
);
159 if (newfd
> srv
->maxfd
)
163 int rocksockserver_loop(rocksockserver
* srv
,
164 char* buf
, size_t bufsize
,
165 int (*on_clientconnect
) (void* userdata
, struct sockaddr_storage
* clientaddr
, int fd
),
166 int (*on_clientread
) (void* userdata
, int fd
, size_t nread
),
167 int (*on_clientwantsdata
) (void* userdata
, int fd
),
168 int (*on_clientdisconnect
) (void* userdata
, int fd
)
170 fd_set read_fds
, write_fds
;
173 #ifdef IS_LITTLE_ENDIAN
178 struct sockaddr_storage remoteaddr
; // client address
185 read_fds
= srv
->master
;
186 write_fds
= srv
->master
;
188 if ((srv
->numfds
= select(srv
->maxfd
+1, &read_fds
, &write_fds
, NULL
, NULL
)) && srv
->numfds
== -1)
190 log_perror("select");
195 if(!srv
->numfds
) continue;
197 // optimization for the case searched_fd = lastfd, when we only have to handle one connection.
198 // i guess that should be the majority of cases.
201 if(FD_ISSET(k
, setptr
)) goto gotcha
;
203 if(FD_ISSET(k
, setptr
)) goto gotcha
;
208 fdptr
= (char*) setptr
;
209 #ifdef IS_LITTLE_ENDIAN
210 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)
211 if( *(size_t*)(fdptr
+ i
)) {
212 for(j
= 0; j
<= sizeof(size_t); j
++) {
214 for(k
= (i
+ j
) * CHAR_BIT
; k
<= srv
->maxfd
; k
++) {
216 for(k
= 0; k
<= srv
->maxfd
; k
++) {
218 if(FD_ISSET(k
, setptr
)) {
222 if(setptr
== &write_fds
)
228 #ifdef IS_LITTLE_ENDIAN
236 if(setptr
== &write_fds
) {
241 log_puts(2, SPLITERAL("FATAL"));
244 printf("maxfd %d, k %d, numfds %d, set %d\n", srv->maxfd, k, srv->numfds, *(int*)(fdptr));
245 for(k = 0; k < USER_MAX_FD; k++)
246 if(FD_ISSET(k, setptr))
247 printf("bit set: %d\n", k);
257 //printf("read_fd %d\n", k);
258 if (k
== srv
->listensocket
) {
259 // new connection available
260 addrlen
= sizeof(remoteaddr
);
261 newfd
= accept(srv
->listensocket
, (struct sockaddr
*)&remoteaddr
, &addrlen
);
265 log_perror("accept");
268 if(newfd
>= USER_MAX_FD
)
269 close(newfd
); // only USER_MAX_FD connections can be handled.
271 FD_SET(newfd
, &srv
->master
);
272 if (newfd
> srv
->maxfd
)
274 if(on_clientconnect
) on_clientconnect(srv
->userdata
, &remoteaddr
, newfd
);
278 if(buf
&& k
!= srv
->signalfd
) {
279 if ((nbytes
= recv(k
, buf
, bufsize
, 0)) <= 0) {
281 if(on_clientdisconnect
) on_clientdisconnect(srv
->userdata
, k
);
287 rocksockserver_disconnect_client(srv
, k
);
289 if(on_clientread
) on_clientread(srv
->userdata
, k
, nbytes
);
293 if(on_clientread
) on_clientread(srv
->userdata
, k
, 0);
300 //printf("write_fd %d\n", k);
301 if(on_clientwantsdata
) on_clientwantsdata(srv
->userdata
, k
);
304 if(srv
->numfds
> 0) goto nextfd
;
306 microsleep(srv
->sleeptime_us
);