1 |
capela |
98 |
// server.c |
2 |
|
|
// |
3 |
|
|
/**************************************************************************** |
4 |
|
|
liblscp - LinuxSampler Control Protocol API |
5 |
|
|
Copyright (C) 2004, rncbc aka Rui Nuno Capela. All rights reserved. |
6 |
|
|
|
7 |
|
|
This library is free software; you can redistribute it and/or |
8 |
|
|
modify it under the terms of the GNU Lesser General Public |
9 |
|
|
License as published by the Free Software Foundation; either |
10 |
|
|
version 2.1 of the License, or (at your option) any later version. |
11 |
|
|
|
12 |
|
|
This library is distributed in the hope that it will be useful, |
13 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 |
|
|
Lesser General Public License for more details. |
16 |
|
|
|
17 |
|
|
You should have received a copy of the GNU Lesser General Public |
18 |
|
|
License along with this library; if not, write to the Free Software |
19 |
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 |
|
|
|
21 |
|
|
*****************************************************************************/ |
22 |
|
|
|
23 |
|
|
#include "server.h" |
24 |
|
|
|
25 |
|
|
#define LSCP_SERVER_RETRY 3 // Maximum number of unanswered PING retries. |
26 |
|
|
#define LSCP_SERVER_SLEEP 30 // Period in seconds for watchdog wakeup. |
27 |
|
|
|
28 |
|
|
|
29 |
|
|
// Local prototypes. |
30 |
|
|
|
31 |
|
|
static lscp_connect_t *_lscp_connect_create (lscp_server_t *pServer, lscp_socket_t sock, struct sockaddr_in *pAddr, int cAddr); |
32 |
|
|
static lscp_status_t _lscp_connect_destroy (lscp_connect_t *pConnect); |
33 |
|
|
static lscp_status_t _lscp_connect_recv (lscp_connect_t *pConnect); |
34 |
|
|
static lscp_status_t _lscp_connect_send (lscp_connect_t *pConnect, const char *pchBuffer, int cchBuffer); |
35 |
|
|
static lscp_status_t _lscp_connect_ping (lscp_connect_t *pConnect); |
36 |
|
|
|
37 |
|
|
static void _lscp_connect_list_append (lscp_connect_list_t *pList, lscp_connect_t *pItem); |
38 |
|
|
static void _lscp_connect_list_remove (lscp_connect_list_t *pList, lscp_connect_t *pItem); |
39 |
|
|
static void _lscp_connect_list_remove_safe (lscp_connect_list_t *pList, lscp_connect_t *pItem); |
40 |
|
|
static void _lscp_connect_list_free (lscp_connect_list_t *pList); |
41 |
|
|
static lscp_connect_t *_lscp_connect_list_find_addr (lscp_connect_list_t *pList, struct sockaddr_in *pAddr); |
42 |
|
|
static lscp_connect_t *_lscp_connect_list_find_sock (lscp_connect_list_t *pList, lscp_socket_t sock); |
43 |
|
|
|
44 |
|
|
static lscp_status_t _lscp_server_udp_recv (lscp_server_t *pServer); |
45 |
|
|
|
46 |
|
|
static void _lscp_server_thread_proc (lscp_server_t *pServer); |
47 |
|
|
static void _lscp_server_select_proc (lscp_server_t *pServer); |
48 |
|
|
|
49 |
|
|
static void _lscp_server_tcp_proc (void *pvServer); |
50 |
|
|
static void _lscp_server_udp_proc (void *pvServer); |
51 |
|
|
|
52 |
|
|
static void _lscp_watchdog_scan (lscp_server_t *pServer); |
53 |
|
|
|
54 |
|
|
static void _lscp_watchdog_proc (void *pvServer); |
55 |
|
|
|
56 |
|
|
#if defined(WIN32) |
57 |
|
|
#include <time.h> |
58 |
|
|
#undef gettimeofday |
59 |
|
|
#define gettimeofday(p,n) {(p)->tv_sec = (long) (clock() / CLOCKS_PER_SEC); (p)->tv_usec = 0;} |
60 |
|
|
#else |
61 |
|
|
#include <sys/time.h> |
62 |
|
|
#endif |
63 |
|
|
|
64 |
|
|
//------------------------------------------------------------------------- |
65 |
|
|
// Server-side client connection list methods. |
66 |
|
|
|
67 |
|
|
static void _lscp_connect_list_init ( lscp_connect_list_t *pList ) |
68 |
|
|
{ |
69 |
|
|
// fprintf(stderr, "_lscp_connect_list_init: pList=%p.\n", pList); |
70 |
|
|
|
71 |
|
|
pList->first = NULL; |
72 |
|
|
pList->last = NULL; |
73 |
|
|
pList->count = 0; |
74 |
|
|
|
75 |
|
|
lscp_mutex_init(pList->mutex); |
76 |
|
|
} |
77 |
|
|
|
78 |
|
|
|
79 |
|
|
static void _lscp_connect_list_append ( lscp_connect_list_t *pList, lscp_connect_t *pItem ) |
80 |
|
|
{ |
81 |
|
|
// fprintf(stderr, "_lscp_connect_list_append: pList=%p pItem=%p.\n", pList, pItem); |
82 |
|
|
|
83 |
|
|
lscp_mutex_lock(pList->mutex); |
84 |
|
|
|
85 |
|
|
pItem->prev = pList->last; |
86 |
|
|
pItem->next = NULL; |
87 |
|
|
|
88 |
|
|
if (pList->last) |
89 |
|
|
(pList->last)->next = pItem; |
90 |
|
|
else |
91 |
|
|
pList->first = pItem; |
92 |
|
|
|
93 |
|
|
pList->last = pItem; |
94 |
|
|
|
95 |
|
|
pList->count++; |
96 |
|
|
|
97 |
|
|
lscp_mutex_unlock(pList->mutex); |
98 |
|
|
} |
99 |
|
|
|
100 |
|
|
|
101 |
|
|
static void _lscp_connect_list_remove ( lscp_connect_list_t *pList, lscp_connect_t *pItem ) |
102 |
|
|
{ |
103 |
|
|
// fprintf(stderr, "_lscp_connect_list_remove: pList=%p pItem=%p.\n", pList, pItem); |
104 |
|
|
|
105 |
|
|
if (pItem->next) |
106 |
|
|
(pItem->next)->prev = pItem->prev; |
107 |
|
|
else |
108 |
|
|
pList->last = pItem->prev; |
109 |
|
|
|
110 |
|
|
if (pItem->prev) |
111 |
|
|
(pItem->prev)->next = pItem->next; |
112 |
|
|
else |
113 |
|
|
pList->first = pItem->next; |
114 |
|
|
|
115 |
|
|
pItem->next = NULL; |
116 |
|
|
pItem->prev = NULL; |
117 |
|
|
|
118 |
|
|
pList->count--; |
119 |
|
|
} |
120 |
|
|
|
121 |
|
|
|
122 |
|
|
static void _lscp_connect_list_remove_safe ( lscp_connect_list_t *pList, lscp_connect_t *pItem ) |
123 |
|
|
{ |
124 |
|
|
lscp_connect_t *p; |
125 |
|
|
|
126 |
|
|
// fprintf(stderr, "_lscp_connect_list_remove_safe: pList=%p pItem=%p.\n", pList, pItem); |
127 |
|
|
|
128 |
|
|
lscp_mutex_lock(pList->mutex); |
129 |
|
|
|
130 |
|
|
for (p = pList->first; p; p = p->next) { |
131 |
|
|
if (p == pItem) { |
132 |
|
|
_lscp_connect_list_remove(pList, pItem); |
133 |
|
|
break; |
134 |
|
|
} |
135 |
|
|
} |
136 |
|
|
|
137 |
|
|
lscp_mutex_unlock(pList->mutex); |
138 |
|
|
} |
139 |
|
|
|
140 |
|
|
|
141 |
|
|
static void _lscp_connect_list_free ( lscp_connect_list_t *pList ) |
142 |
|
|
{ |
143 |
|
|
lscp_connect_t *p, *pNext; |
144 |
|
|
|
145 |
|
|
// fprintf(stderr, "_lscp_connect_list_free: pList=%p.\n", pList); |
146 |
|
|
|
147 |
|
|
lscp_mutex_lock(pList->mutex); |
148 |
|
|
|
149 |
|
|
for (p = pList->first; p; p = pNext) { |
150 |
|
|
pNext = p->next; |
151 |
|
|
_lscp_connect_list_remove(pList, p); |
152 |
|
|
_lscp_connect_destroy(p); |
153 |
|
|
} |
154 |
|
|
|
155 |
|
|
lscp_mutex_unlock(pList->mutex); |
156 |
|
|
|
157 |
|
|
lscp_mutex_destroy(pList->mutex); |
158 |
|
|
} |
159 |
|
|
|
160 |
|
|
|
161 |
|
|
static lscp_connect_t *_lscp_connect_list_find_addr ( lscp_connect_list_t *pList, struct sockaddr_in *pAddr ) |
162 |
|
|
{ |
163 |
|
|
int iPort = ntohs(pAddr->sin_port); |
164 |
|
|
const char *pszAddr = inet_ntoa(pAddr->sin_addr); |
165 |
|
|
lscp_connect_t *p; |
166 |
|
|
|
167 |
|
|
// fprintf(stderr, "_lscp_connect_list_find_addr: pList=%p addr=%s port=%d.\n", pList, inet_ntoa(pAddr->sin_addr), ntohs(pAddr->sin_port)); |
168 |
|
|
|
169 |
|
|
for (p = pList->first; p; p = p->next) { |
170 |
|
|
if (iPort == p->port && strcmp(pszAddr, inet_ntoa(p->client.addr.sin_addr)) == 0) |
171 |
|
|
return p; |
172 |
|
|
} |
173 |
|
|
|
174 |
|
|
return NULL; |
175 |
|
|
} |
176 |
|
|
|
177 |
|
|
|
178 |
|
|
static lscp_connect_t *_lscp_connect_list_find_sock ( lscp_connect_list_t *pList, lscp_socket_t sock ) |
179 |
|
|
{ |
180 |
|
|
lscp_connect_t *p; |
181 |
|
|
|
182 |
|
|
// fprintf(stderr, "_lscp_connect_list_find_sock: pList=%p sock=%d.\n", pList, sock); |
183 |
|
|
|
184 |
|
|
for (p = pList->first; p; p = p->next) { |
185 |
|
|
if (sock == p->client.sock) |
186 |
|
|
return p; |
187 |
|
|
} |
188 |
|
|
|
189 |
|
|
return NULL; |
190 |
|
|
} |
191 |
|
|
|
192 |
|
|
//------------------------------------------------------------------------- |
193 |
|
|
// Server-side threaded client connections. |
194 |
|
|
|
195 |
|
|
static void _lscp_connect_proc ( void *pvConnect ) |
196 |
|
|
{ |
197 |
|
|
lscp_connect_t *pConnect = (lscp_connect_t *) pvConnect; |
198 |
|
|
lscp_server_t *pServer = pConnect->server; |
199 |
|
|
|
200 |
|
|
while (pServer->tcp.iState && pConnect->client.iState) { |
201 |
|
|
if (_lscp_connect_recv(pConnect) != LSCP_OK) |
202 |
|
|
pConnect->client.iState = 0; |
203 |
|
|
} |
204 |
|
|
|
205 |
|
|
(*pServer->pfnCallback)(pConnect, NULL, LSCP_CONNECT_CLOSE, pServer->pvData); |
206 |
|
|
_lscp_connect_list_remove_safe(&(pServer->connects), pConnect); |
207 |
|
|
closesocket(pConnect->client.sock); |
208 |
|
|
} |
209 |
|
|
|
210 |
|
|
static lscp_connect_t *_lscp_connect_create ( lscp_server_t *pServer, lscp_socket_t sock, struct sockaddr_in *pAddr, int cAddr ) |
211 |
|
|
{ |
212 |
|
|
lscp_connect_t *pConnect; |
213 |
|
|
|
214 |
|
|
if (pServer == NULL || sock == INVALID_SOCKET || pAddr == NULL) { |
215 |
|
|
fprintf(stderr, "_lscp_connect_create: Invalid connection arguments.\n"); |
216 |
|
|
return NULL; |
217 |
|
|
} |
218 |
|
|
|
219 |
|
|
pConnect = (lscp_connect_t *) malloc(sizeof(lscp_connect_t)); |
220 |
|
|
if (pConnect == NULL) { |
221 |
|
|
fprintf(stderr, "_lscp_connect_create: Out of memory.\n"); |
222 |
|
|
closesocket(sock); |
223 |
|
|
return NULL; |
224 |
|
|
} |
225 |
|
|
memset(pConnect, 0, sizeof(lscp_connect_t)); |
226 |
|
|
|
227 |
|
|
pConnect->server = pServer; |
228 |
|
|
|
229 |
|
|
#ifdef DEBUG |
230 |
|
|
fprintf(stderr, "_lscp_connect_create: pConnect=%p: sock=%d addr=%s port=%d.\n", pConnect, sock, inet_ntoa(pAddr->sin_addr), ntohs(pAddr->sin_port)); |
231 |
|
|
#endif |
232 |
|
|
|
233 |
|
|
lscp_socket_agent_init(&(pConnect->client), sock, pAddr, cAddr); |
234 |
|
|
|
235 |
|
|
if (pServer->mode == LSCP_SERVER_THREAD) { |
236 |
|
|
if (lscp_socket_agent_start(&(pConnect->client), _lscp_connect_proc, pConnect, 0) != LSCP_OK) { |
237 |
|
|
closesocket(sock); |
238 |
|
|
free(pConnect); |
239 |
|
|
return NULL; |
240 |
|
|
} |
241 |
|
|
} |
242 |
|
|
|
243 |
|
|
return pConnect; |
244 |
|
|
} |
245 |
|
|
|
246 |
|
|
|
247 |
|
|
static lscp_status_t _lscp_connect_destroy ( lscp_connect_t *pConnect ) |
248 |
|
|
{ |
249 |
|
|
lscp_status_t ret = LSCP_FAILED; |
250 |
|
|
|
251 |
|
|
if (pConnect == NULL) |
252 |
|
|
return ret; |
253 |
|
|
|
254 |
|
|
#ifdef DEBUG |
255 |
|
|
fprintf(stderr, "_lscp_connect_destroy: pConnect=%p.\n", pConnect); |
256 |
|
|
#endif |
257 |
|
|
|
258 |
|
|
lscp_socket_agent_free(&(pConnect->client)); |
259 |
|
|
|
260 |
|
|
if (pConnect->sessid) |
261 |
|
|
free(pConnect->sessid); |
262 |
|
|
|
263 |
|
|
free(pConnect); |
264 |
|
|
|
265 |
|
|
return ret; |
266 |
|
|
} |
267 |
|
|
|
268 |
|
|
|
269 |
|
|
lscp_status_t _lscp_connect_recv ( lscp_connect_t *pConnect ) |
270 |
|
|
{ |
271 |
|
|
lscp_status_t ret = LSCP_FAILED; |
272 |
|
|
lscp_server_t *pServer; |
273 |
|
|
char achBuffer[LSCP_BUFSIZ]; |
274 |
|
|
int cchBuffer; |
275 |
|
|
|
276 |
|
|
if (pConnect == NULL) |
277 |
|
|
return ret; |
278 |
|
|
|
279 |
|
|
pServer = pConnect->server; |
280 |
|
|
if (pServer == NULL) |
281 |
|
|
return ret; |
282 |
|
|
|
283 |
|
|
cchBuffer = recv(pConnect->client.sock, achBuffer, sizeof(achBuffer), 0); |
284 |
|
|
if (cchBuffer > 0) |
285 |
|
|
ret = (*pServer->pfnCallback)(pConnect, achBuffer, cchBuffer, pServer->pvData); |
286 |
|
|
else if (cchBuffer < 0) |
287 |
|
|
lscp_socket_perror("_lscp_connect_recv: recv"); |
288 |
|
|
|
289 |
|
|
return ret; |
290 |
|
|
} |
291 |
|
|
|
292 |
|
|
|
293 |
|
|
static lscp_status_t _lscp_connect_send ( lscp_connect_t *pConnect, const char *pchBuffer, int cchBuffer ) |
294 |
|
|
{ |
295 |
|
|
lscp_status_t ret = LSCP_FAILED; |
296 |
|
|
struct sockaddr_in addr; |
297 |
|
|
int cAddr; |
298 |
|
|
|
299 |
|
|
if (pConnect == NULL) |
300 |
|
|
return ret; |
301 |
|
|
if (pchBuffer == NULL || cchBuffer < 1) |
302 |
|
|
return ret; |
303 |
|
|
if (pConnect->port == 0) |
304 |
|
|
return ret; |
305 |
|
|
|
306 |
|
|
cAddr = sizeof(struct sockaddr_in); |
307 |
|
|
memcpy((char *) &addr, (char *) &(pConnect->client.addr), cAddr); |
308 |
|
|
addr.sin_port = htons((short) pConnect->port); |
309 |
|
|
|
310 |
|
|
if (sendto((pConnect->server)->udp.sock, pchBuffer, cchBuffer, 0, (struct sockaddr *) &addr, cAddr) == cchBuffer) |
311 |
|
|
ret = LSCP_OK; |
312 |
|
|
else |
313 |
|
|
lscp_socket_perror("_lscp_connect_send: sendto"); |
314 |
|
|
|
315 |
|
|
return ret; |
316 |
|
|
} |
317 |
|
|
|
318 |
|
|
|
319 |
|
|
static lscp_status_t _lscp_connect_ping ( lscp_connect_t *pConnect ) |
320 |
|
|
{ |
321 |
|
|
char szBuffer[LSCP_BUFSIZ]; |
322 |
|
|
|
323 |
|
|
if (pConnect == NULL) |
324 |
|
|
return LSCP_FAILED; |
325 |
|
|
if (pConnect->sessid == NULL) |
326 |
|
|
return LSCP_FAILED; |
327 |
|
|
|
328 |
|
|
#ifdef DEBUG |
329 |
|
|
fprintf(stderr, "_lscp_connect_ping: pConnect=%p: addr=%s port=%d sessid=%s.\n", pConnect, inet_ntoa(pConnect->client.addr.sin_addr), pConnect->port, pConnect->sessid); |
330 |
|
|
#endif |
331 |
|
|
|
332 |
|
|
sprintf(szBuffer, "PING %d %s\r\n", ntohs((pConnect->server)->udp.addr.sin_port), pConnect->sessid); |
333 |
|
|
|
334 |
|
|
return _lscp_connect_send(pConnect, szBuffer, strlen(szBuffer)); |
335 |
|
|
} |
336 |
|
|
|
337 |
|
|
|
338 |
|
|
//------------------------------------------------------------------------- |
339 |
|
|
// TCP service (stream oriented). |
340 |
|
|
|
341 |
|
|
static lscp_status_t _lscp_server_udp_recv ( lscp_server_t *pServer ) |
342 |
|
|
{ |
343 |
|
|
lscp_status_t ret = LSCP_FAILED; |
344 |
|
|
struct sockaddr_in addr; |
345 |
|
|
int cAddr; |
346 |
|
|
char achBuffer[LSCP_BUFSIZ]; |
347 |
|
|
int cchBuffer; |
348 |
|
|
lscp_connect_t *pConnect; |
349 |
|
|
|
350 |
|
|
cAddr = sizeof(addr); |
351 |
|
|
cchBuffer = recvfrom(pServer->udp.sock, achBuffer, sizeof(achBuffer), 0, (struct sockaddr *) &addr, &cAddr); |
352 |
|
|
if (cchBuffer > 0) { |
353 |
|
|
#ifdef DEBUG |
354 |
|
|
lscp_socket_trace("_lscp_server_udp_recv: recvfrom", &addr, achBuffer, cchBuffer); |
355 |
|
|
#endif |
356 |
|
|
// Just do a simple check for PONG events (ignore sessid). |
357 |
|
|
if (strncmp(achBuffer, "PONG ", 5) == 0) { |
358 |
|
|
pConnect = _lscp_connect_list_find_addr(&(pServer->connects), &addr); |
359 |
|
|
if (pConnect) |
360 |
|
|
pConnect->ping = 0; |
361 |
|
|
} |
362 |
|
|
ret = LSCP_OK; |
363 |
|
|
} |
364 |
|
|
else lscp_socket_perror("_lscp_server_udp_recv: recvfrom"); |
365 |
|
|
|
366 |
|
|
return ret; |
367 |
|
|
} |
368 |
|
|
|
369 |
|
|
|
370 |
|
|
static void _lscp_server_thread_proc ( lscp_server_t *pServer ) |
371 |
|
|
{ |
372 |
|
|
lscp_socket_t sock; |
373 |
|
|
struct sockaddr_in addr; |
374 |
|
|
int cAddr; |
375 |
|
|
lscp_connect_t *pConnect; |
376 |
|
|
|
377 |
|
|
#ifdef DEBUG |
378 |
|
|
fprintf(stderr, "_lscp_server_thread_proc: Server listening for connections.\n"); |
379 |
|
|
#endif |
380 |
|
|
|
381 |
|
|
while (pServer->tcp.iState) { |
382 |
|
|
cAddr = sizeof(struct sockaddr_in); |
383 |
|
|
sock = accept(pServer->tcp.sock, (struct sockaddr *) &addr, &cAddr); |
384 |
|
|
if (sock == INVALID_SOCKET) { |
385 |
|
|
lscp_socket_perror("_lscp_server_thread_proc: accept"); |
386 |
|
|
pServer->tcp.iState = 0; |
387 |
|
|
} else { |
388 |
|
|
pConnect = _lscp_connect_create(pServer, sock, &addr, cAddr); |
389 |
|
|
if (pConnect) { |
390 |
|
|
_lscp_connect_list_append(&(pServer->connects), pConnect); |
391 |
|
|
(*pServer->pfnCallback)(pConnect, NULL, LSCP_CONNECT_OPEN, pServer->pvData); |
392 |
|
|
} |
393 |
|
|
} |
394 |
|
|
} |
395 |
|
|
|
396 |
|
|
#ifdef DEBUG |
397 |
|
|
fprintf(stderr, "_lscp_server_thread_proc: Server closing.\n"); |
398 |
|
|
#endif |
399 |
|
|
} |
400 |
|
|
|
401 |
|
|
|
402 |
|
|
static void _lscp_server_select_proc ( lscp_server_t *pServer ) |
403 |
|
|
{ |
404 |
|
|
fd_set master_fds; // Master file descriptor list. |
405 |
|
|
fd_set select_fds; // temp file descriptor list for select(). |
406 |
|
|
int fd, fdmax; // Maximum file descriptor number. |
407 |
|
|
struct timeval tv; // For specifying a timeout value. |
408 |
|
|
int iSelect; // Holds select return status. |
409 |
|
|
|
410 |
|
|
struct timeval tv1, tv2; // To compute delta timeouts. |
411 |
|
|
|
412 |
|
|
lscp_socket_t sock; |
413 |
|
|
struct sockaddr_in addr; |
414 |
|
|
int cAddr; |
415 |
|
|
lscp_connect_t *pConnect; |
416 |
|
|
|
417 |
|
|
#ifdef DEBUG |
418 |
|
|
fprintf(stderr, "_lscp_server_select_proc: Server listening for connections.\n"); |
419 |
|
|
#endif |
420 |
|
|
FD_ZERO(&master_fds); |
421 |
|
|
FD_ZERO(&select_fds); |
422 |
|
|
|
423 |
|
|
// Add the listeners to the master set |
424 |
|
|
FD_SET((unsigned int) pServer->tcp.sock, &master_fds); |
425 |
|
|
FD_SET((unsigned int) pServer->udp.sock, &master_fds); |
426 |
|
|
|
427 |
|
|
// Keep track of the biggest file descriptor; |
428 |
|
|
// So far, it's ourself, the listener. |
429 |
|
|
if ((int) pServer->udp.sock > (int) pServer->tcp.sock) |
430 |
|
|
fdmax = (int) pServer->udp.sock; |
431 |
|
|
else |
432 |
|
|
fdmax = (int) pServer->tcp.sock; |
433 |
|
|
|
434 |
|
|
// To start counting for regular watchdog simulation. |
435 |
|
|
gettimeofday(&tv1, NULL); |
436 |
|
|
gettimeofday(&tv2, NULL); |
437 |
|
|
|
438 |
|
|
// Main loop... |
439 |
|
|
while (pServer->tcp.iState) { |
440 |
|
|
|
441 |
|
|
// Use a copy of the master. |
442 |
|
|
select_fds = master_fds; |
443 |
|
|
// Use the timeout feature for watchdoggin. |
444 |
|
|
tv.tv_sec = LSCP_SERVER_SLEEP - (tv2.tv_sec - tv1.tv_sec); |
445 |
|
|
tv.tv_usec = 0; |
446 |
|
|
// Wait for events... |
447 |
|
|
iSelect = select(fdmax + 1, &select_fds, NULL, NULL, &tv); |
448 |
|
|
|
449 |
|
|
// Check later id it's time for ping time... |
450 |
|
|
gettimeofday(&tv2, NULL); |
451 |
|
|
|
452 |
|
|
if (iSelect < 0) { |
453 |
|
|
lscp_socket_perror("_lscp_server_select_proc: select"); |
454 |
|
|
pServer->tcp.iState = 0; |
455 |
|
|
} |
456 |
|
|
else if (iSelect > 0) { |
457 |
|
|
// Run through the existing connections looking for data to read... |
458 |
|
|
for (fd = 0; fd < fdmax + 1; fd++) { |
459 |
|
|
if (FD_ISSET(fd, &select_fds)) { // We got one!! |
460 |
|
|
// Is it ourselves, the TCP listener? |
461 |
|
|
if (fd == (int) pServer->tcp.sock) { |
462 |
|
|
// Accept the connection... |
463 |
|
|
cAddr = sizeof(struct sockaddr_in); |
464 |
|
|
sock = accept(pServer->tcp.sock, (struct sockaddr *) &addr, &cAddr); |
465 |
|
|
if (sock == INVALID_SOCKET) { |
466 |
|
|
lscp_socket_perror("_lscp_server_select_proc: accept"); |
467 |
|
|
pServer->tcp.iState = 0; |
468 |
|
|
} else { |
469 |
|
|
// Add to master set. |
470 |
|
|
FD_SET((unsigned int) sock, &master_fds); |
471 |
|
|
// Keep track of the maximum. |
472 |
|
|
if ((int) sock > fdmax) |
473 |
|
|
fdmax = (int) sock; |
474 |
|
|
// And do create the client connection entry. |
475 |
|
|
pConnect = _lscp_connect_create(pServer, sock, &addr, cAddr); |
476 |
|
|
if (pConnect) { |
477 |
|
|
_lscp_connect_list_append(&(pServer->connects), pConnect); |
478 |
|
|
(*pServer->pfnCallback)(pConnect, NULL, LSCP_CONNECT_OPEN, pServer->pvData); |
479 |
|
|
} |
480 |
|
|
} |
481 |
|
|
// Done with one new connection. |
482 |
|
|
} else if (fd == (int) pServer->udp.sock) { |
483 |
|
|
// Or to the UDP listener? |
484 |
|
|
if (_lscp_server_udp_recv(pServer) != LSCP_OK) |
485 |
|
|
pServer->tcp.iState = 0; |
486 |
|
|
// Done with UDP transaction. |
487 |
|
|
} else { |
488 |
|
|
// Otherwise it's trivial transaction... |
489 |
|
|
lscp_mutex_lock(pServer->connects.mutex); |
490 |
|
|
// Find the connection on our cache... |
491 |
|
|
pConnect = _lscp_connect_list_find_sock(&(pServer->connects), (lscp_socket_t) fd); |
492 |
|
|
// Handle data from a client. |
493 |
|
|
if (_lscp_connect_recv(pConnect) != LSCP_OK) { |
494 |
|
|
// Say bye bye! |
495 |
|
|
if (pConnect) { |
496 |
|
|
(*pServer->pfnCallback)(pConnect, NULL, LSCP_CONNECT_CLOSE, pServer->pvData); |
497 |
|
|
_lscp_connect_list_remove(&(pServer->connects), pConnect); |
498 |
|
|
_lscp_connect_destroy(pConnect); |
499 |
|
|
} |
500 |
|
|
// Remove from master set. |
501 |
|
|
FD_CLR((unsigned int) fd, &master_fds); |
502 |
|
|
} |
503 |
|
|
lscp_mutex_unlock(pServer->connects.mutex); |
504 |
|
|
} |
505 |
|
|
} |
506 |
|
|
} |
507 |
|
|
// Done (iSelect > 0) |
508 |
|
|
} |
509 |
|
|
|
510 |
|
|
// Maybe select has timed out? |
511 |
|
|
if (iSelect == 0 || (tv2.tv_sec - tv1.tv_sec > LSCP_SERVER_SLEEP)) { |
512 |
|
|
// Let the pseudo-watchdog do it's stuff... |
513 |
|
|
_lscp_watchdog_scan(pServer); |
514 |
|
|
// Make it a new start... |
515 |
|
|
gettimeofday(&tv1, NULL); |
516 |
|
|
} |
517 |
|
|
} |
518 |
|
|
|
519 |
|
|
#ifdef DEBUG |
520 |
|
|
fprintf(stderr, "_lscp_server_select_proc: Server closing.\n"); |
521 |
|
|
#endif |
522 |
|
|
} |
523 |
|
|
|
524 |
|
|
|
525 |
|
|
static void _lscp_server_tcp_proc ( void *pvServer ) |
526 |
|
|
{ |
527 |
|
|
lscp_server_t *pServer = (lscp_server_t *) pvServer; |
528 |
|
|
|
529 |
|
|
if (pServer->mode == LSCP_SERVER_THREAD) |
530 |
|
|
_lscp_server_thread_proc(pServer); |
531 |
|
|
else |
532 |
|
|
_lscp_server_select_proc(pServer); |
533 |
|
|
} |
534 |
|
|
|
535 |
|
|
|
536 |
|
|
//------------------------------------------------------------------------- |
537 |
|
|
// UDP service (datagram oriented). |
538 |
|
|
|
539 |
|
|
static void _lscp_server_udp_proc ( void *pvServer ) |
540 |
|
|
{ |
541 |
|
|
lscp_server_t *pServer = (lscp_server_t *) pvServer; |
542 |
|
|
|
543 |
|
|
#ifdef DEBUG |
544 |
|
|
fprintf(stderr, "_lscp_server_udp_proc: Server waiting for events.\n"); |
545 |
|
|
#endif |
546 |
|
|
|
547 |
|
|
while (pServer->udp.iState) { |
548 |
|
|
if (_lscp_server_udp_recv(pServer) != LSCP_OK) |
549 |
|
|
pServer->udp.iState = 0; |
550 |
|
|
} |
551 |
|
|
|
552 |
|
|
#ifdef DEBUG |
553 |
|
|
fprintf(stderr, "_lscp_server_udp_proc: Server closing.\n"); |
554 |
|
|
#endif |
555 |
|
|
} |
556 |
|
|
|
557 |
|
|
|
558 |
|
|
//------------------------------------------------------------------------- |
559 |
|
|
// Watchdog thread procedure loop. |
560 |
|
|
|
561 |
|
|
static void _lscp_watchdog_scan ( lscp_server_t *pServer ) |
562 |
|
|
{ |
563 |
|
|
lscp_connect_t *p, *pNext; |
564 |
|
|
|
565 |
|
|
lscp_mutex_lock(pServer->connects.mutex); |
566 |
|
|
|
567 |
|
|
for (p = pServer->connects.first; p; p = pNext) { |
568 |
|
|
pNext = p->next; |
569 |
|
|
if (p->port > 0) { |
570 |
|
|
if (p->ping >= LSCP_SERVER_RETRY) { |
571 |
|
|
fprintf(stderr, "_lscp_watchdog_scan: addr=%s port=%d: Zombie connection about to close.\n", inet_ntoa(p->client.addr.sin_addr), ntohs(p->client.addr.sin_port)); |
572 |
|
|
_lscp_connect_list_remove(&(pServer->connects), p); |
573 |
|
|
_lscp_connect_destroy(p); |
574 |
|
|
} else { |
575 |
|
|
p->ping++; |
576 |
|
|
_lscp_connect_ping(p); |
577 |
|
|
} |
578 |
|
|
} |
579 |
|
|
} |
580 |
|
|
|
581 |
|
|
lscp_mutex_unlock(pServer->connects.mutex); |
582 |
|
|
} |
583 |
|
|
|
584 |
|
|
|
585 |
|
|
static void _lscp_watchdog_proc ( void *pvServer ) |
586 |
|
|
{ |
587 |
|
|
lscp_server_t *pServer = (lscp_server_t *) pvServer; |
588 |
|
|
|
589 |
|
|
#ifdef DEBUG |
590 |
|
|
fprintf(stderr, "_lscp_watchdog_proc: Watchdog thread started.\n"); |
591 |
|
|
#endif |
592 |
|
|
|
593 |
|
|
while (pServer->iWatchdog) { |
594 |
|
|
|
595 |
|
|
#if defined(WIN32) |
596 |
|
|
Sleep(pServer->iSleep * 1000); |
597 |
|
|
#else |
598 |
|
|
sleep(pServer->iSleep); |
599 |
|
|
#endif |
600 |
|
|
_lscp_watchdog_scan(pServer); |
601 |
|
|
} |
602 |
|
|
|
603 |
|
|
#ifdef DEBUG |
604 |
|
|
fprintf(stderr, "_lscp_watchdog_proc: Watchdog thread terminated.\n"); |
605 |
|
|
#endif |
606 |
|
|
} |
607 |
|
|
|
608 |
|
|
|
609 |
|
|
//------------------------------------------------------------------------- |
610 |
|
|
// Server versioning teller fuunction. |
611 |
|
|
|
612 |
|
|
/** Retrieve the current server library version string. */ |
613 |
|
|
const char* lscp_server_package (void) { return LSCP_PACKAGE; } |
614 |
|
|
|
615 |
|
|
/** Retrieve the current server library version string. */ |
616 |
|
|
const char* lscp_server_version (void) { return LSCP_VERSION; } |
617 |
|
|
|
618 |
|
|
/** Retrieve the current server library build timestamp string. */ |
619 |
|
|
const char* lscp_server_build (void) { return __DATE__ " " __TIME__; } |
620 |
|
|
|
621 |
|
|
|
622 |
|
|
//------------------------------------------------------------------------- |
623 |
|
|
// Server sockets. |
624 |
|
|
|
625 |
|
|
/** |
626 |
|
|
* Create a server instance, listening on the given port for client |
627 |
|
|
* connections. A server callback function must be suplied that will |
628 |
|
|
* handle every and each client request. |
629 |
|
|
* |
630 |
|
|
* @param iPort Port number where the server will bind for listening. |
631 |
|
|
* @param pfnCallback Callback function to receive and handle client requests. |
632 |
|
|
* @param pvData Server context opaque data, that will be passed |
633 |
|
|
* to the callback function without change. |
634 |
|
|
* |
635 |
|
|
* @returns The new server instance pointer @ref lscp_server_t if successfull, |
636 |
|
|
* which shall be used on all subsequent server calls, NULL otherwise. |
637 |
|
|
*/ |
638 |
|
|
lscp_server_t* lscp_server_create ( int iPort, lscp_server_proc_t pfnCallback, void *pvData ) |
639 |
|
|
{ |
640 |
|
|
return lscp_server_create_ex(iPort, pfnCallback, pvData, LSCP_SERVER_SELECT); |
641 |
|
|
} |
642 |
|
|
|
643 |
|
|
|
644 |
|
|
/** |
645 |
|
|
* Create a server instance, listening on the given port for client |
646 |
|
|
* connections. A server callback function must be suplied that will |
647 |
|
|
* handle every and each client request. A server threading model |
648 |
|
|
* maybe specified either as multi-threaded (one thread per client) |
649 |
|
|
* or single thread multiplex mode (one thread serves all clients). |
650 |
|
|
* |
651 |
|
|
* @param iPort Port number where the server will bind for listening. |
652 |
|
|
* @param pfnCallback Callback function to receive and handle client requests. |
653 |
|
|
* @param pvData Server context opaque data, that will be passed |
654 |
|
|
* to the callback function without change. |
655 |
|
|
* @param mode Server mode of operation, regarding the internal |
656 |
|
|
* threading model, either @ref LSCP_SERVER_THREAD for |
657 |
|
|
* a multi-threaded server, or @ref LSCP_SERVER_SELECT |
658 |
|
|
* for a single-threaded multiplexed server. |
659 |
|
|
* |
660 |
|
|
* @returns The new server instance pointer if successfull, which shall be |
661 |
|
|
* used on all subsequent server calls, NULL otherwise. |
662 |
|
|
*/ |
663 |
|
|
lscp_server_t* lscp_server_create_ex ( int iPort, lscp_server_proc_t pfnCallback, void *pvData, lscp_server_mode_t mode ) |
664 |
|
|
{ |
665 |
|
|
lscp_server_t *pServer; |
666 |
|
|
lscp_socket_t sock; |
667 |
|
|
struct sockaddr_in addr; |
668 |
|
|
int cAddr; |
669 |
|
|
int iSockOpt = (-1); |
670 |
|
|
|
671 |
|
|
if (pfnCallback == NULL) { |
672 |
|
|
fprintf(stderr, "lscp_server_create: Invalid server callback function.\n"); |
673 |
|
|
return NULL; |
674 |
|
|
} |
675 |
|
|
|
676 |
|
|
// Allocate server descriptor... |
677 |
|
|
|
678 |
|
|
pServer = (lscp_server_t *) malloc(sizeof(lscp_server_t)); |
679 |
|
|
if (pServer == NULL) { |
680 |
|
|
fprintf(stderr, "lscp_server_create: Out of memory.\n"); |
681 |
|
|
return NULL; |
682 |
|
|
} |
683 |
|
|
memset(pServer, 0, sizeof(lscp_server_t)); |
684 |
|
|
|
685 |
|
|
_lscp_connect_list_init(&(pServer->connects)); |
686 |
|
|
|
687 |
|
|
pServer->mode = mode; |
688 |
|
|
pServer->pfnCallback = pfnCallback; |
689 |
|
|
pServer->pvData = pvData; |
690 |
|
|
|
691 |
|
|
#ifdef DEBUG |
692 |
|
|
fprintf(stderr, "lscp_server_create: pServer=%p: iPort=%d.\n", pServer, iPort); |
693 |
|
|
#endif |
694 |
|
|
|
695 |
|
|
// Prepare the TCP stream server socket... |
696 |
|
|
|
697 |
|
|
sock = socket(AF_INET, SOCK_STREAM, 0); |
698 |
|
|
if (sock == INVALID_SOCKET) { |
699 |
|
|
lscp_socket_perror("lscp_server_create: tcp: socket"); |
700 |
|
|
free(pServer); |
701 |
|
|
return NULL; |
702 |
|
|
} |
703 |
|
|
|
704 |
|
|
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) |
705 |
|
|
lscp_socket_perror("lscp_server_create: tcp: setsockopt(SO_REUSEADDR)"); |
706 |
|
|
#if defined(WIN32) |
707 |
|
|
if (setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) |
708 |
|
|
lscp_socket_perror("lscp_server_create: tcp: setsockopt(SO_DONTLINGER)"); |
709 |
|
|
#endif |
710 |
|
|
|
711 |
|
|
#ifdef DEBUG |
712 |
|
|
lscp_socket_getopts("lscp_server_create: tcp", sock); |
713 |
|
|
#endif |
714 |
|
|
|
715 |
|
|
cAddr = sizeof(struct sockaddr_in); |
716 |
|
|
memset((char *) &addr, 0, cAddr); |
717 |
|
|
addr.sin_family = AF_INET; |
718 |
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY); |
719 |
|
|
addr.sin_port = htons((short) iPort); |
720 |
|
|
|
721 |
|
|
if (bind(sock, (const struct sockaddr *) &addr, cAddr) == SOCKET_ERROR) { |
722 |
|
|
lscp_socket_perror("lscp_server_create: tcp: bind"); |
723 |
|
|
closesocket(sock); |
724 |
|
|
free(pServer); |
725 |
|
|
return NULL; |
726 |
|
|
} |
727 |
|
|
|
728 |
|
|
if (listen(sock, 10) == SOCKET_ERROR) { |
729 |
|
|
lscp_socket_perror("lscp_server_create: tcp: listen"); |
730 |
|
|
closesocket(sock); |
731 |
|
|
free(pServer); |
732 |
|
|
return NULL; |
733 |
|
|
} |
734 |
|
|
|
735 |
|
|
if (iPort == 0) { |
736 |
|
|
if (getsockname(sock, (struct sockaddr *) &addr, &cAddr) == SOCKET_ERROR) { |
737 |
|
|
lscp_socket_perror("lscp_server_create: tcp: getsockname"); |
738 |
|
|
closesocket(sock); |
739 |
|
|
free(pServer); |
740 |
|
|
} |
741 |
|
|
// Make TCP and UDP ports equal? |
742 |
|
|
iPort = ntohs(addr.sin_port); |
743 |
|
|
} |
744 |
|
|
|
745 |
|
|
lscp_socket_agent_init(&(pServer->tcp), sock, &addr, cAddr); |
746 |
|
|
|
747 |
|
|
#ifdef DEBUG |
748 |
|
|
fprintf(stderr, "lscp_server_create: tcp: sock=%d addr=%s port=%d.\n", pServer->tcp.sock, inet_ntoa(pServer->tcp.addr.sin_addr), ntohs(pServer->tcp.addr.sin_port)); |
749 |
|
|
#endif |
750 |
|
|
|
751 |
|
|
// Prepare the UDP datagram server socket... |
752 |
|
|
|
753 |
|
|
sock = socket(AF_INET, SOCK_DGRAM, 0); |
754 |
|
|
if (sock == INVALID_SOCKET) { |
755 |
|
|
lscp_socket_perror("lscp_server_create: udp: socket"); |
756 |
|
|
lscp_socket_agent_free(&(pServer->tcp)); |
757 |
|
|
free(pServer); |
758 |
|
|
return NULL; |
759 |
|
|
} |
760 |
|
|
|
761 |
|
|
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) |
762 |
|
|
lscp_socket_perror("lscp_server_create: udp: setsockopt(SO_REUSEADDR)"); |
763 |
|
|
|
764 |
|
|
#ifdef DEBUG |
765 |
|
|
lscp_socket_getopts("lscp_server_create: udp", sock); |
766 |
|
|
#endif |
767 |
|
|
|
768 |
|
|
cAddr = sizeof(struct sockaddr_in); |
769 |
|
|
memset((char *) &addr, 0, cAddr); |
770 |
|
|
addr.sin_family = AF_INET; |
771 |
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY); |
772 |
|
|
addr.sin_port = htons((short) iPort); |
773 |
|
|
|
774 |
|
|
if (bind(sock, (const struct sockaddr *) &addr, cAddr) == SOCKET_ERROR) { |
775 |
|
|
lscp_socket_perror("lscp_server_create: udp: bind"); |
776 |
|
|
lscp_socket_agent_free(&(pServer->tcp)); |
777 |
|
|
closesocket(sock); |
778 |
|
|
free(pServer); |
779 |
|
|
return NULL; |
780 |
|
|
} |
781 |
|
|
|
782 |
|
|
if (iPort == 0) { |
783 |
|
|
if (getsockname(sock, (struct sockaddr *) &addr, &cAddr) == SOCKET_ERROR) { |
784 |
|
|
lscp_socket_perror("lscp_server_create: udp: getsockname"); |
785 |
|
|
lscp_socket_agent_free(&(pServer->tcp)); |
786 |
|
|
closesocket(sock); |
787 |
|
|
free(pServer); |
788 |
|
|
return NULL; |
789 |
|
|
} |
790 |
|
|
} |
791 |
|
|
|
792 |
|
|
lscp_socket_agent_init(&(pServer->udp), sock, &addr, cAddr); |
793 |
|
|
|
794 |
|
|
#ifdef DEBUG |
795 |
|
|
fprintf(stderr, "lscp_server_create: udp: sock=%d addr=%s port=%d.\n", pServer->udp.sock, inet_ntoa(pServer->udp.addr.sin_addr), ntohs(pServer->udp.addr.sin_port)); |
796 |
|
|
#endif |
797 |
|
|
|
798 |
|
|
// Now's finally time to startup threads... |
799 |
|
|
|
800 |
|
|
// TCP/Main service thread... |
801 |
|
|
if (lscp_socket_agent_start(&(pServer->tcp), _lscp_server_tcp_proc, pServer, 0) != LSCP_OK) { |
802 |
|
|
lscp_socket_agent_free(&(pServer->tcp)); |
803 |
|
|
lscp_socket_agent_free(&(pServer->udp)); |
804 |
|
|
free(pServer); |
805 |
|
|
return NULL; |
806 |
|
|
} |
807 |
|
|
|
808 |
|
|
if (pServer->mode == LSCP_SERVER_THREAD) { |
809 |
|
|
// UDP service thread... |
810 |
|
|
if (lscp_socket_agent_start(&(pServer->udp), _lscp_server_udp_proc, pServer, 0) != LSCP_OK) { |
811 |
|
|
lscp_socket_agent_free(&(pServer->tcp)); |
812 |
|
|
lscp_socket_agent_free(&(pServer->udp)); |
813 |
|
|
free(pServer); |
814 |
|
|
return NULL; |
815 |
|
|
} |
816 |
|
|
// Watchdog thread... |
817 |
|
|
pServer->iWatchdog = 1; |
818 |
|
|
pServer->iSleep = LSCP_SERVER_SLEEP; |
819 |
|
|
pServer->pWatchdog = lscp_thread_create(_lscp_watchdog_proc, pServer, 0); |
820 |
|
|
} |
821 |
|
|
|
822 |
|
|
// Finally we've some success... |
823 |
|
|
return pServer; |
824 |
|
|
} |
825 |
|
|
|
826 |
|
|
|
827 |
|
|
/** |
828 |
|
|
* Wait for a server instance to terminate graciously. |
829 |
|
|
* |
830 |
|
|
* @param pServer Pointer to server instance structure. |
831 |
|
|
*/ |
832 |
|
|
lscp_status_t lscp_server_join ( lscp_server_t *pServer ) |
833 |
|
|
{ |
834 |
|
|
if (pServer == NULL) |
835 |
|
|
return LSCP_FAILED; |
836 |
|
|
|
837 |
|
|
#ifdef DEBUG |
838 |
|
|
fprintf(stderr, "lscp_server_join: pServer=%p.\n", pServer); |
839 |
|
|
#endif |
840 |
|
|
|
841 |
|
|
// if (pServer->mode == LSCP_SERVER_THREAD) { |
842 |
|
|
// lscp_thread_join(pServer->pWatchdog); |
843 |
|
|
// lscp_socket_agent_join(&(pServer->udp)); |
844 |
|
|
// } |
845 |
|
|
lscp_socket_agent_join(&(pServer->tcp)); |
846 |
|
|
|
847 |
|
|
return LSCP_OK; |
848 |
|
|
} |
849 |
|
|
|
850 |
|
|
|
851 |
|
|
/** |
852 |
|
|
* Terminate and destroy a server instance. |
853 |
|
|
* |
854 |
|
|
* @param pServer Pointer to server instance structure. |
855 |
|
|
*/ |
856 |
|
|
lscp_status_t lscp_server_destroy ( lscp_server_t *pServer ) |
857 |
|
|
{ |
858 |
|
|
if (pServer == NULL) |
859 |
|
|
return LSCP_FAILED; |
860 |
|
|
|
861 |
|
|
#ifdef DEBUG |
862 |
|
|
fprintf(stderr, "lscp_server_destroy: pServer=%p.\n", pServer); |
863 |
|
|
#endif |
864 |
|
|
|
865 |
|
|
if (pServer->mode == LSCP_SERVER_THREAD) { |
866 |
|
|
pServer->iWatchdog = 0; |
867 |
|
|
lscp_thread_destroy(pServer->pWatchdog); |
868 |
|
|
} |
869 |
|
|
lscp_socket_agent_free(&(pServer->udp)); |
870 |
|
|
lscp_socket_agent_free(&(pServer->tcp)); |
871 |
|
|
_lscp_connect_list_free(&(pServer->connects)); |
872 |
|
|
|
873 |
|
|
free(pServer); |
874 |
|
|
|
875 |
|
|
return LSCP_OK; |
876 |
|
|
} |
877 |
|
|
|
878 |
|
|
|
879 |
|
|
/** |
880 |
|
|
* Send an event message to all subscribed clients. |
881 |
|
|
* |
882 |
|
|
* @param pServer Pointer to server instance structure. |
883 |
|
|
* @param pchBuffer Pointer to data to be sent to all clients. |
884 |
|
|
* @param cchBuffer Length of the data to be sent in bytes. |
885 |
|
|
* |
886 |
|
|
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
887 |
|
|
*/ |
888 |
|
|
lscp_status_t lscp_server_broadcast ( lscp_server_t *pServer, const char *pchBuffer, int cchBuffer ) |
889 |
|
|
{ |
890 |
|
|
lscp_connect_t *p; |
891 |
|
|
|
892 |
|
|
if (pServer == NULL) |
893 |
|
|
return LSCP_FAILED; |
894 |
|
|
if (pchBuffer == NULL || cchBuffer < 1) |
895 |
|
|
return LSCP_FAILED; |
896 |
|
|
|
897 |
|
|
lscp_mutex_lock(pServer->connects.mutex); |
898 |
|
|
|
899 |
|
|
for (p = pServer->connects.first; p; p = p->next) { |
900 |
|
|
if (p->port > 0 && p->ping == 0) |
901 |
|
|
_lscp_connect_send(p, pchBuffer, cchBuffer); |
902 |
|
|
} |
903 |
|
|
|
904 |
|
|
lscp_mutex_unlock(pServer->connects.mutex); |
905 |
|
|
|
906 |
|
|
return LSCP_OK; |
907 |
|
|
} |
908 |
|
|
|
909 |
|
|
|
910 |
|
|
/** |
911 |
|
|
* Send response for the current client callback request. |
912 |
|
|
* |
913 |
|
|
* @param pConnect Pointer to client connection instance structure. |
914 |
|
|
* @param pchBuffer Pointer to data to be sent to the client as response. |
915 |
|
|
* @param cchBuffer Length of the response data to be sent in bytes. |
916 |
|
|
* |
917 |
|
|
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
918 |
|
|
*/ |
919 |
|
|
lscp_status_t lscp_server_result ( lscp_connect_t *pConnect, const char *pchBuffer, int cchBuffer ) |
920 |
|
|
{ |
921 |
|
|
lscp_status_t ret = LSCP_FAILED; |
922 |
|
|
|
923 |
|
|
if (pConnect == NULL) |
924 |
|
|
return ret; |
925 |
|
|
if (pchBuffer == NULL || cchBuffer < 1) |
926 |
|
|
return ret; |
927 |
|
|
|
928 |
|
|
if (send(pConnect->client.sock, pchBuffer, cchBuffer, 0) != cchBuffer) |
929 |
|
|
lscp_socket_perror("lscp_server_result"); |
930 |
|
|
else |
931 |
|
|
ret = LSCP_OK; |
932 |
|
|
|
933 |
|
|
return ret; |
934 |
|
|
} |
935 |
|
|
|
936 |
|
|
|
937 |
|
|
/** |
938 |
|
|
* Register client as a subscriber of event broadcast messages. |
939 |
|
|
* |
940 |
|
|
* @param pConnect Pointer to client connection instance structure. |
941 |
|
|
* @param iPort UDP port number of the requesting client connection. |
942 |
|
|
* |
943 |
|
|
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
944 |
|
|
*/ |
945 |
|
|
lscp_status_t lscp_server_subscribe ( lscp_connect_t *pConnect, int iPort ) |
946 |
|
|
{ |
947 |
|
|
char szSessID[32]; |
948 |
|
|
|
949 |
|
|
if (pConnect == NULL) |
950 |
|
|
return LSCP_FAILED; |
951 |
|
|
if (iPort == 0 || pConnect->port > 0 || pConnect->sessid) |
952 |
|
|
return LSCP_FAILED; |
953 |
|
|
|
954 |
|
|
// Generate a psudo-unique session-id. |
955 |
|
|
sprintf(szSessID, "%08x", ((unsigned int) pConnect->server << 8) ^ (unsigned int) pConnect); |
956 |
|
|
|
957 |
|
|
pConnect->port = iPort; |
958 |
|
|
pConnect->ping = 0; |
959 |
|
|
pConnect->sessid = strdup(szSessID); |
960 |
|
|
|
961 |
|
|
return _lscp_connect_ping(pConnect); |
962 |
|
|
} |
963 |
|
|
|
964 |
|
|
|
965 |
|
|
/** |
966 |
|
|
* Deregister client as subscriber of event broadcast messages. |
967 |
|
|
* |
968 |
|
|
* @param pConnect Pointer to client connection instance structure. |
969 |
|
|
* @param pszSessID Session identifier of the requesting client connection. |
970 |
|
|
* |
971 |
|
|
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
972 |
|
|
*/ |
973 |
|
|
lscp_status_t lscp_server_unsubscribe ( lscp_connect_t *pConnect, const char *pszSessID ) |
974 |
|
|
{ |
975 |
|
|
if (pConnect == NULL) |
976 |
|
|
return LSCP_FAILED; |
977 |
|
|
if (pConnect->port == 0 || pConnect->sessid == NULL) |
978 |
|
|
return LSCP_FAILED; |
979 |
|
|
|
980 |
|
|
// Session ids must match. |
981 |
|
|
if (strcmp(pszSessID, pConnect->sessid) != 0) |
982 |
|
|
return LSCP_FAILED; |
983 |
|
|
|
984 |
|
|
free(pConnect->sessid); |
985 |
|
|
pConnect->sessid = NULL; |
986 |
|
|
pConnect->ping = 0; |
987 |
|
|
pConnect->port = 0; |
988 |
|
|
|
989 |
|
|
return LSCP_OK; |
990 |
|
|
} |
991 |
|
|
|
992 |
|
|
|
993 |
|
|
// end of server.c |