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"
28 #define LOGP(X) log_perror(X)
30 #define LOGP(X) do {} while(0)
34 #include "../lib/include/strlib.h"
35 #include "../lib/include/stringptr.h"
36 #include "../lib/include/timelib.h"
42 struct addrinfo
* hostaddr
;
44 struct sockaddr_in hostaddr
;
48 int rocksockserver_resolve_host(rs_hostInfo
* hostinfo
) {
49 if (!hostinfo
|| !hostinfo
->host
|| !hostinfo
->port
) return -1;
54 struct addrinfo hints
;
56 memset(&hints
, 0, sizeof(hints
));
57 hints
.ai_family
= AF_UNSPEC
;
58 hints
.ai_socktype
= SOCK_STREAM
;
59 hints
.ai_flags
= AI_PASSIVE
;
60 if(!(ports
= intToString(hostinfo
->port
, pbuf
))) return -1;
62 ret
= getaddrinfo(hostinfo
->host
, ports
, &hints
, &hostinfo
->hostaddr
);
67 log_put(1, VARISL("error resolving: "), VARICC(gai_strerror(ret
)), NULL
);
72 memset(&hostinfo
->hostaddr
, 0, sizeof(struct sockaddr_in
));
73 ipv4fromstring(hostinfo
->host
, (unsigned char*) &hostinfo
->hostaddr
.sin_addr
);
74 hostinfo
->hostaddr
.sin_family
= AF_INET
;
75 hostinfo
->hostaddr
.sin_port
= htons(hostinfo
->port
);
80 int rocksockserver_init(rocksockserver
* srv
, char* listenip
, unsigned short port
, void* userdata
) {
84 if(!srv
|| !listenip
|| !port
) return -1;
87 FD_ZERO(&srv
->master
);
88 srv
->userdata
= userdata
;
89 srv
->sleeptime_us
= 20000; // set a reasonable default value. it's a compromise between throughput and cpu usage basically.
90 ret
= rocksockserver_resolve_host(&conn
);
94 for(p
= conn
.hostaddr
; p
!= NULL
; p
= p
->ai_next
) {
95 srv
->listensocket
= socket(p
->ai_family
, p
->ai_socktype
, p
->ai_protocol
);
96 if (srv
->listensocket
< 0) {
100 // lose the pesky "address already in use" error message
101 setsockopt(srv
->listensocket
, SOL_SOCKET
, SO_REUSEADDR
, &yes
, sizeof(int));
103 if (bind(srv
->listensocket
, p
->ai_addr
, p
->ai_addrlen
) < 0) {
104 close(srv
->listensocket
);
112 log_puts(1, SPLITERAL("selectserver: failed to bind\n"));
116 freeaddrinfo(conn
.hostaddr
);
117 if(ret
== -1) return -1;
119 srv
->listensocket
= socket(AF_INET
, SOCK_STREAM
, 0);
120 if(srv
->listensocket
< 0) {
124 setsockopt(srv
->listensocket
, SOL_SOCKET
, SO_REUSEADDR
, &yes
, sizeof(int));
125 if(bind(srv
->listensocket
, (struct sockaddr
*) &conn
.hostaddr
, sizeof(struct sockaddr_in
)) < 0) {
126 close(srv
->listensocket
);
133 if (listen(srv
->listensocket
, 10) == -1) {
137 FD_SET(srv
->listensocket
, &srv
->master
);
138 srv
->maxfd
= srv
->listensocket
;
143 int rocksockserver_disconnect_client(rocksockserver
* srv
, int client
) {
144 if(client
< 0 || client
> USER_MAX_FD
) return -1;
145 if(FD_ISSET(client
, &srv
->master
)) {
147 FD_CLR(client
, &srv
->master
);
148 if(client
== srv
->maxfd
)
156 void rocksockserver_watch_fd(rocksockserver
* srv
, int newfd
) {
157 FD_SET(newfd
, &srv
->master
);
158 if (newfd
> srv
->maxfd
)
162 int rocksockserver_loop(rocksockserver
* srv
,
163 char* buf
, size_t bufsize
,
164 int (*on_clientconnect
) (void* userdata
, struct sockaddr_storage
* clientaddr
, int fd
),
165 int (*on_clientread
) (void* userdata
, int fd
, size_t nread
),
166 int (*on_clientwantsdata
) (void* userdata
, int fd
),
167 int (*on_clientdisconnect
) (void* userdata
, int fd
)
169 fd_set read_fds
, write_fds
;
172 #ifdef IS_LITTLE_ENDIAN
177 struct sockaddr_storage remoteaddr
; // client address
184 read_fds
= srv
->master
;
185 write_fds
= srv
->master
;
187 if ((srv
->numfds
= select(srv
->maxfd
+1, &read_fds
, &write_fds
, NULL
, NULL
)) && srv
->numfds
== -1)
190 if(!srv
->numfds
) continue;
192 // optimization for the case searched_fd = lastfd, when we only have to handle one connection.
193 // i guess that should be the majority of cases.
196 if(FD_ISSET(k
, setptr
)) goto gotcha
;
198 if(FD_ISSET(k
, setptr
)) goto gotcha
;
203 fdptr
= (char*) setptr
;
204 #ifdef IS_LITTLE_ENDIAN
205 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)
206 if( *(size_t*)(fdptr
+ i
)) {
207 for(j
= 0; j
<= sizeof(size_t); j
++) {
209 for(k
= (i
+ j
) * CHAR_BIT
; k
<= srv
->maxfd
; k
++) {
211 for(k
= 0; k
<= srv
->maxfd
; k
++) {
213 if(FD_ISSET(k
, setptr
)) {
217 if(setptr
== &write_fds
)
223 #ifdef IS_LITTLE_ENDIAN
231 if(setptr
== &write_fds
) {
236 log_puts(2, SPLITERAL("FATAL"));
239 printf("maxfd %d, k %d, numfds %d, set %d\n", srv->maxfd, k, srv->numfds, *(int*)(fdptr));
240 for(k = 0; k < USER_MAX_FD; k++)
241 if(FD_ISSET(k, setptr))
242 printf("bit set: %d\n", k);
252 //printf("read_fd %d\n", k);
253 if (k
== srv
->listensocket
) {
254 // new connection available
255 addrlen
= sizeof(remoteaddr
);
256 newfd
= accept(srv
->listensocket
, (struct sockaddr
*)&remoteaddr
, &addrlen
);
261 if(newfd
>= USER_MAX_FD
)
262 close(newfd
); // only USER_MAX_FD connections can be handled.
264 FD_SET(newfd
, &srv
->master
);
265 if (newfd
> srv
->maxfd
)
267 if(on_clientconnect
) on_clientconnect(srv
->userdata
, &remoteaddr
, newfd
);
271 if(buf
&& k
!= srv
->signalfd
) {
272 if ((nbytes
= recv(k
, buf
, bufsize
, 0)) <= 0) {
274 if(on_clientdisconnect
) on_clientdisconnect(srv
->userdata
, k
);
278 rocksockserver_disconnect_client(srv
, k
);
280 if(on_clientread
) on_clientread(srv
->userdata
, k
, nbytes
);
284 if(on_clientread
) on_clientread(srv
->userdata
, k
, 0);
291 //printf("write_fd %d\n", k);
292 if(on_clientwantsdata
) on_clientwantsdata(srv
->userdata
, k
);
295 if(srv
->numfds
> 0) goto nextfd
;
297 microsleep(srv
->sleeptime_us
);