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"
26 #define LOGP(X) do { if(srv->perr) srv->perr(X); } while(0)
29 #include "../lib/include/strlib.h"
30 #include "../lib/include/timelib.h"
33 #include <arpa/inet.h>
34 #define microsleep(X) usleep(X)
35 static inline char* my_intToString(int i
, char *b
, size_t s
) {
36 int x
= snprintf(b
, s
, "%d", i
);
37 if(x
> 0 && x
< s
) return b
;
40 #define intToString(i, b) my_intToString(i, b, sizeof(b))
41 #define ipv4fromstring(s, b) inet_aton(s, (struct in_addr *)(void*)(b))
48 struct addrinfo
* hostaddr
;
50 struct sockaddr_in hostaddr
;
54 int rocksockserver_resolve_host(rs_hostInfo
* hostinfo
) {
55 if (!hostinfo
|| !hostinfo
->host
|| !hostinfo
->port
) return -1;
60 struct addrinfo hints
;
62 memset(&hints
, 0, sizeof(hints
));
63 hints
.ai_family
= AF_UNSPEC
;
64 hints
.ai_socktype
= SOCK_STREAM
;
65 hints
.ai_flags
= AI_PASSIVE
;
66 if(!(ports
= intToString(hostinfo
->port
, pbuf
))) return -1;
67 return getaddrinfo(hostinfo
->host
, ports
, &hints
, &hostinfo
->hostaddr
);
69 memset(&hostinfo
->hostaddr
, 0, sizeof(struct sockaddr_in
));
70 ipv4fromstring(hostinfo
->host
, (unsigned char*) &hostinfo
->hostaddr
.sin_addr
);
71 hostinfo
->hostaddr
.sin_family
= AF_INET
;
72 hostinfo
->hostaddr
.sin_port
= htons(hostinfo
->port
);
77 /* returns 0 on success.
78 possible error return codes:
79 -1: erroneus parameter
83 positive number: dns error, pass to gai_strerror()
85 int rocksockserver_init(rocksockserver
* srv
, const char* listenip
, unsigned short port
, void* userdata
) {
89 if(!srv
|| !listenip
|| !port
) return -1;
92 FD_ZERO(&srv
->master
);
93 srv
->userdata
= userdata
;
94 srv
->sleeptime_us
= 20000; // set a reasonable default value. it's a compromise between throughput and cpu usage basically.
95 ret
= rocksockserver_resolve_host(&conn
);
99 for(p
= conn
.hostaddr
; p
!= NULL
; p
= p
->ai_next
) {
100 srv
->listensocket
= socket(p
->ai_family
, p
->ai_socktype
, p
->ai_protocol
);
101 if (srv
->listensocket
< 0) {
105 // lose the pesky "address already in use" error message
106 setsockopt(srv
->listensocket
, SOL_SOCKET
, SO_REUSEADDR
, &yes
, sizeof(int));
108 if (bind(srv
->listensocket
, p
->ai_addr
, p
->ai_addrlen
) < 0) {
109 close(srv
->listensocket
);
119 freeaddrinfo(conn
.hostaddr
);
120 if(ret
== -2) return -2;
122 srv
->listensocket
= socket(AF_INET
, SOCK_STREAM
, 0);
123 if(srv
->listensocket
< 0) {
127 setsockopt(srv
->listensocket
, SOL_SOCKET
, SO_REUSEADDR
, &yes
, sizeof(int));
128 if(bind(srv
->listensocket
, (struct sockaddr
*) &conn
.hostaddr
, sizeof(struct sockaddr_in
)) < 0) {
129 close(srv
->listensocket
);
136 if (listen(srv
->listensocket
, 10) == -1) {
140 FD_SET(srv
->listensocket
, &srv
->master
);
141 srv
->maxfd
= srv
->listensocket
;
146 int rocksockserver_disconnect_client(rocksockserver
* srv
, int client
) {
147 if(client
< 0 || client
> USER_MAX_FD
) return -1;
148 if(FD_ISSET(client
, &srv
->master
)) {
150 FD_CLR(client
, &srv
->master
);
151 if(client
== srv
->maxfd
)
159 void rocksockserver_watch_fd(rocksockserver
* srv
, int newfd
) {
160 FD_SET(newfd
, &srv
->master
);
161 if (newfd
> srv
->maxfd
)
165 int rocksockserver_loop(rocksockserver
* srv
,
166 char* buf
, size_t bufsize
,
167 int (*on_clientconnect
) (void* userdata
, struct sockaddr_storage
* clientaddr
, int fd
),
168 int (*on_clientread
) (void* userdata
, int fd
, size_t nread
),
169 int (*on_clientwantsdata
) (void* userdata
, int fd
),
170 int (*on_clientdisconnect
) (void* userdata
, int fd
)
172 fd_set read_fds
, write_fds
;
175 #ifdef IS_LITTLE_ENDIAN
180 struct sockaddr_storage remoteaddr
; // client address
187 read_fds
= srv
->master
;
188 write_fds
= srv
->master
;
190 if ((srv
->numfds
= select(srv
->maxfd
+1, &read_fds
, &write_fds
, NULL
, NULL
)) && srv
->numfds
== -1)
193 if(!srv
->numfds
) continue;
195 // optimization for the case searched_fd = lastfd, when we only have to handle one connection.
196 // i guess that should be the majority of cases.
199 if(FD_ISSET(k
, setptr
)) goto gotcha
;
201 if(FD_ISSET(k
, setptr
)) goto gotcha
;
206 fdptr
= (char*) setptr
;
207 #ifdef IS_LITTLE_ENDIAN
208 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)
209 if( *(size_t*)(fdptr
+ i
)) {
210 for(j
= 0; j
<= sizeof(size_t); j
++) {
212 for(k
= (i
+ j
) * CHAR_BIT
; k
<= srv
->maxfd
; k
++) {
214 for(k
= 0; k
<= srv
->maxfd
; k
++) {
216 if(FD_ISSET(k
, setptr
)) {
220 if(setptr
== &write_fds
)
226 #ifdef IS_LITTLE_ENDIAN
234 if(setptr
== &write_fds
) {
240 printf("maxfd %d, k %d, numfds %d, set %d\n", srv->maxfd, k, srv->numfds, *(int*)(fdptr));
241 for(k = 0; k < USER_MAX_FD; k++)
242 if(FD_ISSET(k, setptr))
243 printf("bit set: %d\n", k);
249 //printf("read_fd %d\n", k);
250 if (k
== srv
->listensocket
) {
251 // new connection available
252 addrlen
= sizeof(remoteaddr
);
253 newfd
= accept(srv
->listensocket
, (struct sockaddr
*)&remoteaddr
, &addrlen
);
258 if(newfd
>= USER_MAX_FD
)
259 close(newfd
); // only USER_MAX_FD connections can be handled.
261 FD_SET(newfd
, &srv
->master
);
262 if (newfd
> srv
->maxfd
)
264 if(on_clientconnect
) on_clientconnect(srv
->userdata
, &remoteaddr
, newfd
);
268 if(buf
&& k
!= srv
->signalfd
) {
269 if ((nbytes
= recv(k
, buf
, bufsize
, 0)) <= 0) {
271 if(on_clientdisconnect
) on_clientdisconnect(srv
->userdata
, k
);
275 rocksockserver_disconnect_client(srv
, k
);
277 if(on_clientread
) on_clientread(srv
->userdata
, k
, nbytes
);
281 if(on_clientread
) on_clientread(srv
->userdata
, k
, 0);
288 //printf("write_fd %d\n", k);
289 if(on_clientwantsdata
) on_clientwantsdata(srv
->userdata
, k
);
292 if(srv
->numfds
> 0) goto nextfd
;
294 microsleep(srv
->sleeptime_us
);