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)
33 #include "../lib/include/strlib.h"
34 #include "../lib/include/stringptr.h"
35 #include "../lib/include/timelib.h"
41 struct addrinfo
* hostaddr
;
43 struct sockaddr_in hostaddr
;
47 int rocksockserver_resolve_host(rs_hostInfo
* hostinfo
) {
48 if (!hostinfo
|| !hostinfo
->host
|| !hostinfo
->port
) return -1;
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
);
66 log_put(1, VARISL("error resolving: "), VARICC(gai_strerror(ret
)), NULL
);
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
);
79 int rocksockserver_init(rocksockserver
* srv
, const char* listenip
, unsigned short port
, void* userdata
) {
83 if(!srv
|| !listenip
|| !port
) return -1;
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
);
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) {
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
);
111 log_puts(1, SPLITERAL("selectserver: failed to bind\n"));
115 freeaddrinfo(conn
.hostaddr
);
116 if(ret
== -1) return -1;
118 srv
->listensocket
= socket(AF_INET
, SOCK_STREAM
, 0);
119 if(srv
->listensocket
< 0) {
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
);
132 if (listen(srv
->listensocket
, 10) == -1) {
136 FD_SET(srv
->listensocket
, &srv
->master
);
137 srv
->maxfd
= srv
->listensocket
;
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
)) {
146 FD_CLR(client
, &srv
->master
);
147 if(client
== srv
->maxfd
)
155 void rocksockserver_watch_fd(rocksockserver
* srv
, int newfd
) {
156 FD_SET(newfd
, &srv
->master
);
157 if (newfd
> srv
->maxfd
)
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
;
171 #ifdef IS_LITTLE_ENDIAN
176 struct sockaddr_storage remoteaddr
; // client address
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)
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.
195 if(FD_ISSET(k
, setptr
)) goto gotcha
;
197 if(FD_ISSET(k
, setptr
)) goto gotcha
;
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
++) {
208 for(k
= (i
+ j
) * CHAR_BIT
; k
<= srv
->maxfd
; k
++) {
210 for(k
= 0; k
<= srv
->maxfd
; k
++) {
212 if(FD_ISSET(k
, setptr
)) {
216 if(setptr
== &write_fds
)
222 #ifdef IS_LITTLE_ENDIAN
230 if(setptr
== &write_fds
) {
235 log_puts(2, SPLITERAL("FATAL"));
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);
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
);
260 if(newfd
>= USER_MAX_FD
)
261 close(newfd
); // only USER_MAX_FD connections can be handled.
263 FD_SET(newfd
, &srv
->master
);
264 if (newfd
> srv
->maxfd
)
266 if(on_clientconnect
) on_clientconnect(srv
->userdata
, &remoteaddr
, newfd
);
270 if(buf
&& k
!= srv
->signalfd
) {
271 if ((nbytes
= recv(k
, buf
, bufsize
, 0)) <= 0) {
273 if(on_clientdisconnect
) on_clientdisconnect(srv
->userdata
, k
);
277 rocksockserver_disconnect_client(srv
, k
);
279 if(on_clientread
) on_clientread(srv
->userdata
, k
, nbytes
);
283 if(on_clientread
) on_clientread(srv
->userdata
, k
, 0);
290 //printf("write_fd %d\n", k);
291 if(on_clientwantsdata
) on_clientwantsdata(srv
->userdata
, k
);
294 if(srv
->numfds
> 0) goto nextfd
;
296 microsleep(srv
->sleeptime_us
);