/[svn]/liblscp/trunk/examples/server.c
ViewVC logotype

Contents of /liblscp/trunk/examples/server.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 98 - (show annotations) (download)
Tue Jun 1 11:13:56 2004 UTC (19 years, 10 months ago) by capela
File MIME type: text/plain
File size: 32004 byte(s)
Initial alpha release.

1 // 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

  ViewVC Help
Powered by ViewVC