iemnet-0.1/0000755000175100001440000000000011456321232012376 5ustar zmoelnigusersiemnet-0.1/tcpserver-help.pd0000644000175100001440000001335511453713371015703 0ustar zmoelnigusers#N canvas 3 45 909 548 12; #X floatatom 118 299 5 0 0 0 - - -; #X floatatom 142 329 5 0 0 0 - - -; #X floatatom 166 379 3 0 0 0 - - -; #X floatatom 193 379 3 0 0 0 - - -; #X floatatom 220 379 3 0 0 0 - - -; #X floatatom 247 379 3 0 0 0 - - -; #X text 129 378 from; #X text 34 299 connections; #X msg 209 110 broadcast 1 2 3; #X text 326 108 send to all clients; #X text 72 329 on socket; #X text 202 265 argument is port number; #X text 92 490 ***WARNING*** Attempting to print long messages can hang pd!; #X obj 95 430 spigot; #X obj 134 410 tgl 15 0 empty empty enable_print 17 7 0 10 -24198 -258699 -45076 0 1; #X obj 95 266 tcpserver 9997; #X text 21 6 [tcpserver] waits for clients to connect to its port. ; #X text 94 473 Received messages are output as stream of bytes; #X obj 95 453 print tcpserver:received; #X msg 234 135 11 216 43; #X obj 324 428 spigot; #X obj 363 408 tgl 15 0 empty empty enable_print 17 7 0 10 -24198 -258699 -45076 0 1; #X obj 324 454 print tcpserver:status; #N canvas 3 45 450 300 per.socket 0; #X obj 93 254 outlet; #X msg 93 154 disconnectsocket \$1; #X floatatom 111 108 5 0 0 0 - - -; #X obj 93 131 f; #X obj 93 112 bng 15 250 50 0 empty empty empty 17 7 0 10 -258699 -241291 -1; #X msg 123 184 send 504 1 2 3; #X text 233 183 send to client on socket 504; #X text 242 153 disconnect by socket number; #X connect 1 0 0 0; #X connect 2 0 3 1; #X connect 3 0 1 0; #X connect 4 0 3 0; #X connect 5 0 0 0; #X restore 88 91 pd per.socket; #N canvas 3 45 492 322 per.client 0; #X obj 20 224 outlet; #X msg 20 81 disconnectclient \$1; #X floatatom 38 32 5 0 0 0 - - -; #X obj 20 57 f; #X obj 20 38 bng 15 250 50 0 empty empty empty 17 7 0 10 -258699 -241291 -1; #X msg 75 136 client; #X msg 50 111 client 1; #X text 118 110 get state of client 1; #X text 127 136 get state of all clients (list on right outlet); #X text 162 81 disconnect by client number; #X msg 100 161 client 1 1 2 3 4; #X text 228 160 send (binary) 1 2 3 4 to client 1; #X connect 1 0 0 0; #X connect 2 0 3 1; #X connect 3 0 1 0; #X connect 4 0 3 0; #X connect 5 0 0 0; #X connect 6 0 0 0; #X connect 10 0 0 0; #X restore 38 66 pd per.client; #X msg 245 227 disconnect; #X text 325 225 disconnect all clients; #N canvas 313 127 698 341 default.targets 1; #X obj 106 268 outlet; #X msg 106 82 target 1; #X msg 157 240 64 64 64 10; #X text 173 83 per default \, send only to client #1; #X text 244 241 send to the default target; #X msg 63 43 target; #X msg 116 112 target -2; #X text 187 113 per default \, send to all clients but #2; #X msg 133 156 targetsocket 7; #X text 245 154 per default \, send only to client on socket 7; #X msg 143 186 targetsocket -504; #X text 272 186 per default \, send to all clients but the one on socket 504; #X text 120 41 per default \, send to all connected clients (aka 'broadcast') ; #X connect 1 0 0 0; #X connect 2 0 0 0; #X connect 5 0 0 0; #X connect 6 0 0 0; #X connect 8 0 0 0; #X connect 10 0 0 0; #X restore 247 174 pd default.targets; #X text 308 139 without specified method 'broadcast' is assumed (see default.targets); #N canvas 3 45 768 526 getting.info 0; #X floatatom 99 162 5 0 0 0 - - -; #X text 139 163 to_client; #X floatatom 390 165 3 0 0 0 - - -; #X symbolatom 422 140 15 0 0 0 - - -; #X floatatom 359 189 5 0 0 0 - - -; #X floatatom 454 115 7 0 0 0 - - -; #X floatatom 180 117 5 0 0 0 - - -; #X text 223 118 on_socket; #X floatatom 139 141 5 0 0 0 - - -; #X obj 99 62 route sent; #X obj 359 61 route client; #X text 533 141 ip; #X text 505 115 port; #X text 416 165 socket; #X text 400 191 client; #X text 184 140 sent_bytes; #X floatatom 99 342 5 0 0 0 - - -; #X floatatom 180 297 5 0 0 0 - - -; #X floatatom 139 321 5 0 0 0 - - -; #X obj 99 242 route bufsize; #X floatatom 93 501 3 0 0 0 - - -; #X floatatom 147 501 3 0 0 0 - - -; #X floatatom 120 501 3 0 0 0 - - -; #X obj 93 421 route address; #X obj 99 271 unpack f f f; #X obj 359 87 unpack f f s f; #X obj 99 91 unpack f f f; #X obj 93 450 unpack f f f f f; #X floatatom 174 501 3 0 0 0 - - -; #X floatatom 202 475 6 0 0 0 - - -; #X text 200 501 ip; #X text 248 475 port; #X text 139 342 socket; #X text 178 322 insize (bytes); #X text 218 298 outsize (bytes); #X text 361 261 info about current inqueue/outqueue; #X obj 99 216 r \$0.tcpserver.o5; #X obj 93 395 r \$0.tcpserver.o5; #X obj 99 36 r \$0.tcpserver.o5; #X obj 359 35 r \$0.tcpserver.o5; #X text 354 435 NOTE: outlets #3 and #4 are for compatibility with mrpeach's net obects. You can get all information via outlet #5 as well; #X connect 9 0 26 0; #X connect 10 0 25 0; #X connect 19 0 24 0; #X connect 23 0 27 0; #X connect 24 0 16 0; #X connect 24 1 18 0; #X connect 24 2 17 0; #X connect 25 0 4 0; #X connect 25 1 2 0; #X connect 25 2 3 0; #X connect 25 3 5 0; #X connect 26 0 0 0; #X connect 26 1 8 0; #X connect 26 2 6 0; #X connect 27 0 20 0; #X connect 27 1 22 0; #X connect 27 2 21 0; #X connect 27 3 28 0; #X connect 27 4 29 0; #X connect 36 0 19 0; #X connect 37 0 23 0; #X connect 38 0 9 0; #X connect 39 0 10 0; #X restore 589 375 pd getting.info; #X text 602 456 copyright (c) 2009 Martin Peach; #X text 602 473 copyright (c) 2010 Roman Haefeli; #X text 602 490 copyright (c) 2010 IOhannes m zmoelnig; #X obj 190 293 s \$0.tcpserver.o5; #X obj 324 383 r \$0.tcpserver.o5; #X text 644 232 check also:; #X obj 647 254 tcpreceive; #X obj 647 279 tcpclient; #X obj 166 356 unpack f f f f f; #X floatatom 275 379 6 0 0 0 - - -; #X connect 8 0 15 0; #X connect 13 0 18 0; #X connect 14 0 13 1; #X connect 15 0 13 0; #X connect 15 1 0 0; #X connect 15 2 1 0; #X connect 15 3 38 0; #X connect 15 4 33 0; #X connect 19 0 15 0; #X connect 20 0 22 0; #X connect 21 0 20 1; #X connect 23 0 15 0; #X connect 24 0 15 0; #X connect 25 0 15 0; #X connect 27 0 15 0; #X connect 34 0 20 0; #X connect 38 0 2 0; #X connect 38 1 3 0; #X connect 38 2 4 0; #X connect 38 3 5 0; #X connect 38 4 39 0; iemnet-0.1/iemnet-meta.pd0000644000175100001440000000070011452723024015126 0ustar zmoelnigusers#N canvas 169 49 432 242 10; #X text 47 84 iemnet: high performance networking with Pd; #X text 41 134 (c) 2010 IOhannes m zmölnig \, Institute of Electronic Music and Acoustics (IEM) \, University of Music and Performing Arts \, Graz \, Austria; #N canvas 25 49 420 300 META 0; #X text 10 10 VERSION 0.1; #X text 10 30 AUTHOR IOhannes m zmoelnig ; #X text 10 50 NAME iemnet; #X text 10 70 LICENSE GPL-2; #X restore 20 20 pd META; iemnet-0.1/tcpsend-help.pd0000644000175100001440000000116211453711672015321 0ustar zmoelnigusers#N canvas 0 92 506 250 12; #X msg 109 130 disconnect; #X obj 145 209 tgl 15 0 empty empty connected 20 7 0 8 -24198 -241291 -1 0 1; #X obj 145 186 tcpsend; #X msg 47 68 send 0 1 2 3; #X text 14 7 tcpsend sends bytes over a tcp connection.; #X text 142 68 send raw data; #X text 140 97 'send' prefix is optional; #X msg 75 96 99 98 97; #X text 181 35 <--first connect; #X text 289 173 2007/06/20 Martin Peach; #X msg 15 36 connect 127.0.0.1 9997; #X obj 364 55 tcpclient; #X obj 364 81 tcpreceive; #X text 359 32 check also:; #X connect 0 0 2 0; #X connect 2 0 1 0; #X connect 3 0 2 0; #X connect 7 0 2 0; #X connect 10 0 2 0; iemnet-0.1/iemnet_receiver.c0000644000175100001440000001611511445670516015725 0ustar zmoelnigusers/* iemnet * copyright (c) 2010 IOhannes m zmölnig, IEM */ #define DEBUGLEVEL 4 #include "iemnet.h" #include "iemnet_data.h" #include #include #include #include #include #define INBUFSIZE 65536L /* was 4096: size of receiving data buffer */ struct _iemnet_receiver { pthread_t thread; int sockfd; /* owned outside; you must call iemnet__receiver_destroy() before freeing socket yourself */ void*userdata; t_iemnet_chunk*data; t_iemnet_receivecallback callback; t_iemnet_queue*queue; t_clock *clock; int newdataflag; int running; int keepreceiving; pthread_mutex_t newdata_mtx, running_mtx, keeprec_mtx; }; /* notifies Pd that there is new data to fetch */ static void iemnet_signalNewData(t_iemnet_receiver*x) { int already=0; int trylock=0; pthread_mutex_lock(&x->newdata_mtx); already=x->newdataflag; x->newdataflag=1; /* don't schedule ticks at the end of life */ if(x->sockfd<0)already=1; pthread_mutex_unlock(&x->newdata_mtx); if(already) { return; } /* * try to lock Pd's main mutex * this is bound to deadlock if this function is called from within Pd's mainthread * (which happens when we destroy the receiver and signalNewData is called on cleanup) * * - shan't we check whether sys_trylock() returns EBUSY ? */ trylock=sys_trylock(); switch(trylock) { case 0: case EBUSY: if(x->clock)clock_delay(x->clock, 0); if(0==trylock)sys_unlock(); default: break; } } /* the workhorse of the family */ static void*iemnet__receiver_readthread(void*arg) { unsigned int i=0; int result = 0; t_iemnet_receiver*receiver=(t_iemnet_receiver*)arg; int sockfd=receiver->sockfd; t_iemnet_queue*q=receiver->queue; unsigned char data[INBUFSIZE]; unsigned int size=INBUFSIZE; struct sockaddr_in from; socklen_t fromlen = sizeof(from); int recv_flags=0; struct timeval timout; fd_set readset; FD_ZERO(&readset); FD_SET(sockfd, &readset); for(i=0; irunning_mtx); receiver->running=1; pthread_mutex_unlock(&receiver->running_mtx); while(1) { t_iemnet_chunk*c=NULL; pthread_mutex_lock(&receiver->keeprec_mtx); if(!receiver->keepreceiving) { pthread_mutex_unlock(&receiver->keeprec_mtx); break; } pthread_mutex_unlock(&receiver->keeprec_mtx); fromlen = sizeof(from); fd_set rs=readset; timout.tv_sec=0; timout.tv_usec=1000; recv_flags|=MSG_DONTWAIT; select(sockfd+1, &rs, NULL, NULL, &timout); if (!FD_ISSET(sockfd, &rs))continue; DEBUG("select can read"); //fprintf(stderr, "reading %d bytes...\n", size); //result = recv(sockfd, data, size, 0); result = recvfrom(sockfd, data, size, recv_flags, (struct sockaddr *)&from, &fromlen); //fprintf(stderr, "read %d bytes...\n", result); DEBUG("recfrom %d bytes", result); if(result<=0)break; c= iemnet__chunk_create_dataaddr(result, data, &from); DEBUG("pushing"); queue_push(q, c); DEBUG("signalling"); iemnet_signalNewData(receiver); DEBUG("rereceive"); } // oha DEBUG("readthread loop termination: %d", result); //if(result>=0)iemnet_signalNewData(receiver); pthread_mutex_lock(&receiver->running_mtx); receiver->running=0; pthread_mutex_unlock(&receiver->running_mtx); DEBUG("read thread terminated"); return NULL; } /* callback from Pd's main thread to fetch queued data */ static void iemnet__receiver_tick(t_iemnet_receiver *x) { int running=0, keepreceiving=0; // received data t_iemnet_chunk*c=queue_pop_noblock(x->queue); DEBUG("tick got chunk %p", c); while(NULL!=c) { (x->callback)(x->userdata, c); iemnet__chunk_destroy(c); c=queue_pop_noblock(x->queue); } DEBUG("tick cleanup"); pthread_mutex_lock(&x->newdata_mtx); x->newdataflag=0; pthread_mutex_unlock(&x->newdata_mtx); pthread_mutex_lock(&x->running_mtx); running = x->running; pthread_mutex_unlock(&x->running_mtx); DEBUG("tick running %d", running); if(!running) { // read terminated pthread_mutex_lock(&x->keeprec_mtx); keepreceiving=x->keepreceiving; pthread_mutex_unlock(&x->keeprec_mtx); /* keepreceiving is set, if receiver is not yet in shutdown mode */ if(keepreceiving) x->callback(x->userdata, NULL); } DEBUG("tick DONE"); } int iemnet__receiver_getsize(t_iemnet_receiver*x) { int size=-1; if(x && x->queue) size=queue_getsize(x->queue); return size; } t_iemnet_receiver*iemnet__receiver_create(int sock, void*userdata, t_iemnet_receivecallback callback) { static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; t_iemnet_receiver*rec=(t_iemnet_receiver*)malloc(sizeof(t_iemnet_receiver)); DEBUG("create new receiver for 0x%X:%d", userdata, sock); //fprintf(stderr, "new receiver for %d\t%x\t%x\n", sock, userdata, callback); if(rec) { t_iemnet_chunk*data=NULL; int res=0; data=iemnet__chunk_create_empty(INBUFSIZE); if(NULL==data) { iemnet__receiver_destroy(rec); DEBUG("create receiver failed"); return NULL; } rec->keepreceiving=1; rec->sockfd=sock; rec->userdata=userdata; rec->data=data; rec->callback=callback; memcpy(&rec->newdata_mtx , &mtx, sizeof(pthread_mutex_t)); memcpy(&rec->running_mtx , &mtx, sizeof(pthread_mutex_t)); memcpy(&rec->keeprec_mtx , &mtx, sizeof(pthread_mutex_t)); rec->newdataflag=0; rec->running=1; rec->queue = queue_create(); rec->clock = clock_new(rec, (t_method)iemnet__receiver_tick); res=pthread_create(&rec->thread, 0, iemnet__receiver_readthread, rec); } //fprintf(stderr, "new receiver created\n"); return rec; } void iemnet__receiver_destroy(t_iemnet_receiver*rec) { static int instance=0; int inst=instance++; int sockfd; DEBUG("[%d] destroy receiver %x", inst, rec); if(NULL==rec)return; pthread_mutex_lock(&rec->keeprec_mtx); if(!rec->keepreceiving) { pthread_mutex_unlock(&rec->keeprec_mtx); return; } rec->keepreceiving=0; pthread_mutex_unlock(&rec->keeprec_mtx); sockfd=rec->sockfd; DEBUG("joining thread"); pthread_join(rec->thread, NULL); DEBUG("[%d] really destroying receiver %x -> %d", inst, rec, sockfd); if(sockfd>=0) { /* this doesn't alway make recvfrom() return! * - try polling * - try sending a signal with pthread_kill() ? */ shutdown(sockfd, 2); /* needed on linux, since the recv won't shutdown on sys_closesocket() alone */ sys_closesocket(sockfd); } DEBUG("[%d] closed socket %d", inst, sockfd); rec->sockfd=-1; // empty the queue DEBUG("[%d] tick %d", inst, rec->running); iemnet__receiver_tick(rec); queue_destroy(rec->queue); DEBUG("[%d] tack", inst); if(rec->data)iemnet__chunk_destroy(rec->data); pthread_mutex_destroy(&rec->newdata_mtx); pthread_mutex_destroy(&rec->running_mtx); pthread_mutex_destroy(&rec->keeprec_mtx); clock_free(rec->clock); rec->clock=NULL; rec->userdata=NULL; rec->data=NULL; rec->callback=NULL; rec->queue=NULL; free(rec); rec=NULL; DEBUG("[%d] destroyed receiver", inst); } iemnet-0.1/iemnet.c0000644000175100001440000000620411456320053014025 0ustar zmoelnigusers/* iemnet * copyright (c) 2010 IOhannes m zmölnig, IEM */ #define DEBUGLEVEL #include "iemnet.h" #include void iemnet__addrout(t_outlet*status_outlet, t_outlet*address_outlet, long address, unsigned short port) { static t_atom addr[5]; static int firsttime=1; if(firsttime) { int i=0; for(i=0; i<5; i++)SETFLOAT(addr+i, 0); firsttime=0; } addr[0].a_w.w_float = (address & 0xFF000000)>>24; addr[1].a_w.w_float = (address & 0x0FF0000)>>16; addr[2].a_w.w_float = (address & 0x0FF00)>>8; addr[3].a_w.w_float = (address & 0x0FF); addr[4].a_w.w_float = port; if(status_outlet )outlet_anything(status_outlet , gensym("address"), 5, addr); if(address_outlet)outlet_list (address_outlet, gensym("list" ), 5, addr); } void iemnet__numconnout(t_outlet*status_outlet, t_outlet*numcon_outlet, int numconnections) { t_atom atom[1]; SETFLOAT(atom, numconnections); if(status_outlet)outlet_anything(status_outlet , gensym("connections"), 1, atom); if(numcon_outlet)outlet_float (numcon_outlet, numconnections); } void iemnet__socketout(t_outlet*status_outlet, t_outlet*socket_outlet, int socketfd) { t_atom atom[1]; SETFLOAT(atom, socketfd); if(status_outlet)outlet_anything(status_outlet , gensym("socket"), 1, atom); if(socket_outlet)outlet_float (socket_outlet, socketfd); } void iemnet__streamout(t_outlet*outlet, int argc, t_atom*argv, int stream) { if(NULL==outlet)return; if(stream) { while(argc-->0) { outlet_list(outlet, gensym("list"), 1, argv); argv++; } } else { outlet_list(outlet, gensym("list"), argc, argv); } } typedef struct _names { t_symbol*name; struct _names*next; } t_iemnet_names; static t_iemnet_names*namelist=0; static int iemnet__nametaken(const char*namestring) { t_symbol*name=gensym(namestring); t_iemnet_names*curname=namelist; t_iemnet_names*lastname=curname; while(curname) { if(name==(curname->name)) { return 1; } lastname=curname; curname=curname->next; } // new name! curname=(t_iemnet_names*)malloc(sizeof(t_iemnet_names)); curname->name=name; curname->next=0; if(lastname) lastname->next=curname; else namelist=curname; return 0; } int iemnet__register(const char*name) { if(iemnet__nametaken(name))return 0; post("iemnet - networking with Pd: [%s]", name); #ifdef LIBRARY_VERSION post(" version "LIBRARY_VERSION""); #endif post(" compiled on "__DATE__" at " __TIME__""); post(" copyright (c) 2010 IOhannes m zmoelnig, IEM"); post(" based on mrpeach/net, based on maxlib"); return 1; } #ifdef _MSC_VER void tcpclient_setup(void); void tcpreceive_setup(void); void tcpsend_setup(void); void tcpserver_setup(void); void udpclient_setup(void); void udpreceive_setup(void); void udpsend_setup(void); void udpserver_setup(void); #endif int debuglevel=0; void iemnet_debuglevel(void*x, t_float f) { debuglevel=(int)f; } IEMNET_EXTERN void iemnet_setup(void) { #ifdef _MSC_VER tcpclient_setup(); tcpreceive_setup(); tcpsend_setup(); tcpserver_setup(); udpclient_setup(); udpreceive_setup(); udpsend_setup(); udpserver_setup(); #endif } iemnet-0.1/NEWS0000644000175100001440000000046211454577552013117 0ustar zmoelniguserswhat's new in iemnet? 0.1 - forked away from mrpeach/net - one thread for each socket - default-target concept: either send to all clients, a single client or all-but a single client - getting rid of cruft either not related to networking (e.g. file-handling) or workarounds for bugs/design problems iemnet-0.1/ChangeLog0000644000175100001440000006063711456320053014164 0ustar zmoelnigusers2010-10-11 16:04 zmoelnig * tcpreceive.c, udpreceive.c: use SO_REUSEPORT (if available during compile-time) 2010-10-11 15:34 zmoelnig * .: svn:ignore all *.pd_linux 2010-10-11 12:15 zmoelnig * AUTHORS: authors illiterati 2010-10-11 12:13 zmoelnig * ChangeLog, NEWS: ChangeLog and NEWS files with bugs bunny and bill clinton 2010-10-11 12:04 zmoelnig * udpserver.c: at least accept one(1) client 2010-10-11 12:03 zmoelnig * iemnet.h: these debug things 2010-10-08 22:11 rdz * tcpserver-help.pd: added 'port' for outlet #4 2010-10-08 22:08 rdz * udpserver-help.pd: added help for [udpserver] 2010-10-08 21:57 rdz * tcpclient-help.pd, tcpreceive-help.pd, tcpsend-help.pd, tcpserver-help.pd, udpreceive-help.pd, udpsend-help.pd: added a 'check also:' section where applicable 2010-10-08 21:43 rdz * udpclient-help.pd: added help for [udpclient] 2010-10-08 20:53 rdz * tcpserver-help.pd: corrected NOTE: there is no outlet 6. made outlet counting consistent (start at 1). 2010-10-06 20:06 zmoelnig * tcpclient-help.pd, tcpreceive-help.pd, tcpserver-help.pd, udpreceive-help.pd, udpsend-help.pd: more iemnet specifics 2010-10-05 22:26 rdz * iemnet-meta.pd: made iemnet-meta.pd comply with the libname-meta.pd format 2010-10-02 20:39 rdz * tcpserver-help.pd: added description to the 'bufsize' status message 2010-10-02 20:34 rdz * tcpclient-help.pd, tcpreceive-help.pd, tcpsend-help.pd, tcpserver-help.pd: updated help of [tcpsend]/[tcpreceive] to reflect their current behaviour 2010-10-02 19:46 rdz * tcpclient-help.pd: updated help to reflect current behaviour of [tcpclient] 2010-10-02 19:15 rdz * tcpserver-help.pd: updated help to reflect [tcpserver]'s current behaviour 2010-09-20 15:06 zmoelnig * iemnet.h: global debuglevel 2010-09-20 14:35 zmoelnig * iemnet_receiver.c: more debugging in the tick 2010-09-20 13:31 zmoelnig * Makefile: added DEBUG_CFLAGS 2010-09-20 13:19 zmoelnig * iemnet.c: less verbose when changing debuglevel 2010-09-20 13:14 zmoelnig * iemnet.c, iemnet.h, iemnet_data.c, iemnet_receiver.c, iemnet_sender.c, tcpclient.c, tcpreceive.c, tcpsend.c, tcpserver.c, udpclient.c, udpreceive.c, udpsend.c, udpserver.c: debugging levels 2010-09-15 12:37 mescalinum * Makefile: remove -mcpu/-mtune flags as it breaks x64 build. we should eventually figure a way of determining automatically the CPU, but 'uname -m' doesn't tell the truth 2010-09-14 08:53 zmoelnig * iemnet_receiver.c: receive in nonblocking mode 2010-09-14 08:39 zmoelnig * iemnet_receiver.c: on the way to thread safety...(?) a number of new mutexes to protect what is there to protect. avoid deadlocks when using the big pd-lock 2010-09-13 13:48 zmoelnig * udpserver.c: more DEBUG; alternative algorithm to add clients with a connection-less protocol it's a bit hard to know which clients are connected to a server... 2010-09-13 13:43 zmoelnig * iemnet_receiver.c, udpreceive.c: nada: whitespace 2010-09-13 13:38 zmoelnig * iemnet_receiver.c: removed the "return" at the beginning of iemnet__receiver_destroy() it looks like i put it there for debugging something; but i cannot remember now exactly... 2010-09-09 13:53 zmoelnig * iemnet_receiver.c: more debug output 2010-09-09 13:52 zmoelnig * udpclient.c: ... 2010-08-27 09:15 zmoelnig * tcpserver.c: get rid o fwarning in sprintf() 2010-08-23 14:35 zmoelnig * iemnet_receiver.c: on read termination read the status variables in a protected area 2010-08-23 12:46 zmoelnig * iemnet.c, iemnet_data.c, iemnet_receiver.c, iemnet_sender.c, udpserver.c: use malloc() rather than getbytes(); use pthread_exit(); 2010-08-22 11:57 zmoelnig * udpserver.c: more support for udpserver 2010-08-20 17:19 zmoelnig * udpsend.c: fixed reentrancy issue 2010-08-20 16:54 zmoelnig * iemnet_data.c, iemnet_sender.c: fixed bugs in multithreading code 2010-08-20 16:20 zmoelnig * iemnet.h: in DEBUG mode, define IEMNET_HAVE_DEBUG 2010-08-20 14:32 zmoelnig * iemnet_data.c: reindentated; add some comments 2010-08-20 14:12 zmoelnig * iemnet_sender.c: more DEBUGs; clear memory before freeing it 2010-08-20 14:11 zmoelnig * iemnet_data.c: added more DEBUGs 2010-08-20 14:09 zmoelnig * udpreceive.c: output sender address 2010-08-09 09:22 zmoelnig * iemnet_data.c: allow to resize NULL-floatlists trying to resize a NULL-floatlist, will create a new one with the required size 2010-07-14 17:52 zmoelnig * udpsend.c: fixed crasher bug when printing "[udpsend] not connected" 2010-04-07 14:45 zmoelnig * iemnet.c, iemnet.h, tcpclient.c, tcpreceive.c, tcpserver.c: iemnet__streamout() takes an argument telling us whether to serialize or not (non-serialization might be useful for proxies) 2010-04-07 14:29 zmoelnig * udpserver.c: first working prototype - still loads of problems the socket outlet doesn't make any sense (remove it?) disconnecting kills the receiver thread! 2010-04-07 14:27 zmoelnig * iemnet.h, iemnet_receiver.c, tcpclient.c, tcpreceive.c, tcpserver.c, udpclient.c, udpreceive.c: new receiver callback: chunk2list has to be handled by client 2010-04-07 14:24 zmoelnig * iemnet_sender.c: sender now optionally uses sendto() if the chunk has an address/port specified, sendto() is used; else the default send() on the socket is used 2010-04-07 14:22 zmoelnig * iemnet_data.h: nice documentation (this file is not so private any more) 2010-04-01 12:22 zmoelnig * Makefile: don't need verbose auto-vectorizer 2010-04-01 12:21 zmoelnig * tcpserver.c: floatlist should be handled in the object 2010-04-01 12:21 zmoelnig * iemnet_receiver.c: cleanup queue in destructor 2010-04-01 12:18 zmoelnig * iemnet_data.c: check whether queue is !NULL before pushing/popping 2010-04-01 11:20 zmoelnig * tcpserver.c: call sender_destroy/receiver_destroy before cleaning up socketreceiver 2010-04-01 10:20 zmoelnig * iemnet.h: use directly rather than for getting needed types 2010-04-01 09:51 zmoelnig * iemnet.h: include stdlib.h on OSX (10.4) you cannot use INADDR_ANY without types, which are not provided by the header that defines INADDR_ANY... 2010-04-01 09:33 zmoelnig * tests/sequence/01_server.pd: display the queue status 2010-04-01 09:24 zmoelnig * tcpsend.c: fixed objName for tcpsend 2010-04-01 09:14 zmoelnig * build/w32-vs2003/iemnet.vcproj, iemnet.c: updated M$VC project 2010-04-01 09:03 zmoelnig * udpserver.c: added note about udpserver not being useable yet 2010-04-01 09:01 zmoelnig * udpserver.c: use error() for errors 2010-04-01 07:22 zmoelnig * Makefile, udpserver.c: non-functional crashy copy of tcpserver for udp 2010-04-01 07:21 zmoelnig * udpreceive.c: status outlet and forgotten struct member... 2010-04-01 07:21 zmoelnig * iemnet_sender.c: iemnet__sender_destroy() now also closes the socket 2010-04-01 07:21 zmoelnig * udpclient.c: use error instead of post 2010-04-01 07:21 zmoelnig * iemnet.c, iemnet.h, tcpclient.c, tcpreceive.c, tcpsend.c, tcpserver.c, udpclient.c, udpreceive.c, udpsend.c: exporting symbols; using objName 2010-04-01 07:20 zmoelnig * iemnet.c, iemnet.h: convenience function for outputting the socket 2010-04-01 07:20 zmoelnig * iemnet.c, iemnet.h: convenience function for outputting the number of connections 2010-04-01 07:20 zmoelnig * tcpreceive-help.pd, tcpreceive.c: tcpclient fixes and features fixed the callback (API changed but due to cast this has gone unnoticed) featured settable ports (and now creation failure is port is taken) 2010-04-01 07:20 zmoelnig * tcpclient.c: made connection thread safe the connection thread modifies the object's state (and calls clock_delay()) since this is not thread safe, it is now protected by sys_lock() NOTE1: i remember someone saying that clock_delay() is thread safe NOTE2: this might still crash if the object is deleted before while the thread is executing 2010-04-01 07:20 zmoelnig * tcpserver.c: removed duplicate inclusion is already included in iemnet.h 2010-03-31 13:44 zmoelnig * tests, tests/sequence, tests/sequence/01_client.pd, tests/sequence/01_server.pd: simple test, whether sequences qppear in the right order 2010-03-31 09:21 zmoelnig * tcpserver.c: made "target" persistent, even if clients disconnect added "targetsocket" to specify the target via socket 2010-03-31 08:58 zmoelnig * iemnet.c, iemnet.h, tcpclient.c, tcpreceive.c, tcpserver.c: output the data of stream-based objects in a serialized form 2010-03-31 08:51 zmoelnig * FEATURES.txt, README.txt: features sheet 2010-03-31 08:04 zmoelnig * tcpserver.c: query the server port especially useful when autoassigning ports (port=0) 2010-03-30 12:45 zmoelnig * tcpserver.c: tcpserver now does not fail to create if port is already in use with the "port" message, the listening port can be changed at runtime (LATER we also want to query the port...) 2010-03-30 11:51 zmoelnig * tcpclient.c: avoid double registration of tcpclient 2010-03-30 10:54 zmoelnig * build/w32-vs2003/iemnet.vcproj: updated M$VC project 2010-03-30 10:52 zmoelnig * iemnet.h, tcpserver.c: added "target" method to tcpserver, to specify how to send ordinary lists target 0: broadcast target >0: send to specified client target <0: send not to specified client (but all others) 2010-03-30 09:46 zmoelnig * iemnet_data.c, iemnet_data.h, iemnet_receiver.c, iemnet_sender.c, tcpclient.c, tcpserver.c: query queue sizes with [tcpclient] use [bang( with [tcpserver] use [client ( 2010-03-30 09:06 zmoelnig * Makefile, iemnet.h, iemnet_data.c, iemnet_data.h: inline code documentation 2010-03-30 07:36 zmoelnig * Makefile, iemnet.c, iemnet.h, iemnet_data.c, iemnet_data.h, iemnet_receiver.c, iemnet_sender.c: split core library into separate files 2010-03-29 17:32 zmoelnig * build/w32-vs2003/iemnet.vcproj, iemnet.h, tcpreceive.c: made it compile on w32 again 2010-03-29 17:30 zmoelnig * tcpreceive.c, tcpsend.c, udpreceive.c, udpsend.c: replaced &s_... by gensym("...") this time it's the right lib 2010-03-29 17:18 zmoelnig * tcpserver.c: disable DEBUG 2010-03-29 17:11 zmoelnig * tcpclient.c, tcpserver.c: output additional data just like with mrpeach's objects 2010-03-29 16:15 zmoelnig * tcpclient.c: use error() rather than post() where appropriate 2010-03-29 16:13 zmoelnig * tcpclient.c: proper handling of remote disconnect 2010-03-29 14:10 zmoelnig * iemnet.c, tcpserver.c: provide feedback to Pd main-thread whether sender-thread still exists 2010-03-29 13:37 zmoelnig * iemnet.c: sender/receiver dtors can now be called recursively without double freeing resources (hopefully) 2010-03-29 13:07 zmoelnig * iemnet.c, tcpclient.c, tcpserver.c: clean up more properly on disconnect disconnecting has the awful tendency to trigger itself; for now, this is handled in the calling object's code, but later iemnet_(sender|receiver) should take care themselves 2010-03-29 13:04 zmoelnig * NOTES.txt: remind me of doing tests 2010-03-27 08:55 zmoelnig * iemnet.c: no need to lock the entire Pd-process 2010-03-26 14:36 zmoelnig * iemnet-meta.pd: iemnet-meta.pd is needed for "make dist" this should be properly documented within the Makefile... 2010-03-26 14:35 zmoelnig * Makefile: make cleaner 2010-03-26 14:35 zmoelnig * NOTES.txt, README.txt: more mission and scratchpad 2010-03-26 14:35 zmoelnig * iemnet.c: sys_lock() before calling clock_delay() LATER: think about using sys_trylock 2010-03-26 14:35 zmoelnig * tcpclient.c, udpclient.c: output server address 2010-03-26 14:34 zmoelnig * Makefile, udpclient.c: a simple udpclient bidirectional communication using UDP 2010-03-26 14:34 zmoelnig * tcpclient.c, tcpreceive.c, tcpsend.c, tcpserver.c, udpreceive.c, udpsend.c: proper use of DEBUG use DEBUG() directly rather than "#ifdef DEBUG" 2010-03-26 14:34 zmoelnig * udpsend.c: whitespaces... 2010-03-26 14:33 zmoelnig * udpreceive~.c, udpsend~-help.pd, udpsend~.c, udpsend~.h: no more audio-over-net objects use AOO (Audio Over OSC) or the like for such things... 2010-03-26 14:33 zmoelnig * Makefile, udpsend.c: udpsend 2010-03-26 14:33 zmoelnig * udpreceive.c: fix description: udpreceive is really a server 2010-03-26 14:32 zmoelnig * tcpserver.c: output the sending host 2010-03-26 14:32 zmoelnig * Makefile, udpreceive.c: udpreceive seems to work... 2010-03-26 14:32 zmoelnig * tcpserver.c: compatibility with mrpeach; convenience re-add all those stupid outlets. use convenience functions to send data to these outlets 2010-03-26 14:32 zmoelnig * iemnet.c, iemnet.h: convenience functions these probably should go into a separate file 2010-03-26 14:31 zmoelnig * tcpsend.c: use DEBUG() 2010-03-26 14:31 zmoelnig * iemnet.c, iemnet.h, tcpclient.c, tcpserver.c: changed API of read-callback the callback will provide the raw data chunk as well (easier to add more data) 2010-03-25 11:36 zmoelnig * iemnet.c, iemnet.h: DEBUG mechanism 2010-03-25 11:35 zmoelnig * README.txt: more missions 2010-03-25 11:34 zmoelnig * README.txt: mission statement 2010-03-25 08:29 zmoelnig * Makefile: included tcp* objects in Makefile 2010-03-25 08:28 zmoelnig * iemnet.h: most of the tcp-objects seem to work now; performance increase as measured until now is great :-) 2010-03-25 08:28 zmoelnig * iemnet.c, tcpclient.c, tcpreceive.c, tcpsend.c, tcpserver.c: most of the tcp-objects seem to work now; performance increase as measured until now is great :-) 2010-03-24 22:47 eighthave * Makefile: added include path for Pd-extended.app include folder to include path, after the -I$(PD_PATH)/src/ so it'll only use the headers inside the Pd-extended.app if the source is not specified 2010-03-24 17:39 zmoelnig * iemnet.c, tcpclient.c: on M$VC we only build libraries (and the autoregistration doesn't work) 2010-03-24 17:38 zmoelnig * build/w32-vs2003/iemnet.vcproj: removed the "shared.c" 2010-03-24 17:25 zmoelnig * iemnet.h, tcpserver.c: fixed M$VC preprocessor code 2010-03-24 17:20 zmoelnig * Makefile, iemnet.c, iemnet.h, shared.c, tcpclient.c, tcpserver.c: automatic calling 2010-03-24 16:41 zmoelnig * shared.c: cleaned up 2010-03-24 16:40 zmoelnig * tcpclient.c: check for validity before disconnecting (and invalidate pointers after) 2010-03-24 16:31 zmoelnig * shared.c: call shutdown before closesocket() 2010-03-24 15:58 zmoelnig * iemnet.c, iemnet.h, shared.c, tcpclient.c, tcpserver.c: made it compile (and run) on w32 2010-03-24 15:57 zmoelnig * build, build/w32-vs2003, build/w32-vs2003/iemnet.sln, build/w32-vs2003/iemnet.vcproj: w32 project 2010-03-24 15:24 zmoelnig * Makefile, shared.c, tcpclient.c, tcpserver.c: a first client 2010-03-24 12:09 zmoelnig * iemnet.h, shared.c, tcpserver.c: kind of works now: we can dump about 500MB within 5secs into tcpserver on lo 2010-03-24 12:09 zmoelnig * Makefile: only build tcpserver for now 2010-03-23 19:26 zmoelnig * shared.c: clock-based interfacing with Pd's main thraed 2010-03-23 19:05 zmoelnig * iemnet.h, shared.c, tcpserver.c: hmm, less crashes; threads hang 2010-03-23 17:49 zmoelnig * Makefile, iemnet.h, shared.c, tcpserver.c: factored out code into "shared"; it's still rather unstable... 2010-03-23 13:36 zmoelnig * tcpserver.c: cleaned up 2010-03-23 12:10 zmoelnig * Makefile: Makefile (taken from ext13) 2010-03-23 11:54 zmoelnig * .: forked mrpeach's "net" 2010-03-22 20:12 mrpeach * Changed valid-stream output to be a signal output. Moved all post()s out of the perform routine. Updated help patch and version. 2010-03-22 15:29 zmoelnig * check for NULL-pointer in destructor 2010-03-16 17:22 mrpeach * Output valid state only when it changes. 2010-03-16 16:42 mrpeach * Added an outlet to udpreceive~ to indicate valid audio. Ouput address and port only if changed. Updated version and help patch. 2010-03-11 21:04 mrpeach * Updfated version number and help patch. 2010-03-11 19:28 mrpeach * Added a "TAG!" identifier field to the tag so it can be verified as a tag. This should help prevent crashes in case of dropped packets. 2010-03-09 17:31 mrpeach * Moved client-specific parameters into a single t_tcpserver_socketreceiver struct. 2010-03-09 10:28 zmoelnig * fixing bug #2966186 2010-03-02 17:44 mrpeach * Added a verbosity method to stop [tcpclient] printing in the main window every time it connects/disconnects. Updated the help patch to match. 2010-02-24 18:37 mrpeach * Corrected some error messages, check for EINVAL after recvfrom(), to try to find out why it happens... 2010-01-20 19:41 mrpeach * Added SO_BROADCAST so you can actually broadcast with a .255 address... 2010-01-18 17:41 mrpeach * use unix line endings 2010-01-18 17:25 mrpeach * Make the connection thread detached and don't refer to its struct directly, check for thread creation errors. 2010-01-18 04:31 mrpeach * Use NULL instead of 0 to reset thread pointer so maybe now MinGW can compile it...removed unused symbols 2010-01-15 18:53 mrpeach * Added include for ws2tcpip.h for socklen_t for _WIN32 2010-01-14 20:26 mrpeach * Don't need float_cast.h anymore, using the flint union with ntohl/htonl instead. 2010-01-14 20:24 mrpeach * Always send in network byte order for all architectures. UDP receiving socket doesn't need to be non-blocking (I think...). Cleaned up help patch. 2010-01-14 20:21 mrpeach * Changed int optLen to socklen_t to avoid signedness warning 2010-01-13 21:54 mrpeach * Fixed header files for _WIN32 2010-01-12 18:58 mrpeach * Block size is settable by creation argument. Buffer size message is specified in frames. Info message gives some more info. Channels transmitted can be set to zero. Cleaned up help patch. 2010-01-11 16:56 mrpeach * Added "unix" to the test for "UNIX". 2010-01-11 14:27 mrpeach * help patch for udpsend~ and udpreceive~ 2010-01-11 14:25 mrpeach * Versions of netsend~ for udp. 2010-01-04 16:49 mrpeach * Had the wrong #include for ioctl... 2010-01-02 20:50 mrpeach * Changed MSW to _WIN32. Added tcpserver_send_buffer_avaliable_for_client() using SIOCOUTQ ioctl in linux to see if send buffer has any room left. Apparently no equivalent exists for BSD or Windows. 2010-01-02 20:04 mrpeach * Changed MSW to _WIN32 2010-01-02 20:03 mrpeach * Changed MSW to _WIN32 2010-01-02 19:59 mrpeach * Changed MSW to _WIN32 2010-01-02 19:52 mrpeach * Changed MSW to _WIN32 2010-01-02 19:51 mrpeach * Changed MSW to _WIN32 2009-11-29 17:37 mrpeach * Sender threads are now created in the detached state so their resources will be freed when the threads complete. This appears to stop the accumulation of handles on WinXP at least. 2009-11-28 21:38 mrpeach * Removed duplicate send, added threaded send for files, and also send whenever buffer is full (65536 bytes) for incoming lists (although [tcpserver] is unlikely to be fed lists that long) 2009-11-12 22:16 mrpeach * Using pthreads to send each message to each client in its own thread. This should eliminate hangups when clients disappear halfway through, and increase responsiveness of Pd with [tcpserver]. Messages are sent as one unit instead of byte-by-byte. Select() is no longer used to check for available space in the buffer. 2009-04-08 19:48 mrpeach * Oops, I had forgotten to add the default timeout of 1000us. 2009-04-08 19:34 mrpeach * Added timeout message to set send timeout in microseconds, defaults to 1000. Changed help patch to match. 2009-04-08 18:35 mrpeach * Added [timeout( message to set microsecond timeout for send. This gives time for unusually small buffers to clear. Set default timeout to 1000us. Also prints a message if the whole send didn't complete. Updated help patch to match. 2009-03-09 16:01 mrpeach * The 'sent' message now contains client number, bytes sent, socket number. Empty 'send' message triggers list of client messages on right outlet. Empty socket number message causes a client message on right outlet. Help patch updated. 2009-03-04 22:33 mrpeach * Changed send routine to send one byte at a time and output number of bytes sent, so it won't block if the other end disappears. Also settable buffer size. Help path updated. 2009-03-02 17:55 mrpeach * Fixed some warnings about signed/unsigned variables. 2009-03-02 17:01 mrpeach * Fixed a bug that prevented the send routine from leaving the loop when no more data can be sent. Now it won't hang if you try sending to a recently disconnected client, honest! 2009-03-02 03:41 mrpeach * tcpserver_send_buf: send only one byte at a time into the buffer to avoid buffer overflow. This should not slow the whole thing down too much since we're already passing things one byte at a time. 2009-03-01 21:56 mrpeach * Added a [clientbuf( message to set the send buffer size for that client. Also the [client( message will output the current buffer size as well as the socket and ip for that client. Help patch updated. 2009-02-24 21:07 mrpeach * Updated help files to reflect new [tcpserver], cleaned up a bit more. 2009-02-24 21:06 mrpeach * Adds a status outlet which currently gives info about connected clients and the amount of sent data. 2009-02-23 20:58 mrpeach * Fixed select call so it works properly 2009-02-22 23:39 mrpeach * include errno.h for linux 2009-02-22 22:36 mrpeach * Removed some commented-out stuff 2009-02-22 22:25 mrpeach * Checks to see if a socket can be written to before sending. If not, prints a message to the pd window and doesn't send the data. 2008-11-05 19:58 mrpeach * Updated to add port number to list on right outlet 2008-11-05 19:58 mrpeach * Added port number output to ip list on right outlet as suggested by zmoelnig 2008-11-05 19:35 mrpeach * Updated to add port number on ip (right) outlet 2008-11-05 19:34 mrpeach * Added port number to ip outlet as suggested by zmoelnig 2008-11-05 19:06 mrpeach * Added broadcast permission to sockets as suggested by zmoelnig's patch 2221504 2008-06-18 17:30 mrpeach * Clarify what SO_REUSEADDR is for, no code changes. 2008-05-07 09:56 zmoelnig * removed svn:executable-flag 2008-03-27 19:56 mrpeach * No limit on sendable file size. 2008-03-20 15:57 mrpeach * Updated with dump message 2008-03-20 15:50 mrpeach * Added dump message for hexdump of received characters to main window. Added #include for u_long define. 2008-01-21 14:09 mrpeach * Applied W.Ritsch's patch to fix sign error with input > 127 2007-08-06 22:18 mrpeach * Closing socket should actually work now. Before it was just removing it from the list. 2007-08-02 15:56 mrpeach * Added disconnectclient and disconnectsocket methods so server can close connections too. 2007-07-23 16:48 mrpeach * Removed unused static binbuf that caused occasional crashes when reinstantiating the object. Thanks IOhannes for pointing it out! 2007-06-20 21:10 mrpeach * Updated 2007-06-20 20:31 mrpeach * Implemented IOhannes' list send 2006-12-12 09:07 zmoelnig * removed the "break;" statement in the broadcast() code to enable broadcasting to all connected clients (and not jsut the first one) 2006-12-04 20:18 mrpeach * make sure bytes > 127 are not interpreted as negative. 2006-12-04 20:17 mrpeach * make sure bytes >127 are not interpreted as negative. 2006-12-01 16:50 mrpeach * Use sprintf_s instead of snprintf if _MSC_VER defined 2006-11-28 16:44 mrpeach * changed MAX_PATH to FILENAME_MAX. 2006-11-28 16:43 mrpeach * changed MAX_PATH to FILENAME_MAX 2006-11-07 21:58 mrpeach * test file for net object help patches 2006-11-07 21:57 mrpeach * added send filename 2006-11-07 21:21 mrpeach * removed declspec 2006-11-07 21:20 mrpeach * added file send 2006-08-24 06:51 mrpeach * Renamed files without x_net_ prefix. Removed extra copies of tcpserver and tcpclient 2006-08-17 05:33 eighthave * added mrpeach targets and added missing header to get things compiling on Mac OS X 2006-08-16 20:22 mrpeach * Added the net, osc and sqosc~ directories iemnet-0.1/udpserver.c0000644000175100001440000005315411454576532014606 0ustar zmoelnigusers/* udpserver.c * copyright (c) 2010 IOhannes m zmölnig, IEM * copyright (c) 2006-2010 Martin Peach * copyright (c) 2004 Olaf Matthes */ /* */ /* A server for bidirectional communication from within Pd. */ /* Allows to send back data to specific clients connected to the server. */ /* */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version 2 */ /* of the License, or (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* */ /* ---------------------------------------------------------------------------- */ #define DEBUGLEVEL 1 #include "iemnet.h" #include #include #define MAX_CONNECT 32 /* maximum number of connections */ /* ----------------------------- udpserver ------------------------- */ static t_class *udpserver_class; static const char objName[] = "udpserver"; typedef struct _udpserver_sender { struct _udpserver *sr_owner; long sr_host; unsigned short sr_port; t_int sr_fd; t_iemnet_sender*sr_sender; } t_udpserver_sender; typedef struct _udpserver { t_object x_obj; t_outlet *x_msgout; t_outlet *x_connectout; t_outlet *x_sockout; // legacy t_outlet *x_addrout; // legacy t_outlet *x_statout; t_udpserver_sender *x_sr[MAX_CONNECT]; /* socket per connection */ t_int x_nconnections; t_int x_connectsocket; /* socket waiting for new connections */ t_int x_port; unsigned char x_accept; /* whether we accept new connections or not */ int x_defaulttarget; /* the default connection to send to; 0=broadcast; >0 use this client; <0 exclude this client */ t_iemnet_receiver *x_receiver; t_iemnet_floatlist *x_floatlist; } t_udpserver; static t_udpserver_sender *udpserver_sender_new(t_udpserver *owner, unsigned long host, unsigned short port) { t_udpserver_sender *x = (t_udpserver_sender *)malloc(sizeof(t_udpserver_sender)); if(NULL==x) { error("%s_sender: unable to allocate %d bytes", objName, sizeof(*x)); return NULL; } else { int sockfd = owner->x_connectsocket; x->sr_owner=owner; x->sr_fd=sockfd; x->sr_host=host; //ntohl(addr->sin_addr.s_addr); x->sr_port=port; //ntohs(addr->sin_port); x->sr_sender=iemnet__sender_create(sockfd); } return (x); } static void udpserver_sender_free(t_udpserver_sender *x) { DEBUG("freeing %x", x); if (x != NULL) { int sockfd=x->sr_fd; t_iemnet_sender*sender=x->sr_sender; x->sr_owner=NULL; x->sr_sender=NULL; x->sr_fd=-1; free(x); if(sender) iemnet__sender_destroy(sender); sys_closesocket(sockfd); } DEBUG("freeed %x", x); } static t_udpserver_sender* udpserver_sender_copy(t_udpserver_sender*x) { return udpserver_sender_new(x->sr_owner,x->sr_host, x->sr_port); } static int udpserver_socket2index(t_udpserver*x, int sockfd) { int i=0; for(i = 0; i < x->x_nconnections; i++) /* check if connection exists */ { if(x->x_sr[i]->sr_fd == sockfd) { return i; } } return -1; } /* checks whether client is a valid (1-based) index * if the id is invalid, returns -1 * if the id is valid, return the 0-based index (client-1) */ static int udpserver_fixindex(t_udpserver*x, int client) { if(x->x_nconnections <= 0) { pd_error(x, "[%s]: no clients connected", objName); return -1; } if (!((client > 0) && (client <= x->x_nconnections))) { pd_error(x, "[%s] client %d out of range [1..%d]", objName, client, x->x_nconnections); return -1; } return (client-1); } /* returns 1 if addr1==addr2, 0 otherwise */ static int equal_addr(unsigned long host1, unsigned short port1, unsigned long host2, unsigned short port2) { return ( ((port1 == port2) && (host1 == host2)) ); } static int udpserver__find_sender(t_udpserver*x, unsigned long host, unsigned short port) { int i=0; for(i=0; ix_nconnections; i++) { if(NULL==x->x_sr[i])return -1; if(equal_addr(host, port, x->x_sr[i]->sr_host, x->x_sr[i]->sr_port))return i; } return -1; } /** * check whether the sender is already registered * if not, add it to the list of registered senders */ static t_udpserver_sender* udpserver_sender_add(t_udpserver*x, unsigned long host, unsigned short port ) { int id=-1; if(!x->x_accept)return NULL; id=udpserver__find_sender(x, host, port); DEBUG("%X:%d -> %d", host, port, id); if(id<0) { #if 1 /* since udp is a connection-less protocol we have no way of knowing the currently connected clients * the following 3 lines assume, that there is only one client connected (the last we got data from */ id=0; udpserver_sender_free(x->x_sr[id]); x->x_sr[id]=udpserver_sender_new(x, host, port); x->x_nconnections=1; #else /* this is a more optimistic approach as above: * every client that we received data from is added to the list of receivers * the idea is to remove the sender, if it's known to not receive any data */ id=x->x_nconnections; /* an unknown address! add it */ if(idx_sr[id]=udpserver_sender_new(x, host, port); DEBUG("new sender[%d]= %x", id, x->x_sr[id]); x->x_nconnections++; } else { // oops, no more senders! id=-1; } #endif } DEBUG("sender_add: %d", id); if(id>=0) { return x->x_sr[id]; } return NULL; } static void udpserver_sender_remove(t_udpserver*x, int id) { if(id>=0 && idx_nconnections && x->x_sr[id]) { int i; t_udpserver_sender* sdr=x->x_sr[id]; udpserver_sender_free(sdr); // close the gap by shifting the remaining connections to the left for(i=id; ix_nconnections; i++) { x->x_sr[id]=x->x_sr[id+1]; } x->x_sr[id]=NULL; x->x_nconnections--; } } /* ---------------- udpserver info ---------------------------- */ static void udpserver_info_client(t_udpserver *x, int client) { // "client " // "bufsize " static t_atom output_atom[4]; if(x&&x->x_sr&&x->x_sr[client]) { int sockfd = x->x_sr[client]->sr_fd; unsigned short port = x->x_sr[client]->sr_port; long address = x->x_sr[client]->sr_host; char hostname[MAXPDSTRING]; int insize =iemnet__receiver_getsize(x->x_receiver); int outsize=iemnet__sender_getsize (x->x_sr[client]->sr_sender ); snprintf(hostname, MAXPDSTRING-1, "%d.%d.%d.%d", (unsigned char)((address & 0xFF000000)>>24), (unsigned char)((address & 0x0FF0000)>>16), (unsigned char)((address & 0x0FF00)>>8), (unsigned char)((address & 0x0FF)) ); hostname[MAXPDSTRING-1]=0; SETFLOAT (output_atom+0, client+1); SETFLOAT (output_atom+1, sockfd); SETSYMBOL(output_atom+2, gensym(hostname)); SETFLOAT (output_atom+3, port); outlet_anything( x->x_statout, gensym("client"), 4, output_atom); SETFLOAT (output_atom+0, client+1); SETFLOAT (output_atom+1, insize); SETFLOAT (output_atom+2, outsize); outlet_anything( x->x_statout, gensym("bufsize"), 3, output_atom); } } static void udpserver_info(t_udpserver *x) { static t_atom output_atom[4]; int sockfd=x->x_connectsocket; int port=x->x_port; if(sockfd<0) { // no open port error("[%s] no valid sock", objName); } if(x->x_port<=0) { struct sockaddr_in server; socklen_t serversize=sizeof(server); if(!getsockname(sockfd, (struct sockaddr *)&server, &serversize)) { x->x_port=ntohs(server.sin_port); port=x->x_port; } else { error("[%s] gesockname failed for %d", objName, sockfd); } } SETFLOAT (output_atom+0, port); outlet_anything( x->x_statout, gensym("port"), 1, output_atom); } static void udpserver_info_connection(t_udpserver *x, t_udpserver_sender*y) { iemnet__addrout(x->x_statout, x->x_addrout, y->sr_host, y->sr_port); // outlet_float(x->x_sockout, y->sr_fd); } /* ---------------- main udpserver (send) stuff --------------------- */ static void udpserver_disconnect_socket(t_udpserver *x, t_floatarg fsocket); static void udpserver_send_bytes(t_udpserver*x, int client, t_iemnet_chunk*chunk) { DEBUG("send_bytes to %x -> %x[%d]", x, x->x_sr, client); if(x->x_sr)DEBUG("client %X", x->x_sr[client]); if(x && x->x_sr && x->x_sr[client]) { t_atom output_atom[3]; int size=0; t_iemnet_sender*sender=sender=x->x_sr[client]->sr_sender; int sockfd = x->x_sr[client]->sr_fd; chunk->addr=x->x_sr[client]->sr_host; chunk->port=x->x_sr[client]->sr_port; if(sender) { size=iemnet__sender_send(sender, chunk); } SETFLOAT(&output_atom[0], client+1); SETFLOAT(&output_atom[1], size); SETFLOAT(&output_atom[2], sockfd); outlet_anything( x->x_statout, gensym("sent"), 3, output_atom); if(size<0) { // disconnected! udpserver_disconnect_socket(x, sockfd); } } } /* broadcasts a message to all connected clients but the given one */ static void udpserver_send_butclient(t_udpserver *x, int but, int argc, t_atom *argv) { int client=0; t_iemnet_chunk*chunk=iemnet__chunk_create_list(argc, argv); /* enumerate through the clients and send each the message */ for(client = 0; client < x->x_nconnections; client++) /* check if connection exists */ { /* socket exists for this client */ if(client!=but)udpserver_send_bytes(x, client, chunk); } iemnet__chunk_destroy(chunk); } /* sends a message to a given client */ static void udpserver_send_toclient(t_udpserver *x, int client, int argc, t_atom *argv) { t_iemnet_chunk*chunk=iemnet__chunk_create_list(argc, argv); udpserver_send_bytes(x, client, chunk); iemnet__chunk_destroy(chunk); } /* send message to client using client number note that the client numbers might change in case a client disconnects! */ /* clients start at 1 but our index starts at 0 */ static void udpserver_send_client(t_udpserver *x, t_symbol *s, int argc, t_atom *argv) { int client=0; if (argc > 0) { client=udpserver_fixindex(x, atom_getint(argv)); if(client<0)return; if(argc==1) { udpserver_info_client(x, client); } else { udpserver_send_toclient(x, client, argc-1, argv+1); } return; } else { for(client=0; clientx_nconnections; client++) udpserver_info_client(x, client); } } /* broadcasts a message to all connected clients */ static void udpserver_broadcast(t_udpserver *x, t_symbol *s, int argc, t_atom *argv) { int client; t_iemnet_chunk*chunk=iemnet__chunk_create_list(argc, argv); DEBUG("broadcasting to %d clients", x->x_nconnections); /* enumerate through the clients and send each the message */ for(client = 0; client < x->x_nconnections; client++) /* check if connection exists */ { /* socket exists for this client */ udpserver_send_bytes(x, client, chunk); } iemnet__chunk_destroy(chunk); } /* broadcasts a message to all connected clients */ static void udpserver_broadcastbut(t_udpserver *x, t_symbol *s, int argc, t_atom *argv) { int client=0; int but=-1; t_iemnet_chunk*chunk=NULL; if(argc<2) { return; } if((but=udpserver_fixindex(x, atom_getint(argv)))<0)return; udpserver_send_butclient(x, but, argc-1, argv+1); } static void udpserver_defaultsend(t_udpserver *x, t_symbol *s, int argc, t_atom *argv) { int client=-1; int sockfd=x->x_defaulttarget; DEBUG("sending to sockfd: %d", sockfd); if(0==sockfd) udpserver_broadcast(x, s, argc, argv); else if(sockfd>0) { client=udpserver_socket2index(x, sockfd); udpserver_send_toclient(x, client, argc, argv); } else if(sockfd<0) { client=udpserver_socket2index(x, -sockfd); udpserver_send_butclient(x, client, argc, argv); } } static void udpserver_defaulttarget(t_udpserver *x, t_floatarg f) { int sockfd=0; int rawclient=f; int client=(rawclient<0)?(-rawclient):rawclient; if(client > x->x_nconnections) { error("[%s] target %d out of range [0..%d]", objName, client, x->x_nconnections); return; } // map the client to a persistant socket if(client>0) { sockfd=x->x_sr[client-1]->sr_fd; } if(rawclient<0)sockfd=-sockfd; x->x_defaulttarget=sockfd; } static void udpserver_targetsocket(t_udpserver *x, t_floatarg f) { int sockfd=f; x->x_defaulttarget=sockfd; } /* send message to client using socket number */ static void udpserver_send_socket(t_udpserver *x, t_symbol *s, int argc, t_atom *argv) { int client = -1; t_iemnet_chunk*chunk=NULL; if(argc) { client = udpserver_socket2index(x, atom_getint(argv)); if(client<0)return; } else { pd_error(x, "%s_send: no socket specified", objName); return; } /* get socket number of connection (first element in list) */ if(argc && argv->a_type == A_FLOAT) { int sockfd=atom_getint(argv); client = udpserver_socket2index(x, sockfd); if(client < 0) { error("[%s]: no connection on socket %d", objName, sockfd); return; } } else { error("[%s]: no socket specified", objName); return; } chunk=iemnet__chunk_create_list(argc-1, argv+1); udpserver_send_bytes(x, client, chunk); iemnet__chunk_destroy(chunk); } static void udpserver_disconnect(t_udpserver *x, int client) { int k; DEBUG("disconnect %x %d", x, client); t_udpserver_sender*sdr; int conns; if(client<0 || client >= x->x_nconnections)return; sdr=udpserver_sender_copy(x->x_sr[client]); udpserver_sender_remove(x, client); conns=x->x_nconnections; udpserver_info_connection(x, sdr); outlet_float(x->x_connectout, conns); } /* disconnect a client by number */ static void udpserver_disconnect_client(t_udpserver *x, t_floatarg fclient) { int client = udpserver_fixindex(x, fclient); if(client<0)return; udpserver_disconnect(x, client); } /* disconnect a client by socket */ static void udpserver_disconnect_socket(t_udpserver *x, t_floatarg fsocket) { int id=udpserver_socket2index(x, (int)fsocket); if(id>=0) udpserver_disconnect_client(x, id+1); } /* disconnect a client by socket */ static void udpserver_disconnect_all(t_udpserver *x) { int id=x->x_nconnections; while(--id>=0) { udpserver_disconnect(x, id); } } /* whether we should accept new connections */ static void udpserver_accept(t_udpserver *x, t_float f) { x->x_accept=(unsigned char)f; } /* ---------------- main udpserver (receive) stuff --------------------- */ static void udpserver_receive_callback(void *y, t_iemnet_chunk*c) { t_udpserver*x=(t_udpserver*)y; if(NULL==y)return; if(c) { int conns = x->x_nconnections; DEBUG("add new sender from %d", c->port); t_udpserver_sender*sdr=udpserver_sender_add(x, c->addr, c->port); DEBUG("added new sender from %d", c->port); if(sdr) { udpserver_info_connection(x, sdr); x->x_floatlist=iemnet__chunk2list(c, x->x_floatlist); // gets destroyed in the dtor /* here we might have a reentrancy problem */ if(conns!=x->x_nconnections) { outlet_float(x->x_connectout, x->x_nconnections); } outlet_list(x->x_msgout, gensym("list"), x->x_floatlist->argc, x->x_floatlist->argv); } } else { // disconnection never happens with a connectionless protocol like UDP pd_error(x, "[%s] received disconnection event", objName); } } // this get's never called static void udpserver_connectpoll(t_udpserver *x) { struct sockaddr_in incomer_address; unsigned int sockaddrl = sizeof( struct sockaddr ); int fd = -1; int i; // TODO: provide a way to not accept connection // idea: add a message "accept $1" to turn off/on acceptance of new connections fd = accept(x->x_connectsocket, (struct sockaddr*)&incomer_address, &sockaddrl); bug("connectpoll"); if (fd < 0) error("[%s] accept failed", objName); else { unsigned long host = ntohl(incomer_address.sin_addr.s_addr); unsigned short port = ntohs(incomer_address.sin_port); t_udpserver_sender *y = udpserver_sender_new(x, host, port); if (!y) { sys_closesocket(fd); return; } x->x_nconnections++; i = x->x_nconnections - 1; x->x_sr[i] = y; udpserver_info_connection(x, y); } outlet_float(x->x_connectout, x->x_nconnections); } static void udpserver_port(t_udpserver*x, t_floatarg fportno) { static t_atom ap[1]; int portno = fportno; struct sockaddr_in server; socklen_t serversize=sizeof(server); int sockfd = x->x_connectsocket; SETFLOAT(ap, -1); if(x->x_port == portno) { return; } /* cleanup any open ports */ if(sockfd>=0) { //sys_rmpollfn(sockfd); sys_closesocket(sockfd); x->x_connectsocket=-1; x->x_port=-1; } sockfd = socket(AF_INET, SOCK_DGRAM, 0); server.sin_family = AF_INET; /* LATER allow setting of inaddr */ server.sin_addr.s_addr = INADDR_ANY; /* assign server port number */ server.sin_port = htons((u_short)portno); /* name the socket */ if (bind(sockfd, (struct sockaddr *)&server, serversize) < 0) { sys_sockerror("udpserver: bind"); sys_closesocket(sockfd); outlet_anything(x->x_statout, gensym("port"), 1, ap); return; } x->x_receiver=iemnet__receiver_create(sockfd, x, udpserver_receive_callback); x->x_connectsocket = sockfd; x->x_port = portno; // find out which port is actually used (useful when assigning "0") if(!getsockname(sockfd, (struct sockaddr *)&server, &serversize)) { x->x_port=ntohs(server.sin_port); } SETFLOAT(ap, x->x_port); outlet_anything(x->x_statout, gensym("port"), 1, ap); } static void *udpserver_new(t_floatarg fportno) { t_udpserver *x; int i; x = (t_udpserver *)pd_new(udpserver_class); x->x_msgout = outlet_new(&x->x_obj, 0); /* 1st outlet for received data */ x->x_connectout = outlet_new(&x->x_obj, gensym("float")); /* 2nd outlet for number of connected clients */ x->x_sockout = outlet_new(&x->x_obj, gensym("float")); x->x_addrout = outlet_new(&x->x_obj, gensym("list" )); x->x_statout = outlet_new(&x->x_obj, 0);/* 5th outlet for everything else */ x->x_connectsocket = -1; x->x_port = -1; x->x_nconnections = 0; for(i = 0; i < MAX_CONNECT; i++) { x->x_sr[i] = NULL; } x->x_defaulttarget=0; x->x_floatlist=iemnet__floatlist_create(1024); udpserver_port(x, fportno); x->x_accept=1; return (x); } static void udpserver_free(t_udpserver *x) { int i; for(i = 0; i < MAX_CONNECT; i++) { if (NULL!=x->x_sr[i]) { DEBUG("[%s] free %x", objName, x); udpserver_sender_free(x->x_sr[i]); x->x_sr[i]=NULL; } } if (x->x_connectsocket >= 0) { //sys_rmpollfn(x->x_connectsocket); sys_closesocket(x->x_connectsocket); } if(x->x_floatlist)iemnet__floatlist_destroy(x->x_floatlist);x->x_floatlist=NULL; } IEMNET_EXTERN void udpserver_setup(void) { if(!iemnet__register(objName))return; error("[%s] does not work yet", objName); udpserver_class = class_new(gensym(objName),(t_newmethod)udpserver_new, (t_method)udpserver_free, sizeof(t_udpserver), 0, A_DEFFLOAT, 0); class_addmethod(udpserver_class, (t_method)udpserver_disconnect_client, gensym("disconnectclient"), A_DEFFLOAT, 0); class_addmethod(udpserver_class, (t_method)udpserver_disconnect_socket, gensym("disconnectsocket"), A_DEFFLOAT, 0); class_addmethod(udpserver_class, (t_method)udpserver_disconnect_all, gensym("disconnect"), 0); class_addmethod(udpserver_class, (t_method)udpserver_accept, gensym("accept"), A_FLOAT, 0); class_addmethod(udpserver_class, (t_method)udpserver_send_socket, gensym("send"), A_GIMME, 0); class_addmethod(udpserver_class, (t_method)udpserver_send_client, gensym("client"), A_GIMME, 0); class_addmethod(udpserver_class, (t_method)udpserver_broadcast, gensym("broadcast"), A_GIMME, 0); class_addmethod(udpserver_class, (t_method)udpserver_defaulttarget, gensym("target"), A_DEFFLOAT, 0); class_addmethod(udpserver_class, (t_method)udpserver_targetsocket, gensym("targetsocket"), A_DEFFLOAT, 0); class_addlist (udpserver_class, (t_method)udpserver_defaultsend); class_addmethod(udpserver_class, (t_method)udpserver_port, gensym("port"), A_DEFFLOAT, 0); class_addbang (udpserver_class, (t_method)udpserver_info); DEBUGMETHOD(udpserver_class); } IEMNET_INITIALIZER(udpserver_setup); /* end of udpserver.c */ iemnet-0.1/iemnet_data.h0000644000175100001440000001471111357112433015026 0ustar zmoelnigusers/* *********************************************+ * iemnet * networking for Pd * * (c) 2010 IOhannes m zmölnig * Institute of Electronic Music and Acoustics (IEM) * University of Music and Dramatic Arts (KUG), Graz, Austria * * data handling structures * think of these as private, no need to worry about them outside the core lib */ /* ---------------------------------------------------------------------------- */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version 2 */ /* of the License, or (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* */ /* ---------------------------------------------------------------------------- */ #ifndef INCLUDE__IEMNET_DATA_H_ #define INCLUDE__IEMNET_DATA_H_ /** * a resizable list of float-only atoms */ typedef struct _iemnet_floatlist { t_atom*argv; size_t argc; size_t size; // real size (might be bigger than argc) } t_iemnet_floatlist; /** * create a list of float-only atoms * * \param size initial size of the floatlist * \return pointer to a float-atom list or NULL of creation failed */ t_iemnet_floatlist*iemnet__floatlist_create(unsigned int size); /** * destroy a list of float-only atoms * * \param pointer to a float-atom list */ void iemnet__floatlist_destroy(t_iemnet_floatlist*cl); /** * chunk of data as sent to a socket or received from it * for received data, this might additionally hold the originator (if available) */ typedef struct _iemnet_chunk { unsigned char* data; size_t size; long addr; unsigned short port; } t_iemnet_chunk; /** * free a "chunk" (de-allocate memory,...) */ void iemnet__chunk_destroy(t_iemnet_chunk*); /** * initialize a "chunk" (allocate memory,...) of fixed size * receiver address will be set to 0 * * \param size of the chunk (data will be zeroed out) * \return a new chunk of given size */ t_iemnet_chunk*iemnet__chunk_create_empty(int); /** * initialize a "chunk" (allocate memory,...) with given data * receiver address will be set to 0 * * \param size of data * \param data of size * \return a new chunk that holds a copy of data */ t_iemnet_chunk*iemnet__chunk_create_data(int size, unsigned char*data); /** * initialize a "chunk" (allocate memory,...) with given data from specified address * \param size of data * \param data of size * \param addr originating address (can be NULL) * \return a new chunk that holds a copy of data */ t_iemnet_chunk*iemnet__chunk_create_dataaddr(int size, unsigned char*data, struct sockaddr_in*addr); /** * initialize a "chunk" (allocate memory,...) with given data * receiver address will be set to 0 * * \param argc size of list * \param argv list of atoms containing only "bytes" (t_floats [0..255]) * \return a new chunk that holds a copy of the list data */ t_iemnet_chunk*iemnet__chunk_create_list(int argc, t_atom*argv); /** * initialize a "chunk" (allocate memory,...) from another chunk * * \param src the source chunk * \return a new chunk that holds a copy of the source data */ t_iemnet_chunk*iemnet__chunk_create_chunk(t_iemnet_chunk*source); /** * convert a data chunk to a Pd-list of A_FLOATs * the destination list will eventually be resized if it is too small to hold the chunk * * \param c the chunk to convert * \param dest the destination list * \return the destination list if all went well, else NULL */ t_iemnet_floatlist*iemnet__chunk2list(t_iemnet_chunk*c, t_iemnet_floatlist*dest); /** * opaque type for a thread safe queue (FIFO) */ typedef struct _iemnet_queue t_iemnet_queue; EXTERN_STRUCT _iemnet_queue; /** * push data to the FIFO (queue) * * \param q the queue to push to * \param d the pushed data (the queue will only store the pointer to the data; so don't free it yet) * \return the fill state of the queue after the push * * \note thread safe */ int queue_push(t_iemnet_queue* const q, t_iemnet_chunk* const d); /** * \brief pop data from the FIFO (queue), blocking * * pops data from the stack; * if the stack is empty, this function will block until data is pushed to the stack * if the queue is finalized, this function will return immediately with NULL * * \param q the queue to pop from * \return pointer to the popped data; the caller is responsible for freeing the chunk * * \note thread safe */ t_iemnet_chunk* queue_pop_block(t_iemnet_queue* const q); /** * pop data from the stack (queue), non-blocking * * pops data from the stack; if the stack is empty, this function will immediately return NULL * * \param q the queue to pop from * \return pointer to the popped data or NULL; the caller is responsible for freeing the chunk * * \note thread safe */ t_iemnet_chunk* queue_pop_noblock(t_iemnet_queue* const); /** * get size if queue * * \param q the queue to get the size of * \return the fill state of the queue, -1 if something goes wrong * * \note thread safe */ int queue_getsize(t_iemnet_queue* const q); /** * initiate cleanup process * * unblocks all blocking calls to queue_pop_block(t_iemnet_queue* const q); * * \param q the queue to unblock */ void queue_finish(t_iemnet_queue* q); /** * destroy queue (FIFO) * * releases all data in the queue (by calling iemnet__chunk_destroy()) and then frees all other ressources attached to the queue * * \param q the queue to destroy */ void queue_destroy(t_iemnet_queue* q); /** * create a queue (FIFO) * * \return the newly created queue; if something went wrong NULL is returned */ t_iemnet_queue* queue_create(void); #endif /* INCLUDE__IEMNET_DATA_H_ */ iemnet-0.1/tcpreceive.c0000644000175100001440000002415611454632564014716 0ustar zmoelnigusers/* tcpreceive.c * copyright (c) 2010 IOhannes m zmölnig, IEM * copyright (c) 2006-2010 Martin Peach * copyright (c) Miller Puckette */ /* */ /* A client for unidirectional communication from within Pd. */ /* */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version 2 */ /* of the License, or (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* */ #define DEBUGLEVEL 1 static const char*objName="tcpreceive"; #include "iemnet.h" #ifndef _WIN32 /* needed for TCP_NODELAY */ # include #endif /* ----------------------------- tcpreceive ------------------------- */ static t_class *tcpreceive_class; #define MAX_CONNECTIONS 128 // this is going to cause trouble down the line...:( typedef struct _tcpconnection { long addr; unsigned short port; int socket; struct _tcpreceive*owner; t_iemnet_receiver*receiver; } t_tcpconnection; typedef struct _tcpreceive { t_object x_obj; t_outlet *x_msgout; t_outlet *x_addrout; t_outlet *x_connectout; t_outlet *x_statout; int x_connectsocket; int x_port; int x_serialize; int x_nconnections; t_tcpconnection x_connection[MAX_CONNECTIONS]; t_iemnet_floatlist *x_floatlist; } t_tcpreceive; static int tcpreceive_find_socket(t_tcpreceive *x, int fd) { int i; for (i = 0; i < MAX_CONNECTIONS; ++i) if (x->x_connection[i].socket == fd)return i; return -1; } static int tcpreceive_disconnect(t_tcpreceive *x, int id); static void tcpreceive_read_callback(void *w, t_iemnet_chunk*c) { t_tcpconnection*y=(t_tcpconnection*)w; t_tcpreceive*x=NULL; int index=-1; if(NULL==y || NULL==(x=y->owner))return; index=tcpreceive_find_socket(x, y->socket); if(index>=0) { if(c) { // TODO?: outlet info about connection x->x_floatlist=iemnet__chunk2list(c, x->x_floatlist); // gets destroyed in the dtor iemnet__streamout(x->x_msgout, x->x_floatlist->argc, x->x_floatlist->argv, x->x_serialize); } else { // disconnected tcpreceive_disconnect(x, index); } } } /* tcpreceive_addconnection tries to add the socket fd to the list */ /* returns 1 on success, else 0 */ static int tcpreceive_addconnection(t_tcpreceive *x, int fd, long addr, unsigned short port) { int i; for (i = 0; i < MAX_CONNECTIONS; ++i) { if (x->x_connection[i].socket == -1) { x->x_connection[i].socket = fd; x->x_connection[i].addr = addr; x->x_connection[i].port = port; x->x_connection[i].owner = x; x->x_connection[i].receiver= iemnet__receiver_create(fd, x->x_connection+i, tcpreceive_read_callback); return 1; } } return 0; } /* tcpreceive_connectpoll checks for incoming connection requests on the original socket */ /* a new socket is assigned */ static void tcpreceive_connectpoll(t_tcpreceive *x) { struct sockaddr_in from; socklen_t fromlen = sizeof(from); long addr; unsigned short port; int fd; fd = accept(x->x_connectsocket, (struct sockaddr *)&from, &fromlen); if (fd < 0) error("[%s] accept failed", objName); else { // t_socketreceiver *y = socketreceiver_new((void *)x, // (t_socketnotifier)tcpreceive_notify, // 0, 0); /* get the sender's ip */ addr = ntohl(from.sin_addr.s_addr); port = ntohs(from.sin_port); if (tcpreceive_addconnection(x, fd, addr, port)) { x->x_nconnections++; outlet_float(x->x_connectout, x->x_nconnections); iemnet__addrout(x->x_statout, x->x_addrout, addr, port); } else { error ("[%s] Too many connections", objName); sys_closesocket(fd); } } } static int tcpreceive_disconnect(t_tcpreceive *x, int id) { if(id>=0 && id < MAX_CONNECTIONS && x->x_connection[id].port>0) { iemnet__receiver_destroy(x->x_connection[id].receiver); x->x_connection[id].receiver=NULL; sys_closesocket(x->x_connection[id].socket); x->x_connection[id].socket = -1; x->x_connection[id].addr = 0L; x->x_connection[id].port = 0; x->x_nconnections--; outlet_float(x->x_connectout, x->x_nconnections); return 1; } return 0; } /* tcpreceive_closeall closes all open sockets and deletes them from the list */ static void tcpreceive_disconnect_all(t_tcpreceive *x) { int i; for (i = 0; i < MAX_CONNECTIONS; i++) { tcpreceive_disconnect(x, i); } } /* tcpreceive_removeconnection tries to delete the socket fd from the list */ /* returns 1 on success, else 0 */ static int tcpreceive_disconnect_socket(t_tcpreceive *x, int fd) { int i; for (i = 0; i < MAX_CONNECTIONS; ++i) { if (x->x_connection[i].socket == fd) { return tcpreceive_disconnect(x, i); } } return 0; } static void tcpreceive_port(t_tcpreceive*x, t_floatarg fportno) { static t_atom ap[1]; int portno = fportno; struct sockaddr_in server; socklen_t serversize=sizeof(server); int sockfd = x->x_connectsocket; int intarg; SETFLOAT(ap, -1); if(x->x_port == portno) { return; } /* cleanup any open ports */ if(sockfd>=0) { sys_rmpollfn(sockfd); sys_closesocket(sockfd); x->x_connectsocket=-1; x->x_port=-1; } sockfd = socket(AF_INET, SOCK_STREAM, 0); if(sockfd<0) { error("[%s]: unable to create socket", objName); return; } /* ask OS to allow another Pd to reopen this port after we close it. */ #ifdef SO_REUSEADDR intarg = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&intarg, sizeof(intarg)) < 0) { error("[%s]: setsockopt (SO_REUSEADDR) failed", objName); } #endif /* SO_REUSEADDR */ #ifdef SO_REUSEPORT intarg = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (char *)&intarg, sizeof(intarg)) < 0) { error("[%s]: setsockopt (SO_REUSEPORT) failed", objName); } #endif /* SO_REUSEPORT */ /* Stream (TCP) sockets are set NODELAY */ intarg = 1; if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&intarg, sizeof(intarg)) < 0) post("[%s]: setsockopt (TCP_NODELAY) failed", objName); server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons((u_short)portno); /* name the socket */ if (bind(sockfd, (struct sockaddr *)&server, serversize) < 0) { sys_sockerror("[tcpreceive] bind failed"); sys_closesocket(sockfd); sockfd = -1; outlet_anything(x->x_statout, gensym("port"), 1, ap); return; } /* streaming protocol */ if (listen(sockfd, 5) < 0) { sys_sockerror("[tcpreceive] listen"); sys_closesocket(sockfd); sockfd = -1; outlet_anything(x->x_statout, gensym("port"), 1, ap); return; } else { sys_addpollfn(sockfd, (t_fdpollfn)tcpreceive_connectpoll, x); // wait for new connections } x->x_connectsocket = sockfd; x->x_port = portno; // find out which port is actually used (useful when assigning "0") if(!getsockname(sockfd, (struct sockaddr *)&server, &serversize)) { x->x_port=ntohs(server.sin_port); } SETFLOAT(ap, x->x_port); outlet_anything(x->x_statout, gensym("port"), 1, ap); } static void tcpreceive_serialize(t_tcpreceive *x, t_floatarg doit) { x->x_serialize=doit; } static void tcpreceive_free(t_tcpreceive *x) { /* is this ever called? */ if (x->x_connectsocket >= 0) { sys_rmpollfn(x->x_connectsocket); sys_closesocket(x->x_connectsocket); } tcpreceive_disconnect_all(x); if(x->x_floatlist)iemnet__floatlist_destroy(x->x_floatlist);x->x_floatlist=NULL; } static void *tcpreceive_new(t_floatarg fportno) { t_tcpreceive *x; int portno = fportno; int i; x = (t_tcpreceive *)pd_new(tcpreceive_class); x->x_msgout = outlet_new(&x->x_obj, 0); x->x_addrout = outlet_new(&x->x_obj, gensym("list")); /* legacy */ x->x_connectout = outlet_new(&x->x_obj, gensym("float")); /* legacy */ x->x_statout = outlet_new(&x->x_obj, 0);/* outlet for everything else */ x->x_serialize=1; x->x_connectsocket=-1; x->x_port=-1; x->x_nconnections=0; /* clear the connection list */ for (i = 0; i < MAX_CONNECTIONS; ++i) { x->x_connection[i].socket = -1; x->x_connection[i].addr = 0L; x->x_connection[i].port = 0; } x->x_floatlist=iemnet__floatlist_create(1024); tcpreceive_port(x, portno); return (x); } IEMNET_EXTERN void tcpreceive_setup(void) { if(!iemnet__register(objName))return; tcpreceive_class = class_new(gensym(objName), (t_newmethod)tcpreceive_new, (t_method)tcpreceive_free, sizeof(t_tcpreceive), 0, A_DEFFLOAT, 0); class_addmethod(tcpreceive_class, (t_method)tcpreceive_port, gensym("port"), A_DEFFLOAT, 0); class_addmethod(tcpreceive_class, (t_method)tcpreceive_serialize, gensym("serialize"), A_FLOAT, 0); DEBUGMETHOD(tcpreceive_class); } IEMNET_INITIALIZER(tcpreceive_setup); /* end x_net_tcpreceive.c */ iemnet-0.1/tests/0000755000175100001440000000000011456321231013537 5ustar zmoelnigusersiemnet-0.1/tests/sequence/0000755000175100001440000000000011456321231015347 5ustar zmoelnigusersiemnet-0.1/tests/sequence/01_client.pd0000644000175100001440000000541311354650660017465 0ustar zmoelnigusers#N canvas 501 150 636 624 10; #X obj 139 179 tcpclient; #X msg 308 152 connect \$1 9999; #X msg 308 108 bang; #X obj 308 130 symbol localhost; #X symbolatom 351 112 10 0 0 0 - - -; #X obj 159 201 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 127 127; #X obj 139 284 list split 1; #X obj 139 242 until; #X obj 139 263 list append; #X obj 139 219 t b l; #X obj 183 243 bang; #X obj 139 306 select 255; #X obj 139 328 i; #X obj 139 350 + 1; #X obj 139 372 t f f; #X obj 121 398 f; #X obj 114 108 metro 100; #X obj 114 85 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X msg 114 130 1; #X obj 79 323 t b b; #X msg 108 345 0; #X floatatom 121 555 5 0 0 3 dummyload - -; #X obj 253 378 -; #X obj 252 354 t f f; #X obj 253 400 select 1; #X obj 306 489 print error; #X obj 306 443 pack 0 0 0; #X obj 185 426 t f f; #X obj 373 416 + 1; #X obj 185 448 select 254; #X msg 185 470 -1; #X obj 206 328 t b f f f; #X msg 306 465 \$2 should be \$3; #X obj 306 421 t b b; #X obj 459 485 i; #X obj 459 507 + 1; #X obj 459 529 t f f; #X floatatom 459 551 5 0 0 3 errors - -; #X text 49 27 testing the server; #X floatatom 188 78 5 0 0 0 - - -; #X obj 55 394 i; #X obj 55 416 + 1; #X obj 55 438 t f f; #X floatatom 22 481 5 0 0 3 iterations - -; #X msg 56 372 0; #X obj 52 125 t b b; #X obj 52 147 timer; #X floatatom 52 169 5 0 0 0 - - -; #X obj 22 458 i; #X obj 14 344 metro 100; #X msg 14 366 bang; #X obj 16 325 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X connect 0 0 9 0; #X connect 0 1 5 0; #X connect 1 0 0 0; #X connect 2 0 3 0; #X connect 3 0 1 0; #X connect 4 0 3 0; #X connect 6 0 11 0; #X connect 6 1 8 1; #X connect 6 2 10 0; #X connect 7 0 8 0; #X connect 8 0 6 0; #X connect 9 0 7 0; #X connect 9 1 8 1; #X connect 10 0 7 1; #X connect 11 0 12 0; #X connect 11 1 31 0; #X connect 12 0 13 0; #X connect 13 0 14 0; #X connect 14 0 15 1; #X connect 14 1 12 1; #X connect 15 0 21 0; #X connect 16 0 18 0; #X connect 16 0 45 0; #X connect 17 0 16 0; #X connect 18 0 0 0; #X connect 19 0 15 0; #X connect 19 1 20 0; #X connect 19 1 40 0; #X connect 20 0 12 1; #X connect 22 0 24 0; #X connect 23 0 22 1; #X connect 23 1 22 0; #X connect 24 1 33 0; #X connect 26 0 32 0; #X connect 27 0 29 0; #X connect 27 1 28 0; #X connect 28 0 26 2; #X connect 29 0 30 0; #X connect 30 0 22 1; #X connect 31 0 19 0; #X connect 31 1 27 0; #X connect 31 2 23 0; #X connect 31 3 26 1; #X connect 32 0 25 0; #X connect 33 0 26 0; #X connect 33 1 34 0; #X connect 34 0 35 0; #X connect 35 0 36 0; #X connect 36 0 37 0; #X connect 36 1 34 1; #X connect 39 0 16 1; #X connect 40 0 41 0; #X connect 41 0 42 0; #X connect 42 0 48 1; #X connect 42 1 40 1; #X connect 44 0 40 0; #X connect 45 0 46 0; #X connect 45 1 46 1; #X connect 46 0 47 0; #X connect 48 0 43 0; #X connect 49 0 50 0; #X connect 50 0 48 0; #X connect 51 0 49 0; iemnet-0.1/tests/sequence/01_server.pd0000644000175100001440000000305311355064121017503 0ustar zmoelnigusers#N canvas 219 165 438 532 10; #X obj 71 256 tcpserver 9999; #X obj 71 278 t b; #X obj 71 300 i; #X obj 115 326 + 1; #X obj 115 348 % 255; #X obj 250 252 loadbang; #X msg 71 388 broadcast \$1; #X obj 71 429 list append; #X obj 250 274 f 1024; #X obj 250 296 t b f b; #X obj 250 438 list prepend; #X obj 273 319 until; #X obj 273 341 f 255; #X obj 273 363 list prepend; #X obj 273 385 t l l; #X msg 106 276 bang; #X msg 321 250 bang; #X obj 71 451 list trim; #X obj 23 229 t a; #X text 27 43 responds to any data from the client by sending pack a stream consisting of a header and a dummy payload.; #X text 31 84 the dummy payload is a number of 0xFF bytes; #X obj 71 322 t f f; #X text 32 99 the header is a single byte counting up from 0..254 (and wrapping around); #X floatatom 166 305 5 0 0 0 - - -; #X obj 152 169 route sent; #X obj 152 191 unpack 0 0 0; #X floatatom 186 225 0 0 0 0 - - -; #X floatatom 221 211 5 0 0 0 - - -; #X connect 0 0 1 0; #X connect 0 4 24 0; #X connect 1 0 2 0; #X connect 2 0 21 0; #X connect 3 0 4 0; #X connect 4 0 2 1; #X connect 5 0 8 0; #X connect 6 0 7 0; #X connect 7 0 17 0; #X connect 8 0 9 0; #X connect 9 0 10 0; #X connect 9 1 11 0; #X connect 10 0 7 1; #X connect 11 0 12 0; #X connect 12 0 13 0; #X connect 13 0 14 0; #X connect 14 0 10 1; #X connect 14 1 13 1; #X connect 15 0 1 0; #X connect 16 0 8 0; #X connect 17 0 18 0; #X connect 18 0 0 0; #X connect 21 0 6 0; #X connect 21 1 3 0; #X connect 23 0 3 1; #X connect 24 0 25 0; #X connect 25 1 26 0; #X connect 25 2 27 0; iemnet-0.1/iemnet_sender.c0000644000175100001440000001315311445657050015376 0ustar zmoelnigusers/* iemnet * * sender * sends data "chunks" to a socket * possibly threaded * * copyright (c) 2010 IOhannes m zmölnig, IEM */ #define DEBUGLEVEL 2 #include "iemnet.h" #include "iemnet_data.h" #include #include #include #include #include #ifdef _WIN32 # include # include /* for socklen_t */ #else # include #endif #include #if IEMNET_HAVE_DEBUG static int debug_lockcount=0; # define LOCK(x) do {pthread_mutex_lock(x);debug_lockcount++; if(debuglevel&DEBUGLEVEL)post(" LOCK %d (@%s:%d)", debug_lockcount, __FILE__, __LINE__); } while(0) # define UNLOCK(x) do {debug_lockcount--;if(debuglevel&DEBUGLEVEL)post("UNLOCK %d (@%s:%d)", debug_lockcount, __FILE__, __LINE__);pthread_mutex_unlock(x);}while(0) #else # define LOCK(x) pthread_mutex_lock(x) # define UNLOCK(x) pthread_mutex_unlock(x) #endif /* draft: * - there is a sender thread for each open connection * - the main thread just adds chunks to each sender threads processing queue * - the sender thread tries to send the queue as fast as possible */ struct _iemnet_sender { pthread_t thread; int sockfd; /* owned outside; must call iemnet__sender_destroy() before freeing socket yourself */ t_iemnet_queue*queue; int keepsending; // indicates whether we want to thread to continue or to terminate int isrunning; pthread_mutex_t mtx; /* mutex to protect isrunning,.. */ }; /* the workhorse of the family */ static int iemnet__sender_dosend(int sockfd, t_iemnet_queue*q) { struct sockaddr_in to; socklen_t tolen = sizeof(to); t_iemnet_chunk*c=queue_pop_block(q); if(c) { unsigned char*data=c->data; unsigned int size=c->size; int result=-1; // fprintf(stderr, "sending %d bytes at %x to %d\n", size, data, sockfd); if(c->port) { DEBUG("sending %d bytes to %x:%d", size, c->addr, c->port); to.sin_addr.s_addr=htonl(c->addr); to.sin_port =htons(c->port); result = sendto(sockfd, data, size, 0, (struct sockaddr *)&to, tolen); } else { DEBUG("sending %d bytes", size); result = send(sockfd, data, size, 0); } if(result<0) { // broken pipe return 0; } // shouldn't we do something with the result here? DEBUG("sent %d bytes", result); iemnet__chunk_destroy(c); } else { return 0; } return 1; } static void*iemnet__sender_sendthread(void*arg) { t_iemnet_sender*sender=(t_iemnet_sender*)arg; int sockfd=sender->sockfd; t_iemnet_queue*q=sender->queue; while(sender->keepsending) { if(!iemnet__sender_dosend(sockfd, q))break; } LOCK (&sender->mtx); sender->isrunning=0; UNLOCK (&sender->mtx); DEBUG("send thread terminated"); return NULL; } int iemnet__sender_send(t_iemnet_sender*s, t_iemnet_chunk*c) { t_iemnet_queue*q=0; int size=-1; LOCK (&s->mtx); q=s->queue; if(!s->isrunning) { UNLOCK (&s->mtx); return -1; } UNLOCK (&s->mtx); if(q) { t_iemnet_chunk*chunk=iemnet__chunk_create_chunk(c); size = queue_push(q, chunk); } return size; } void iemnet__sender_destroy(t_iemnet_sender*s) { int sockfd=-1; /* simple protection against recursive calls: * s->keepsending is only set to "0" in here, * so if it is false, we know that we are already being called */ DEBUG("destroy sender %x with queue %x (%d)", s, s->queue, s->keepsending); LOCK (&s->mtx); sockfd=s->sockfd; // check s->isrunning DEBUG("keepsending %d\tisrunning %d", s->keepsending, s->isrunning); if(!s->keepsending) { UNLOCK (&s->mtx); return; } s->keepsending=0; UNLOCK (&s->mtx); queue_finish(s->queue); DEBUG("queue finished"); if(sockfd>=0) { int err=shutdown(sockfd, 2); /* needed on linux, since the recv won't shutdown on sys_closesocket() alone */ sys_closesocket(sockfd); } pthread_join(s->thread, NULL); DEBUG("thread joined"); queue_destroy(s->queue); pthread_mutex_destroy (&s->mtx); memset(s, 0, sizeof(t_iemnet_sender)); free(s); s=NULL; DEBUG("destroyed sender"); } t_iemnet_sender*iemnet__sender_create(int sock) { static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; t_iemnet_sender*result=(t_iemnet_sender*)malloc(sizeof(t_iemnet_sender)); int res=0; DEBUG("create sender %x", result); if(NULL==result){ DEBUG("create sender failed"); return NULL; } result->queue = queue_create(); result->sockfd = sock; result->keepsending =1; result->isrunning=1; DEBUG("create_sender queue=%x", result->queue); memcpy(&result->mtx , &mtx, sizeof(pthread_mutex_t)); res=pthread_create(&result->thread, 0, iemnet__sender_sendthread, result); if(0==res) { } else { // something went wrong } DEBUG("created sender"); return result; } int iemnet__sender_getlasterror(t_iemnet_sender*x) { x=NULL; #ifdef _WIN32 return WSAGetLastError(); #endif return errno; } int iemnet__sender_getsockopt(t_iemnet_sender*s, int level, int optname, void *optval, socklen_t*optlen) { int result=getsockopt(s->sockfd, level, optname, optval, optlen); if(result!=0) { post("%s: getsockopt returned %d", __FUNCTION__, iemnet__sender_getlasterror(s)); } return result; } int iemnet__sender_setsockopt(t_iemnet_sender*s, int level, int optname, const void*optval, socklen_t optlen) { int result=setsockopt(s->sockfd, level, optname, optval, optlen); if(result!=0) { post("%s: setsockopt returned %d", __FUNCTION__, iemnet__sender_getlasterror(s)); } return result; } int iemnet__sender_getsize(t_iemnet_sender*x) { int size=-1; if(x && x->queue) size=queue_getsize(x->queue); return size; } iemnet-0.1/udpsend.c0000644000175100001440000001231611445657050014217 0ustar zmoelnigusers/* udpsend.c * copyright (c) 2010 IOhannes m zmölnig, IEM * copyright (c) 2006-2010 Martin Peach * copyright (c) Miller Puckette */ /* */ /* A client for unidirectional communication from within Pd. */ /* */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version 2 */ /* of the License, or (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* */ #define DEBUGLEVEL 1 static const char objName[] = "udpsend"; #include "iemnet.h" #include static t_class *udpsend_class; typedef struct _udpsend { t_object x_obj; t_iemnet_sender*x_sender; } t_udpsend; static void udpsend_connect(t_udpsend *x, t_symbol *hostname, t_floatarg fportno) { struct sockaddr_in server; int sockfd; int portno = fportno; int broadcast = 1;/* nonzero is true */ if (x->x_sender) { error("[%s] already connected", objName); return; } /* create a socket */ sockfd = socket(AF_INET, SOCK_DGRAM, 0); DEBUG("send socket %d\n", sockfd); if (sockfd < 0) { sys_sockerror("[udpsend] socket"); return; } /* Based on zmoelnig's patch 2221504: Enable sending of broadcast messages (if hostname is a broadcast address)*/ #ifdef SO_BROADCAST if( 0 != setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (const void *)&broadcast, sizeof(broadcast))) { error("[%s] couldn't switch to broadcast mode", objName); } #endif /* SO_BROADCAST */ /* connect socket using hostname provided in command line */ server.sin_family = AF_INET; do { #if 0 struct addrinfo * addr=NULL; if(getaddrinfo(hostname->s_name, NULL, NULL, &addr)) { error("[%s] bad host '%s'?", objName, hostname->s_name); return; } else { struct addrinfo * res; for (res = addr; res != NULL; res = res->ai_next) { struct sockaddr_in *sa = (struct sockaddr_in *) res->ai_addr; int len = res->ai_addrlen; // memcpy((char *)&server.sin_addr, (char *)res->ai_addr, hp->h_length); // LATER check how to do that... } } freeaddrinfo(addr); #else struct hostent *hp = gethostbyname(hostname->s_name); if (hp == 0) { error("[%s] bad host '%s'?", objName, hostname->s_name); return; } memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); #endif } while(0); /* assign client port number */ server.sin_port = htons((u_short)portno); DEBUG("connecting to port %d", portno); /* try to connect. */ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) { sys_sockerror("[udpsend] connecting stream socket"); sys_closesocket(sockfd); return; } x->x_sender=iemnet__sender_create(sockfd); outlet_float(x->x_obj.ob_outlet, 1); } static void udpsend_disconnect(t_udpsend *x) { if(x->x_sender) { iemnet__sender_destroy(x->x_sender); x->x_sender=NULL; outlet_float(x->x_obj.ob_outlet, 0); } } static void udpsend_send(t_udpsend *x, t_symbol *s, int argc, t_atom *argv) { if(x->x_sender) { t_iemnet_chunk*chunk=iemnet__chunk_create_list(argc, argv); iemnet__sender_send(x->x_sender, chunk); iemnet__chunk_destroy(chunk); } else { error("[%s]: not connected", objName); } } static void udpsend_free(t_udpsend *x) { udpsend_disconnect(x); } static void *udpsend_new(void) { t_udpsend *x = (t_udpsend *)pd_new(udpsend_class); outlet_new(&x->x_obj, gensym("float")); x->x_sender=NULL; return (x); } IEMNET_EXTERN void udpsend_setup(void) { if(!iemnet__register(objName))return; udpsend_class = class_new(gensym(objName), (t_newmethod)udpsend_new, (t_method)udpsend_free, sizeof(t_udpsend), 0, 0); class_addmethod(udpsend_class, (t_method)udpsend_connect, gensym("connect"), A_SYMBOL, A_FLOAT, 0); class_addmethod(udpsend_class, (t_method)udpsend_disconnect, gensym("disconnect"), 0); class_addmethod(udpsend_class, (t_method)udpsend_send, gensym("send"), A_GIMME, 0); class_addlist(udpsend_class, (t_method)udpsend_send); DEBUGMETHOD(udpsend_class); } IEMNET_INITIALIZER(udpsend_setup); /* end udpsend.c*/ iemnet-0.1/tcpclient-help.pd0000644000175100001440000000572511453711672015657 0ustar zmoelnigusers#N canvas 3 62 1018 535 12; #X msg 134 135 disconnect; #X obj 255 328 unpack 0 0 0 0; #X floatatom 255 351 3 0 0 0 - - -; #X floatatom 286 351 3 0 0 0 - - -; #X floatatom 318 351 3 0 0 0 - - -; #X floatatom 350 351 3 0 0 0 - - -; #X text 219 351 from; #X msg 71 72 connect 132.205.142.12 80; #X obj 235 256 tcpclient; #X obj 275 307 tgl 15 0 empty empty connected 18 7 0 8 -24198 -241291 -1 0 1; #X text 187 21 connect with an IP address and port number; #X msg 46 47 connect www.concordia.ca 80; #X text 306 255 tcpclient opens a tcp socket to send and receive bytes on; #X text 44 257 See also:; #X msg 21 22 connect 127.0.0.1 9997; #X obj 45 279 tcpreceive; #X text 320 158 semicolon-terminated string for netserver or netreceive ; #X msg 159 160 send 49 127 128 51 59; #X obj 45 302 tcpserver; #X text 593 24 tcpclient can connect to a server and send and receive messages as lists of bytes. Any integer value between 0 and 255 can be transmitted or received.; #X msg 199 184 71 69 84 32 104 116 116 112 58 47 47 47 105 110 100 101 120 46 104 116 109 108 13 10; #X text 226 220 'send' prefix is optional; #X obj 235 402 spigot; #X obj 274 379 tgl 15 0 empty empty enable_print 18 7 0 8 -24198 -241291 -1 0 1; #X floatatom 797 190 9 0 0 0 - - -; #X msg 110 95 send 71 69 84 32 104 116 116 112 58 47 47 47 105 110 100 101 120 46 112 104 112 13 10 13 10; #X text 40 399 2010/03/01 Martin Peach; #X text 418 430 Attempting to print long messages can hang Pd!; #X obj 797 166 route sent; #X text 869 190 sent bytes; #X floatatom 797 347 3 0 0 0 - - -; #X floatatom 851 347 3 0 0 0 - - -; #X floatatom 824 347 3 0 0 0 - - -; #X obj 797 268 route address; #X obj 797 296 unpack f f f f f; #X floatatom 878 347 3 0 0 0 - - -; #X floatatom 906 321 6 0 0 0 - - -; #X text 904 347 ip; #X text 952 320 port; #X obj 235 431 print tcpclient:received; #X obj 520 344 spigot; #X obj 559 321 tgl 15 0 empty empty enable_print 18 7 0 8 -24198 -241291 -1 0 1; #X obj 520 373 print tcpclient:status; #X text 286 401 Received messages are output as a stream of bytes; #X text 515 103 GET http://index.phpCRLF; #X text 45 477 NOTE: outlet's #2 & #3 are for compatibility with mrpeach's net objects. all information is available via outlet#4 as well!; #X text 41 414 2010/10/08 Roman Haefeli; #X obj 295 281 s \$0.tcpclient.o4; #X obj 520 298 r \$0.tcpclient.o4; #X obj 797 242 r \$0.tcpclient.o4; #X obj 797 142 r \$0.tcpclient.o4; #X connect 0 0 8 0; #X connect 1 0 2 0; #X connect 1 1 3 0; #X connect 1 2 4 0; #X connect 1 3 5 0; #X connect 7 0 8 0; #X connect 8 0 22 0; #X connect 8 1 1 0; #X connect 8 2 9 0; #X connect 8 3 47 0; #X connect 11 0 8 0; #X connect 14 0 8 0; #X connect 17 0 8 0; #X connect 20 0 8 0; #X connect 22 0 39 0; #X connect 23 0 22 1; #X connect 25 0 8 0; #X connect 28 0 24 0; #X connect 33 0 34 0; #X connect 34 0 30 0; #X connect 34 1 32 0; #X connect 34 2 31 0; #X connect 34 3 35 0; #X connect 34 4 36 0; #X connect 40 0 42 0; #X connect 41 0 40 1; #X connect 48 0 40 0; #X connect 49 0 33 0; #X connect 50 0 28 0; iemnet-0.1/tcpreceive-help.pd0000644000175100001440000000364311453711672016020 0ustar zmoelnigusers#N canvas 162 156 680 353 12; #X text 10 10 tcpreceive receives bytes over a tcp connection.; #X msg 20 69 port 10000; #X text 134 104 1st argument: port number; #X text 102 68 reset port number; #X floatatom 54 238 3 0 0 0 - - -; #X floatatom 108 238 3 0 0 0 - - -; #X floatatom 81 238 3 0 0 0 - - -; #X obj 54 187 unpack f f f f f; #X floatatom 135 238 3 0 0 0 - - -; #X floatatom 163 212 6 0 0 0 - - -; #X text 161 238 ip; #X text 209 212 port; #X text 178 188 from; #X obj 20 270 print tcpreceive:received; #X floatatom 88 160 3 0 0 0 - - -; #X text 114 160 connections; #X obj 122 137 s \$0.tcpreceive.o3; #X obj 253 242 spigot; #X obj 292 222 tgl 15 0 empty empty enable_print 17 7 0 10 -24198 -258699 -45076 0 1; #X obj 253 197 r \$0.tcpreceive.o3; #X obj 253 268 print tcpreceive:status; #X floatatom 483 121 3 0 0 0 - - -; #X floatatom 537 121 3 0 0 0 - - -; #X floatatom 510 121 3 0 0 0 - - -; #X obj 483 41 route address; #X obj 483 70 unpack f f f f f; #X floatatom 564 121 3 0 0 0 - - -; #X floatatom 592 95 6 0 0 0 - - -; #X text 590 121 ip; #X text 638 95 port; #X obj 483 15 r \$0.tcpreceive.o3; #X obj 483 165 r \$0.tcpreceive.o3; #X obj 483 191 route port; #X floatatom 483 217 6 0 0 0 - - -; #X text 532 217 port; #X obj 20 105 tcpreceive 9997; #X text 15 310 NOTE: outlet's #2 & #3 are for compatibility with mrpeach's net objects. all information is available via outlet#4 as well!; #X obj 500 286 tcpsend; #X obj 500 311 tcpserver; #X text 499 263 check also:; #X connect 1 0 35 0; #X connect 7 0 4 0; #X connect 7 1 6 0; #X connect 7 2 5 0; #X connect 7 3 8 0; #X connect 7 4 9 0; #X connect 17 0 20 0; #X connect 18 0 17 1; #X connect 19 0 17 0; #X connect 24 0 25 0; #X connect 25 0 21 0; #X connect 25 1 23 0; #X connect 25 2 22 0; #X connect 25 3 26 0; #X connect 25 4 27 0; #X connect 30 0 24 0; #X connect 31 0 32 0; #X connect 32 0 33 0; #X connect 35 0 13 0; #X connect 35 1 7 0; #X connect 35 2 14 0; #X connect 35 3 16 0; iemnet-0.1/iemnet.h0000644000175100001440000002026611454576431014051 0ustar zmoelnigusers/* *********************************************+ * iemnet * networking for Pd * * (c) 2010 IOhannes m zmölnig * Institute of Electronic Music and Acoustics (IEM) * University of Music and Dramatic Arts (KUG), Graz, Austria * * based on net/ library by Martin Peach * based on maxlib by Olaf Matthes */ /* ---------------------------------------------------------------------------- */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version 2 */ /* of the License, or (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* */ /* ---------------------------------------------------------------------------- */ #ifndef INCLUDE_IEMNET_H_ #define INCLUDE_IEMNET_H_ #include "m_pd.h" /* from s_stuff.h */ typedef void (*t_fdpollfn)(void *ptr, int fd); EXTERN void sys_closesocket(int fd); EXTERN void sys_sockerror(char *s); EXTERN void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr); EXTERN void sys_rmpollfn(int fd); #ifdef _WIN32 # include # include #else # include # include # include #endif #include /* iemnet_data.c */ #include "iemnet_data.h" /* iemnet_sender.c */ /** * opaque data type used for sending data over a socket */ typedef struct _iemnet_sender t_iemnet_sender; EXTERN_STRUCT _iemnet_sender; /** * create a sender to a given socket * * \param sock a previously opened socket * \return pointer to a sender object * \note the socket must be writeable */ t_iemnet_sender*iemnet__sender_create(int sock); /** * destroy a sender to a given socket * destroying a sender will free all resources of the sender * * \param pointer to a sender object to be destroyed * * \note it will also close() the socket */ void iemnet__sender_destroy(t_iemnet_sender*); /** * send data over a socket * * \param pointer to a sender object * \param pointer to a chunk of data to be sent * \return the current fill state of the send buffer * * \note the sender creates a local copy of chunk; the caller has to delete their own copy */ int iemnet__sender_send(t_iemnet_sender*, t_iemnet_chunk*); /** * query the fill state of the send buffer * * \param pointer to a sender object * \return the current fill state of the send buffer */ int iemnet__sender_getsize(t_iemnet_sender*); /* iemnet_receiver.c */ /** * opaque data type used for receiving data from a socket */ typedef struct _iemnet_receiver t_iemnet_receiver; EXTERN_STRUCT _iemnet_receiver; /** * callback function for receiving * whenever data arrives at the socket, a callback will be called synchronously */ typedef void (*t_iemnet_receivecallback)(void*userdata, t_iemnet_chunk*rawdata); /** * create a receiver object * * whenever something is received on the socket, the callback is called with the payload in the main thread of the caller * * \param sock the (readable) socket to receive from * \param data user data to be passed to callback * \param callback a callback function that is called on the caller's side * * \note the callback will be scheduled in the caller's thread with clock_delay() */ t_iemnet_receiver*iemnet__receiver_create(int sock, void*data, t_iemnet_receivecallback callback); /** * destroy a receiver at a given socket * destroying a receiver will free all resources of the receiver * * \param pointer to a receiver object to be destroyed * * \note it will also close() the socket */ void iemnet__receiver_destroy(t_iemnet_receiver*); /** * query the fill state of the receive buffer * * \param pointer to a receiver object * \return the current fill state of the receive buffer */ int iemnet__receiver_getsize(t_iemnet_receiver*); /* convenience functions */ /** * output the address (IP, port) * the given address is first output through the status_outlet as a "host" message * and then as a list through the address_outlet * * \param status_outlet outlet for general status messages * \param address_outlet outlet for addresses only * \param address the host ip * \param port the host port * * \note the address will be output as a 5 element list, with the 1st 4 elements denoting the quads of the IP address (as bytes) and the last element the port */ void iemnet__addrout(t_outlet*status_outlet, t_outlet*address_outlet, long address, unsigned short port); /** * output the socket we received data from * the given socket is first output through the status_outlet as a "socket" message * and then as a single number through the socket_outlet * * \param status_outlet outlet for general status messages * \param socket_outlet outlet for sockets only * \param sockfd the socket */ void iemnet__socketout(t_outlet*status_outlet, t_outlet*socket_outlet, int sockfd); /** * output the number of connections * the given number of connections is first output through the status_outlet as a "connections" message * and then as a single number through the numconn_outlet * * \param status_outlet outlet for general status messages * \param address_outlet outlet for numconnections only * \param numconnections the number of connections */ void iemnet__numconnout(t_outlet*status_outlet, t_outlet*numconn_outlet, int numconnections); /** * output a list as a stream (serialize) * * the given list of atoms will be sent to the output one-by-one * * \param outlet outlet to sent the data to * \param argc size of the list * \param argv data * \param stream if true, serialize the data; if false output as "packets" * * \note with stream based protocols (TCP/IP) the length of the received lists has no meaning, so the data has to be serialized anyhow; however when creating proxies, sending serialized data is often slow, so there is an option to disable serialization */ void iemnet__streamout(t_outlet*outlet, int argc, t_atom*argv, int stream); /** * register an objectname and printout a banner * * this will printout a copyright notice * additionally, it will return whether it has already been called for the given name * * \param name an objectname to "register" * \return 1 if this function has been called the first time with the given name, 0 otherwise * */ int iemnet__register(const char*name); #if defined(_MSC_VER) # define snprintf _snprintf # define IEMNET_EXTERN __declspec(dllexport) extern # define CCALL __cdecl # define IEMNET_INITIALIZER(f) \ static void autoinit__ ## f(void) { f(); } #elif defined(__GNUC__) # define IEMNET_EXTERN extern # define CCALL # define IEMNET_INITIALIZER(f) \ static void autoinit__ ## f(void) __attribute__((constructor)); \ static void autoinit__ ## f(void) { f(); } #endif /** * \fn void DEBUG(const char* format,...); * * \brief debug output * \note this will only take effect if DEBUG is not undefined */ #ifdef DEBUG # define IEMNET_HAVE_DEBUG 1 #endif #ifdef IEMNET_HAVE_DEBUG # undef IEMNET_HAVE_DEBUG #endif extern int debuglevel; void iemnet_debuglevel(void*,t_float); #define DEBUGMETHOD(c) class_addmethod(c, (t_method)iemnet_debuglevel, gensym("debug"), A_FLOAT, 0) #ifdef DEBUG # undef DEBUG # define DEBUG if(debuglevel&DEBUGLEVEL)startpost("[%s:%d]", __FUNCTION__, __LINE__); if(debuglevel&DEBUGLEVEL)post #else static void debug_dummy(const char *format, ...) {;} # define DEBUG debug_dummy #endif #endif /* INCLUDE_IEMNET_H_ */ iemnet-0.1/iemnet_data.c0000644000175100001440000001771211445657050015034 0ustar zmoelnigusers/* iemnet * data handling code * - wrappers for data "chunks" * - queues * * copyright (c) 2010 IOhannes m zmölnig, IEM */ #define DEBUGLEVEL 8 #include "iemnet.h" #include "iemnet_data.h" #include #include #include #include #include #define INBUFSIZE 65536L /* was 4096: size of receiving data buffer */ /* data handling */ t_iemnet_floatlist*iemnet__floatlist_init(t_iemnet_floatlist*cl) { unsigned int i; if(NULL==cl)return NULL; for(i=0; isize; i++) SETFLOAT((cl->argv+i), 0.f); return cl; } void iemnet__floatlist_destroy(t_iemnet_floatlist*cl) { if(NULL==cl)return; if(cl->argv) free(cl->argv); cl->argv=NULL; cl->argc=0; cl->size=0; free(cl); } t_iemnet_floatlist*iemnet__floatlist_create(unsigned int size) { t_iemnet_floatlist*result=(t_iemnet_floatlist*)malloc(sizeof(t_iemnet_floatlist)); if(NULL==result)return NULL; result->argv = (t_atom*)malloc(size*sizeof(t_atom)); if(NULL==result->argv) { iemnet__floatlist_destroy(result); return NULL; } result->argc=size; result->size=size; result=iemnet__floatlist_init(result); return result; } t_iemnet_floatlist*iemnet__floatlist_resize(t_iemnet_floatlist*cl, unsigned int size) { t_atom*tmp; if (NULL==cl) { return iemnet__floatlist_create(size); } if(size<=cl->size) { cl->argc=size; return cl; } tmp=(t_atom*)malloc(size*sizeof(t_atom)); if(NULL==tmp) return NULL; free(cl->argv); cl->argv=tmp; cl->argc=cl->size=size; cl=iemnet__floatlist_init(cl); return cl; } void iemnet__chunk_destroy(t_iemnet_chunk*c) { if(NULL==c)return; if(c->data)free(c->data); c->data=NULL; c->size=0; free(c); } t_iemnet_chunk* iemnet__chunk_create_empty(int size) { t_iemnet_chunk*result=(t_iemnet_chunk*)malloc(sizeof(t_iemnet_chunk)); if(result) { result->size=size; result->data=(unsigned char*)malloc(sizeof(unsigned char)*size); if(NULL == result->data) { result->size=0; iemnet__chunk_destroy(result); return NULL; } memset(result->data, 0, result->size); result->addr=0L; result->port=0; } return result; } t_iemnet_chunk* iemnet__chunk_create_data(int size, unsigned char*data) { t_iemnet_chunk*result=iemnet__chunk_create_empty(size); if(result) { memcpy(result->data, data, result->size); } return result; } t_iemnet_chunk* iemnet__chunk_create_dataaddr(int size, unsigned char*data, struct sockaddr_in*addr) { t_iemnet_chunk*result=iemnet__chunk_create_data(size, data); if(addr) { result->addr = ntohl(addr->sin_addr.s_addr); result->port = ntohs(addr->sin_port); } return result; } t_iemnet_chunk* iemnet__chunk_create_list(int argc, t_atom*argv) { int i; t_iemnet_chunk*result=iemnet__chunk_create_empty(argc); if(NULL==result)return NULL; for(i=0; idata[i]=c; argv++; } return result; } t_iemnet_chunk*iemnet__chunk_create_chunk(t_iemnet_chunk*c) { t_iemnet_chunk*result=NULL; if(NULL==c)return NULL; result=iemnet__chunk_create_data(c->size, c->data); result->addr=c->addr; result->port=c->port; return result; } t_iemnet_floatlist*iemnet__chunk2list(t_iemnet_chunk*c, t_iemnet_floatlist*dest) { unsigned int i; if(NULL==c)return NULL; dest=iemnet__floatlist_resize(dest, c->size); if(NULL==dest)return NULL; for(i=0; isize; i++) { dest->argv[i].a_w.w_float = c->data[i]; } return dest; } /* queue handling */ /* * using code found at http://newsgroups.derkeiler.com/Archive/Comp/comp.programming.threads/2008-02/msg00502.html */ #ifdef t_iemnet_queue # undef t_iemnet_queue #endif typedef struct _node { struct _node* next; t_iemnet_chunk*data; } t_node; struct _iemnet_queue { t_node* head; /* = 0 */ t_node* tail; /* = 0 */ pthread_mutex_t mtx; pthread_cond_t cond; int done; // in cleanup state int size; }; /* push a chunk into the queue * this will return the current queue size */ int queue_push( t_iemnet_queue* const _this, t_iemnet_chunk* const data ) { t_node* tail; t_node* n=NULL; int size=-1; if(NULL == _this)return size; pthread_mutex_lock(&_this->mtx); size=_this->size; pthread_mutex_unlock(&_this->mtx); if(NULL == data) return size; n=(t_node*)malloc(sizeof(t_node)); n->next = 0; n->data = data; pthread_mutex_lock(&_this->mtx); if (! (tail = _this->tail)) { _this->head = n; } else { tail->next = n; } _this->tail = n; _this->size+=data->size; size=_this->size; // added new chunk, so tell waiting threads that they can pop the data pthread_cond_signal(&_this->cond); pthread_mutex_unlock(&_this->mtx); return size; } /* pop a chunk from the queue * if the queue is empty, this will block until * something has been pushed * OR the queue is "done" (in which case NULL is returned) */ t_iemnet_chunk* queue_pop_block( t_iemnet_queue* const _this ) { t_node* head=0; t_iemnet_chunk*data=0; if(NULL == _this)return NULL; pthread_mutex_lock(&_this->mtx); /* if the queue is empty, wait */ if(NULL == _this->head) { pthread_cond_wait(&_this->cond, &_this->mtx); /* somebody signaled us, that we should do some work * either the queue has been filled, or we are done... */ if(_this->done) { pthread_mutex_unlock(&_this->mtx); return NULL; } } /* save the head, below we gonna work on this */ head = _this->head; /* update _this */ if (! (_this->head = head->next)) { _this->tail = 0; } if(head && head->data) { _this->size-=head->data->size; } pthread_mutex_unlock(&_this->mtx); if(head) { data=head->data; free(head); head=NULL; } return data; } /* pop a chunk from the queue * if the queue is empty, this will immediately return NULL * (note that despite of the name this does block for synchronization) */ t_iemnet_chunk* queue_pop_noblock( t_iemnet_queue* const _this ) { t_node* head=0; t_iemnet_chunk*data=0; if(NULL == _this)return NULL; pthread_mutex_lock(&_this->mtx); if (! (head = _this->head)) { // empty head pthread_mutex_unlock(&_this->mtx); return NULL; } if (! (_this->head = head->next)) { _this->tail = 0; } if(head && head->data) { _this->size-=head->data->size; } pthread_mutex_unlock(&_this->mtx); if(head) { data=head->data; free(head); head=NULL; } return data; } t_iemnet_chunk* queue_pop(t_iemnet_queue* const _this) { return queue_pop_block(_this); } int queue_getsize(t_iemnet_queue* const _this) { int size=-1; if(_this) { pthread_mutex_lock(&_this->mtx); size=_this->size; pthread_mutex_unlock(&_this->mtx); } return size; } void queue_finish(t_iemnet_queue* q) { DEBUG("queue_finish: %x", q); if(NULL==q) return; pthread_mutex_lock(&q->mtx); q->done=1; DEBUG("queue signaling: %x", q); pthread_cond_signal(&q->cond); DEBUG("queue signaled: %x", q); pthread_mutex_unlock(&q->mtx); DEBUG("queue_finished: %x", q); } void queue_destroy(t_iemnet_queue* q) { t_iemnet_chunk*c=NULL; if(NULL==q) return; DEBUG("queue destroy %x", q); queue_finish(q); /* remove all the chunks from the queue */ while(NULL!=(c=queue_pop_noblock(q))) { iemnet__chunk_destroy(c); } q->head=NULL; q->tail=NULL; pthread_mutex_destroy(&q->mtx); pthread_cond_destroy(&q->cond); free(q); q=NULL; DEBUG("queue destroyed %x", q); } t_iemnet_queue* queue_create(void) { static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; t_iemnet_queue*q=(t_iemnet_queue*)malloc(sizeof(t_iemnet_queue)); DEBUG("queue create %x", q); if(NULL==q)return NULL; q->head = NULL; q->tail = NULL; memcpy(&q->cond, &cond, sizeof(pthread_cond_t)); memcpy(&q->mtx , &mtx, sizeof(pthread_mutex_t)); q->done = 0; q->size = 0; DEBUG("queue created %x", q); return q; } iemnet-0.1/AUTHORS0000644000175100001440000000011411454577721013460 0ustar zmoelnigusersRoman Häfeli Olaf Matthes Martin Peach Miller Puckette IOhannes m zmölnig iemnet-0.1/udpclient-help.pd0000644000175100001440000000557011453710214015646 0ustar zmoelnigusers#N canvas 0 34 988 533 12; #X msg 96 97 disconnect; #X floatatom 255 351 3 0 0 0 - - -; #X floatatom 284 351 3 0 0 0 - - -; #X floatatom 311 351 3 0 0 0 - - -; #X floatatom 336 351 3 0 0 0 - - -; #X text 219 351 from; #X obj 275 307 tgl 15 0 empty empty connected 18 7 0 8 -24198 -241291 -1 1 1; #X text 187 21 connect with an IP address and port number; #X text 44 257 See also:; #X msg 21 22 connect 127.0.0.1 9997; #X text 364 156 semicolon-terminated string for netserver or netreceive ; #X text 302 182 'send' prefix is optional; #X obj 235 412 spigot; #X obj 274 389 tgl 15 0 empty empty enable_print 18 7 0 8 -24198 -241291 -1 1 1; #X floatatom 772 190 9 0 0 0 - - -; #X text 40 399 2010/03/01 Martin Peach; #X text 418 440 Attempting to print long messages can hang Pd!; #X obj 772 166 route sent; #X text 844 190 sent bytes; #X floatatom 772 347 3 0 0 0 - - -; #X floatatom 826 347 3 0 0 0 - - -; #X floatatom 799 347 3 0 0 0 - - -; #X obj 772 268 route address; #X obj 772 296 unpack f f f f f; #X floatatom 853 347 3 0 0 0 - - -; #X floatatom 881 321 6 0 0 0 - - -; #X text 879 347 ip; #X text 927 320 port; #X obj 520 344 spigot; #X obj 559 321 tgl 15 0 empty empty enable_print 18 7 0 8 -24198 -241291 -1 0 1; #X text 286 411 Received messages are output as a stream of bytes; #X text 45 477 NOTE: outlet's #2 & #3 are for compatibility with mrpeach's net objects. all information is available via outlet#4 as well!; #X obj 45 279 udpreceive; #X obj 45 302 udpserver; #X text 40 414 2010/10/08 Roman Haefeli; #X obj 235 256 udpclient; #X obj 235 441 print udpclient:received; #X obj 520 373 print udpclient:status; #X obj 255 328 unpack f f f f f; #X floatatom 364 351 6 0 0 0 - - -; #X msg 128 129 send 47 116 101 115 116 32 104 101 108 108 111 10; #X msg 156 157 send 51 49 32 97 98 99 59 10; #X text 482 128 /test hello (OSC message); #X text 308 255 udpclient can be used for a bi-directional connection ; #X msg 183 184 97 98 99 100 10; #X msg 47 48 connect swisstime.ethz.ch 13; #X text 527 14 udpclient can connect to a server and send and receive messages as lists of bytes. Any integer value between 0 and 255 can be transmitted or received.; #X obj 295 281 s \$0.udpclient.o4; #X obj 520 298 r \$0.udpclient.o4; #X obj 772 142 r \$0.udpclient.o4; #X obj 772 242 r \$0.udpclient.o4; #X connect 0 0 35 0; #X connect 9 0 35 0; #X connect 12 0 36 0; #X connect 13 0 12 1; #X connect 17 0 14 0; #X connect 22 0 23 0; #X connect 23 0 19 0; #X connect 23 1 21 0; #X connect 23 2 20 0; #X connect 23 3 24 0; #X connect 23 4 25 0; #X connect 28 0 37 0; #X connect 29 0 28 1; #X connect 35 0 12 0; #X connect 35 1 38 0; #X connect 35 2 6 0; #X connect 35 3 47 0; #X connect 38 0 1 0; #X connect 38 1 2 0; #X connect 38 2 3 0; #X connect 38 3 4 0; #X connect 38 4 39 0; #X connect 40 0 35 0; #X connect 41 0 35 0; #X connect 44 0 35 0; #X connect 45 0 35 0; #X connect 48 0 28 0; #X connect 49 0 17 0; #X connect 50 0 22 0; iemnet-0.1/tcpsend.c0000644000175100001440000001127711445657050014222 0ustar zmoelnigusers/* tcpsend.c * copyright (c) 2010 IOhannes m zmölnig, IEM * copyright (c) 2006-2010 Martin Peach * copyright (c) Miller Puckette */ /* */ /* A client for unidirectional communication from within Pd. */ /* */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version 2 */ /* of the License, or (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* */ #define DEBUGLEVEL 1 static const char objName[] = "tcpsend"; #include "iemnet.h" #include #ifndef _WIN32 # include #endif static t_class *tcpsend_class; typedef struct _tcpsend { t_object x_obj; int x_fd; t_iemnet_sender*x_sender; } t_tcpsend; static void tcpsend_disconnect(t_tcpsend *x) { if (x->x_fd >= 0) { if(x->x_sender)iemnet__sender_destroy(x->x_sender); x->x_sender=NULL; sys_closesocket(x->x_fd); x->x_fd = -1; outlet_float(x->x_obj.ob_outlet, 0); post("tcpsend: disconnected"); } } static void tcpsend_connect(t_tcpsend *x, t_symbol *hostname, t_floatarg fportno) { struct sockaddr_in server; struct hostent *hp; int sockfd; int portno = fportno; int intarg; if (x->x_fd >= 0) { error("tcpsend: already connected"); return; } /* create a socket */ sockfd = socket(AF_INET, SOCK_STREAM, 0); DEBUG("send socket %d\n", sockfd); if (sockfd < 0) { sys_sockerror("tcpsend: socket"); return; } /* connect socket using hostname provided in command line */ server.sin_family = AF_INET; hp = gethostbyname(hostname->s_name); if (hp == 0) { post("tcpsend: bad host?\n"); return; } /* for stream (TCP) sockets, specify "nodelay" */ intarg = 1; if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&intarg, sizeof(intarg)) < 0) post("tcpsend: setsockopt (TCP_NODELAY) failed\n"); memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); /* assign client port number */ server.sin_port = htons((u_short)portno); post("tcpsend: connecting to port %d", portno); /* try to connect. */ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) { sys_sockerror("tcpsend: connecting stream socket"); sys_closesocket(sockfd); return; } x->x_fd = sockfd; x->x_sender=iemnet__sender_create(sockfd); outlet_float(x->x_obj.ob_outlet, 1); } static void tcpsend_send(t_tcpsend *x, t_symbol *s, int argc, t_atom *argv) { int size=0; t_iemnet_sender*sender=x->x_sender; t_iemnet_chunk*chunk=iemnet__chunk_create_list(argc, argv); if(sender && chunk) { size=iemnet__sender_send(sender, chunk); } iemnet__chunk_destroy(chunk); } static void tcpsend_free(t_tcpsend *x) { tcpsend_disconnect(x); } static void *tcpsend_new(void) { t_tcpsend *x = (t_tcpsend *)pd_new(tcpsend_class); outlet_new(&x->x_obj, gensym("float")); x->x_fd = -1; return (x); } IEMNET_EXTERN void tcpsend_setup(void) { if(!iemnet__register(objName))return; tcpsend_class = class_new(gensym(objName), (t_newmethod)tcpsend_new, (t_method)tcpsend_free, sizeof(t_tcpsend), 0, 0); class_addmethod(tcpsend_class, (t_method)tcpsend_connect, gensym("connect"), A_SYMBOL, A_FLOAT, 0); class_addmethod(tcpsend_class, (t_method)tcpsend_disconnect, gensym("disconnect"), 0); class_addmethod(tcpsend_class, (t_method)tcpsend_send, gensym("send"), A_GIMME, 0); class_addlist(tcpsend_class, (t_method)tcpsend_send); DEBUGMETHOD(tcpsend_class); } IEMNET_INITIALIZER(tcpsend_setup); /* end tcpsend.c */ iemnet-0.1/Makefile0000644000175100001440000002037311456320053014043 0ustar zmoelnigusers# To use this Makefile for your project, first put the name of your library in # LIBRARY_NAME variable. The folder for your project should have the same name # as your library. LIBRARY_NAME = iemnet LIBRARY_VERSION = 0.1 # Next, add your .c source files to the SOURCES variable. The help files will # be included automatically SOURCES = tcpserver.c tcpclient.c tcpsend.c tcpreceive.c udpreceive.c udpsend.c udpclient.c udpserver.c #SOURCES = tcpclient.c tcpreceive.c tcpsend.c tcpserver.c udpreceive~.c udpreceive.c udpsend~.c udpsend.c # For objects that only build on certain platforms, add those to the SOURCES # line for the right platforms. SOURCES_android = SOURCES_cygwin = SOURCES_macosx = SOURCES_iphoneos = SOURCES_linux = SOURCES_windows = # .c source files that will be statically linked to _all_ objects HELPERSOURCES = iemnet.c iemnet_data.c iemnet_receiver.c iemnet_sender.c # list all pd objects (i.e. myobject.pd) files here, and their helpfiles will # be included automatically PDOBJECTS = # if you want to include any other files in the source and binary tarballs, # list them here. This can be anything from header files, READMEs, example # patches, documentation, etc. EXTRA_DIST = # to enable debugging set this to "-DDEBUG" # you can slo just run make as "make DEBUG_CFLAGS='-DDEBUG'" DEBUG_CFLAGS = #------------------------------------------------------------------------------# # # you shouldn't need to edit anything below here, if we did it right :) # #------------------------------------------------------------------------------# # where Pd lives PD_PATH = ../../../pd # where to install the library prefix = /usr/local libdir = $(prefix)/lib pkglibdir = $(libdir)/pd-externals objectsdir = $(pkglibdir) INSTALL = install INSTALL_FILE = $(INSTALL) -p -m 644 INSTALL_LIB = $(INSTALL) -p -m 644 -s INSTALL_DIR = $(INSTALL) -p -m 755 -d CFLAGS = -DPD -DLIBRARY_VERSION=\"$(LIBRARY_VERSION)\" -I$(PD_PATH)/src -Wall -W -Wno-unused -g LDFLAGS = LIBS = ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) \ $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows) UNAME := $(shell uname -s) ifeq ($(UNAME),Darwin) CPU := $(shell uname -p) ifeq ($(CPU),arm) # iPhone/iPod Touch SOURCES += $(SOURCES_macosx) EXTENSION = pd_darwin OS = iphoneos IPHONE_BASE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin CC=$(IPHONE_BASE)/gcc CPP=$(IPHONE_BASE)/cpp CXX=$(IPHONE_BASE)/g++ ISYSROOT = -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk IPHONE_CFLAGS = -miphoneos-version-min=3.0 $(ISYSROOT) -arch armv6 OPT_CFLAGS = -fast -funroll-loops -fomit-frame-pointer CFLAGS := $(IPHONE_CFLAGS) $(OPT_CFLAGS) $(CFLAGS) \ -I/Applications/Pd-extended.app/Contents/Resources/include LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT) LIBS += -lc STRIP = strip -x DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) DISTBINDIR=$(DISTDIR)-$(OS) else # Mac OS X SOURCES += $(SOURCES_macosx) EXTENSION = pd_darwin OS = macosx OPT_CFLAGS = -ftree-vectorize -ftree-vectorizer-verbose=0 -fast FAT_FLAGS = -arch i386 -arch ppc -mmacosx-version-min=10.4 CFLAGS += $(FAT_FLAGS) -fPIC -I/sw/include \ -I/Applications/Pd-extended.app/Contents/Resources/include LDFLAGS += $(FAT_FLAGS) -bundle -undefined dynamic_lookup -L/sw/lib LIBS += -lc STRIP = strip -x DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) DISTBINDIR=$(DISTDIR)-$(OS) endif endif ifeq ($(UNAME),Linux) SOURCES += $(SOURCES_linux) EXTENSION = pd_linux OS = linux OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer CFLAGS += -fPIC LDFLAGS += -Wl,--export-dynamic -shared -fPIC LIBS += -lc STRIP = strip --strip-unneeded -R .note -R .comment DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m) endif ifeq (CYGWIN,$(findstring CYGWIN,$(UNAME))) SOURCES += $(SOURCES_cygwin) EXTENSION = dll OS = cygwin OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer CFLAGS += LDFLAGS += -Wl,--export-dynamic -shared -L$(PD_PATH)/src LIBS += -lc -lpd STRIP = strip --strip-unneeded -R .note -R .comment DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) DISTBINDIR=$(DISTDIR)-$(OS) endif ifeq (MINGW,$(findstring MINGW,$(UNAME))) SOURCES += $(SOURCES_windows) EXTENSION = dll OS = windows OPT_CFLAGS = -O3 -funroll-loops -fomit-frame-pointer WINDOWS_HACKS = -D'O_NONBLOCK=1' CFLAGS += -mms-bitfields $(WINDOWS_HACKS) LDFLAGS += -s -shared -Wl,--enable-auto-import LIBS += -L$(PD_PATH)/src -L$(PD_PATH)/bin -L$(PD_PATH)/obj -lpd -lwsock32 -lkernel32 -luser32 -lgdi32 STRIP = strip --strip-unneeded -R .note -R .comment DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) DISTBINDIR=$(DISTDIR)-$(OS) endif CFLAGS += $(OPT_CFLAGS) CFLAGS += $(DEBUG_CFLAGS) .PHONY = install libdir_install single_install install-doc install-exec clean dist etags all: $(SOURCES:.c=.$(EXTENSION)) %.o: %.c $(CC) $(CFLAGS) -o "$@" -c "$<" %.$(EXTENSION): %.o $(HELPERSOURCES:.c=.o) $(CC) $(LDFLAGS) -o "$@" $^ $(LIBS) chmod a-x "$*.$(EXTENSION)" # this links everything into a single binary file $(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o $(HELPERSOURCES:.c=.o) $(CC) $(LDFLAGS) -o $@.$(EXTENSION) $^ $(LIBS) chmod a-x $@.$(EXTENSION) install: libdir_install # The meta and help files are explicitly installed to make sure they are # actually there. Those files are not optional, then need to be there. libdir_install: $(SOURCES:.c=.$(EXTENSION)) install-doc $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd \ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) test -z "$(SOURCES)" || \ $(INSTALL_LIB) $(SOURCES:.c=.$(EXTENSION)) \ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) test -z "$(PDOBJECTS)" || \ $(INSTALL_LIB) $(OBJECTS) \ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) # install library linked as single binary single_install: $(LIBRARY_NAME) install-doc install-exec $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) $(INSTALL_FILE) $(LIBRARY_NAME).$(EXTENSION) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) $(STRIP) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/$(LIBRARY_NAME).$(EXTENSION) install-doc: $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) test -z "$(SOURCES)" || \ $(INSTALL_FILE) $(SOURCES:.c=-help.pd) \ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) test -z "$(PDOBJECTS)" || \ $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) \ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) $(INSTALL_FILE) README.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/README.txt $(INSTALL_FILE) VERSION.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/VERSION.txt $(INSTALL_FILE) CHANGES.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/CHANGES.txt clean: -rm -f -- $(SOURCES:.c=.o) $(HELPERSOURCES:.c=.o) -rm -f -- $(SOURCES:.c=.$(EXTENSION)) -rm -f -- $(LIBRARY_NAME).$(EXTENSION) distclean: clean -rm -f *~ *.o *.$(EXTENSION) -rm -f -- $(DISTBINDIR).tar.gz -rm -rf -- $(DISTBINDIR) -rm -f -- $(DISTDIR).tar.gz -rm -rf -- $(DISTDIR) $(DISTBINDIR): $(INSTALL_DIR) $(DISTBINDIR) libdir: all $(DISTBINDIR) $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTBINDIR) $(INSTALL_FILE) $(SOURCES) $(DISTBINDIR) $(INSTALL_FILE) $(SOURCES:.c=-help.pd) $(DISTBINDIR) test -z "$(EXTRA_DIST)" || \ $(INSTALL_FILE) $(EXTRA_DIST) $(DISTBINDIR) # tar --exclude-vcs -czpf $(DISTBINDIR).tar.gz $(DISTBINDIR) $(DISTDIR): $(INSTALL_DIR) $(DISTDIR) dist: $(DISTDIR) $(INSTALL_FILE) Makefile $(DISTDIR) $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTDIR) test -z "$(ALLSOURCES)" || \ $(INSTALL_FILE) $(ALLSOURCES) $(DISTDIR) test -z "$(ALLSOURCES)" || \ $(INSTALL_FILE) $(ALLSOURCES:.c=-help.pd) $(DISTDIR) test -z "$(PDOBJECTS)" || \ $(INSTALL_FILE) $(PDOBJECTS) $(DISTDIR) test -z "$(PDOBJECTS)" || \ $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) $(DISTDIR) test -z "$(EXTRA_DIST)" || \ $(INSTALL_FILE) $(EXTRA_DIST) $(DISTDIR) tar --exclude-vcs -czpf $(DISTDIR).tar.gz $(DISTDIR) etags: etags *.h $(SOURCES) ../../pd/src/*.[ch] /usr/include/*.h /usr/include/*/*.h showpaths: @echo "PD_PATH: $(PD_PATH)" @echo "objectsdir: $(objectsdir)" @echo "LIBRARY_NAME: $(LIBRARY_NAME)" @echo "SOURCES: $(SOURCES)" @echo "ALLSOURCES: $(ALLSOURCES)" @echo "UNAME: $(UNAME)" @echo "CPU: $(CPU)" iemnet-0.1/NOTES.txt0000644000175100001440000000171511354122545014036 0ustar zmoelnigusersscratchpad for the development of iemnet ======================================== speed & syslocks ================ one bottleneck right now is the synchronisation with Pd's main thread to register a clock callback. doing a sys_lock() whenever a package arrives will slow down things immensely. alternatives: no sys_lock(): will eventually crash Pd (no option) use sys_trylock(): this might eventually fail to notify Pd of newly arrived packets (bad for throughput) external polling: no syslock needed at all, but more complicated keep track of clock_callback: "notified" flag tells us, whether we have already sent a notification which has not yet been handled...no need to notify again #4 looks most promising tests for tcpclient/server: client disconnects -> server should get notified server disconnects -> client should get notified client crashes -> server should disconnect server crashes -> client should disconnect iemnet-0.1/FEATURES.txt0000644000175100001440000000354711354606435014376 0ustar zmoelnigusersiemnet - networking for Pd ========================== A general: iemnet objects provide a low-level interface (OSI-5 transport layer) to networking from within Pd. iemnet tries to do only one thing (transmitting data over the internet), but it tries to do it good. A1 data data passed over the network has to be given as "list of bytes". in Pd-speak these are "list"s, containing only floating point numbers whoses values must be integer and in the range 0..255. you have to take care of that yourself. if you don't (e.g. trying to send symbols, fractional numbers, out of range numbers), you are to blame. in order to send more complex data, you have to wrap them into an application layer protocol, such as Open Sound Control (OSC). you can find objects to convert OSC messages to/from messages understood by iemnet objects in Martin Peaches great "osc" library. A2 threading iemnet makes heavy use of threading. this means that sending data (to the internet), receiving data (from the internet) and processing datat (within Pd) can run in parallel. this means that you won't get audio dropouts if the network is slow because your your neighbour is downloading videos. if you have a multi-core (SMP) system, threads can utitlize this. if you don't have a multi-core (SMP) system, you still benefit from the threading approach. B TCP/IP objects B1 [tcpserver] listens on a port for incoming messages if the port is already occupied, you are provided feedback so you can react the port can be changed at runtime you can let the system chose an available port for you (and you can query it) passing only a list (without the "send ", "client " or "broadcast" prefix) specifies only the payload (data). the target can be set with the "target" message ("target 0" means "broadcast", "target " will send to "client " if id>0 and to all but "client " if id<0 iemnet-0.1/tcpserver.c0000644000175100001440000004605311445657050014577 0ustar zmoelnigusers/* tcpserver.c * copyright (c) 2010 IOhannes m zmölnig, IEM * copyright (c) 2006-2010 Martin Peach * copyright (c) 2004 Olaf Matthes */ /* */ /* A server for bidirectional communication from within Pd. */ /* Allows to send back data to specific clients connected to the server. */ /* */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version 2 */ /* of the License, or (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* */ /* ---------------------------------------------------------------------------- */ #define DEBUGLEVEL 1 #include "iemnet.h" #include "iemnet_data.h" #include #define MAX_CONNECT 32 /* maximum number of connections */ /* ----------------------------- tcpserver ------------------------- */ static t_class *tcpserver_class; static const char objName[] = "tcpserver"; typedef struct _tcpserver_socketreceiver { struct _tcpserver *sr_owner; long sr_host; unsigned short sr_port; t_int sr_fd; t_iemnet_sender*sr_sender; t_iemnet_receiver*sr_receiver; } t_tcpserver_socketreceiver; typedef struct _tcpserver { t_object x_obj; t_outlet *x_msgout; t_outlet *x_connectout; t_outlet *x_sockout; // legacy t_outlet *x_addrout; // legacy t_outlet *x_statout; int x_serialize; t_tcpserver_socketreceiver *x_sr[MAX_CONNECT]; /* socket per connection */ t_int x_nconnections; t_int x_connectsocket; /* socket waiting for new connections */ t_int x_port; int x_defaulttarget; /* the default connection to send to; 0=broadcast; >0 use this client; <0 exclude this client */ t_iemnet_floatlist *x_floatlist; } t_tcpserver; static void tcpserver_receive_callback(void*x, t_iemnet_chunk*); static t_tcpserver_socketreceiver *tcpserver_socketreceiver_new(t_tcpserver *owner, int sockfd, struct sockaddr_in*addr) { t_tcpserver_socketreceiver *x = (t_tcpserver_socketreceiver *)getbytes(sizeof(*x)); if(NULL==x) { error("%s_socketreceiver: unable to allocate %d bytes", objName, sizeof(*x)); return NULL; } else { x->sr_owner=owner; x->sr_fd=sockfd; x->sr_host=ntohl(addr->sin_addr.s_addr); x->sr_port=ntohs(addr->sin_port); x->sr_sender=iemnet__sender_create(sockfd); x->sr_receiver=iemnet__receiver_create(sockfd, x, tcpserver_receive_callback); } return (x); } static void tcpserver_socketreceiver_free(t_tcpserver_socketreceiver *x) { DEBUG("freeing %x", x); if (x != NULL) { int sockfd=x->sr_fd; t_iemnet_sender*sender=x->sr_sender; t_iemnet_receiver*receiver=x->sr_receiver; x->sr_owner=NULL; x->sr_sender=NULL; x->sr_receiver=NULL; x->sr_fd=-1; if(sender) iemnet__sender_destroy(sender); if(receiver)iemnet__receiver_destroy(receiver); sys_closesocket(sockfd); freebytes(x, sizeof(*x)); } DEBUG("freeed %x", x); } static int tcpserver_socket2index(t_tcpserver*x, int sockfd) { int i=0; for(i = 0; i < x->x_nconnections; i++) /* check if connection exists */ { if(x->x_sr[i]->sr_fd == sockfd) { return i; } } return -1; } /* checks whether client is a valid (1-based) index * if the id is invalid, returns -1 * if the id is valid, return the 0-based index (client-1) */ static int tcpserver_fixindex(t_tcpserver*x, int client) { if(x->x_nconnections <= 0) { pd_error(x, "[%s]: no clients connected", objName); return -1; } if (!((client > 0) && (client <= x->x_nconnections))) { pd_error(x, "[%s] client %d out of range [1..%d]", objName, client, x->x_nconnections); return -1; } return (client-1); } /* ---------------- tcpserver info ---------------------------- */ static void tcpserver_info_client(t_tcpserver *x, int client) { // "client " // "bufsize " static t_atom output_atom[4]; if(x&&x->x_sr&&x->x_sr[client]) { int sockfd = x->x_sr[client]->sr_fd; unsigned short port = x->x_sr[client]->sr_port; long address = x->x_sr[client]->sr_host; char hostname[MAXPDSTRING]; int insize =iemnet__receiver_getsize(x->x_sr[client]->sr_receiver); int outsize=iemnet__sender_getsize (x->x_sr[client]->sr_sender ); snprintf(hostname, MAXPDSTRING-1, "%d.%d.%d.%d", (unsigned char)((address & 0xFF000000)>>24), (unsigned char)((address & 0x0FF0000)>>16), (unsigned char)((address & 0x0FF00)>>8), (unsigned char)((address & 0x0FF))); hostname[MAXPDSTRING-1]=0; SETFLOAT (output_atom+0, client+1); SETFLOAT (output_atom+1, sockfd); SETSYMBOL(output_atom+2, gensym(hostname)); SETFLOAT (output_atom+3, port); outlet_anything( x->x_statout, gensym("client"), 4, output_atom); SETFLOAT (output_atom+0, client+1); SETFLOAT (output_atom+1, insize); SETFLOAT (output_atom+2, outsize); outlet_anything( x->x_statout, gensym("bufsize"), 3, output_atom); } } static void tcpserver_info(t_tcpserver *x) { static t_atom output_atom[4]; int sockfd=x->x_connectsocket; int port=x->x_port; if(sockfd<0) { // no open port post("no valid sock"); } if(x->x_port<=0) { struct sockaddr_in server; socklen_t serversize=sizeof(server); if(!getsockname(sockfd, (struct sockaddr *)&server, &serversize)) { x->x_port=ntohs(server.sin_port); port=x->x_port; } else { post("gesockname failed for %d", sockfd); } } SETFLOAT (output_atom+0, port); outlet_anything( x->x_statout, gensym("port"), 1, output_atom); } static void tcpserver_info_connection(t_tcpserver *x, t_tcpserver_socketreceiver*y) { iemnet__addrout(x->x_statout, x->x_addrout, y->sr_host, y->sr_port); outlet_float(x->x_sockout, y->sr_fd); } /* ---------------- main tcpserver (send) stuff --------------------- */ static void tcpserver_disconnect_socket(t_tcpserver *x, t_floatarg fsocket); static void tcpserver_send_bytes(t_tcpserver*x, int client, t_iemnet_chunk*chunk) { DEBUG("send_bytes to %x -> %x[%d]", x, x->x_sr, client); if(x->x_sr)DEBUG("client %X", x->x_sr[client]); if(x && x->x_sr && x->x_sr[client]) { t_atom output_atom[3]; int size=-1; t_iemnet_sender*sender=sender=x->x_sr[client]->sr_sender; int sockfd = x->x_sr[client]->sr_fd; if(sender) { size=iemnet__sender_send(sender, chunk); } SETFLOAT(&output_atom[0], client+1); SETFLOAT(&output_atom[1], size); SETFLOAT(&output_atom[2], sockfd); outlet_anything( x->x_statout, gensym("sent"), 3, output_atom); if(size<0) { // disconnected! tcpserver_disconnect_socket(x, sockfd); } } } /* broadcasts a message to all connected clients but the given one */ static void tcpserver_send_butclient(t_tcpserver *x, int but, int argc, t_atom *argv) { int client=0; t_iemnet_chunk*chunk=iemnet__chunk_create_list(argc, argv); /* enumerate through the clients and send each the message */ for(client = 0; client < x->x_nconnections; client++) /* check if connection exists */ { /* socket exists for this client */ if(client!=but)tcpserver_send_bytes(x, client, chunk); } iemnet__chunk_destroy(chunk); } /* sends a message to a given client */ static void tcpserver_send_toclient(t_tcpserver *x, int client, int argc, t_atom *argv) { t_iemnet_chunk*chunk=iemnet__chunk_create_list(argc, argv); tcpserver_send_bytes(x, client, chunk); iemnet__chunk_destroy(chunk); } /* send message to client using client number note that the client numbers might change in case a client disconnects! */ /* clients start at 1 but our index starts at 0 */ static void tcpserver_send_client(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv) { int client=0; if (argc > 0) { client=tcpserver_fixindex(x, atom_getint(argv)); if(client<0)return; if(argc==1) { tcpserver_info_client(x, client); } else { tcpserver_send_toclient(x, client, argc-1, argv+1); } return; } else { for(client=0; clientx_nconnections; client++) tcpserver_info_client(x, client); } } /* broadcasts a message to all connected clients */ static void tcpserver_broadcast(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv) { int client; t_iemnet_chunk*chunk=iemnet__chunk_create_list(argc, argv); /* enumerate through the clients and send each the message */ for(client = 0; client < x->x_nconnections; client++) /* check if connection exists */ { /* socket exists for this client */ tcpserver_send_bytes(x, client, chunk); } iemnet__chunk_destroy(chunk); } /* broadcasts a message to all connected clients */ static void tcpserver_broadcastbut(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv) { int client=0; int but=-1; t_iemnet_chunk*chunk=NULL; if(argc<2) { return; } if((but=tcpserver_fixindex(x, atom_getint(argv)))<0)return; tcpserver_send_butclient(x, but, argc-1, argv+1); } static void tcpserver_defaultsend(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv) { int client=-1; int sockfd=x->x_defaulttarget; if(0==sockfd) tcpserver_broadcast(x, s, argc, argv); else if(sockfd>0) { client=tcpserver_socket2index(x, sockfd); tcpserver_send_toclient(x, client, argc, argv); } else if(sockfd<0) { client=tcpserver_socket2index(x, -sockfd); tcpserver_send_butclient(x, client, argc, argv); } } static void tcpserver_defaulttarget(t_tcpserver *x, t_floatarg f) { int sockfd=0; int rawclient=f; int client=(rawclient<0)?(-rawclient):rawclient; if(client > x->x_nconnections) { error("[%s] target %d out of range [0..%d]", objName, client, x->x_nconnections); return; } // map the client to a persistant socket if(client>0) { sockfd=x->x_sr[client-1]->sr_fd; } if(rawclient<0)sockfd=-sockfd; x->x_defaulttarget=sockfd; } static void tcpserver_targetsocket(t_tcpserver *x, t_floatarg f) { int sockfd=f; x->x_defaulttarget=sockfd; } /* send message to client using socket number */ static void tcpserver_send_socket(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv) { int client = -1; t_iemnet_chunk*chunk=NULL; if(argc) { client = tcpserver_socket2index(x, atom_getint(argv)); if(client<0)return; } else { pd_error(x, "%s_send: no socket specified", objName); return; } /* get socket number of connection (first element in list) */ if(argc && argv->a_type == A_FLOAT) { int sockfd=atom_getint(argv); client = tcpserver_socket2index(x, sockfd); if(client < 0) { post("%s_send: no connection on socket %d", objName, sockfd); return; } } else { post("%s_send: no socket specified", objName); return; } chunk=iemnet__chunk_create_list(argc-1, argv+1); tcpserver_send_bytes(x, client, chunk); iemnet__chunk_destroy(chunk); } static void tcpserver_disconnect(t_tcpserver *x, int client) { int k; DEBUG("disconnect %x %d", x, client); tcpserver_info_connection(x, x->x_sr[client]); tcpserver_socketreceiver_free(x->x_sr[client]); x->x_sr[client]=NULL; /* rearrange list now: move entries to close the gap */ for(k = client; k < x->x_nconnections; k++) { x->x_sr[k] = x->x_sr[k + 1]; } x->x_sr[k + 1]=NULL; x->x_nconnections--; outlet_float(x->x_connectout, x->x_nconnections); } /* disconnect a client by number */ static void tcpserver_disconnect_client(t_tcpserver *x, t_floatarg fclient) { int client = tcpserver_fixindex(x, fclient); if(client<0)return; tcpserver_disconnect(x, client); } /* disconnect a client by socket */ static void tcpserver_disconnect_socket(t_tcpserver *x, t_floatarg fsocket) { int id=tcpserver_socket2index(x, (int)fsocket); if(id>=0) tcpserver_disconnect_client(x, id+1); } /* disconnect a client by socket */ static void tcpserver_disconnect_all(t_tcpserver *x) { int id=x->x_nconnections; while(--id>=0) { tcpserver_disconnect(x, id); } } /* ---------------- main tcpserver (receive) stuff --------------------- */ static void tcpserver_receive_callback(void *y0, t_iemnet_chunk*c) { t_tcpserver_socketreceiver *y=(t_tcpserver_socketreceiver*)y0; t_tcpserver*x=NULL; if(NULL==y || NULL==(x=y->sr_owner))return; if(c) { tcpserver_info_connection(x, y); x->x_floatlist=iemnet__chunk2list(c, x->x_floatlist); // get's destroyed in the dtor iemnet__streamout(x->x_msgout, x->x_floatlist->argc, x->x_floatlist->argv, x->x_serialize); } else { // disconnected int sockfd=y->sr_fd; tcpserver_disconnect_socket(x, sockfd); } // post("tcpserver: %d bytes in %d packets", bytecount, packetcount); } static void tcpserver_connectpoll(t_tcpserver *x) { struct sockaddr_in incomer_address; unsigned int sockaddrl = sizeof( struct sockaddr ); int fd = accept(x->x_connectsocket, (struct sockaddr*)&incomer_address, &sockaddrl); int i; if (fd < 0) post("%s: accept failed", objName); else { t_tcpserver_socketreceiver *y = tcpserver_socketreceiver_new((void *)x, fd, &incomer_address); if (!y) { sys_closesocket(fd); return; } x->x_nconnections++; i = x->x_nconnections - 1; x->x_sr[i] = y; tcpserver_info_connection(x, y); } outlet_float(x->x_connectout, x->x_nconnections); } static void tcpserver_port(t_tcpserver*x, t_floatarg fportno) { static t_atom ap[1]; int portno = fportno; struct sockaddr_in server; socklen_t serversize=sizeof(server); int sockfd = x->x_connectsocket; SETFLOAT(ap, -1); if(x->x_port == portno) { return; } /* cleanup any open ports */ if(sockfd>=0) { sys_rmpollfn(sockfd); sys_closesocket(sockfd); x->x_connectsocket=-1; x->x_port=-1; } sockfd = socket(AF_INET, SOCK_STREAM, 0); server.sin_family = AF_INET; /* LATER allow setting of inaddr */ server.sin_addr.s_addr = INADDR_ANY; /* assign server port number */ server.sin_port = htons((u_short)portno); /* name the socket */ if (bind(sockfd, (struct sockaddr *)&server, serversize) < 0) { sys_sockerror("tcpserver: bind"); sys_closesocket(sockfd); outlet_anything(x->x_statout, gensym("port"), 1, ap); return; } /* streaming protocol */ if (listen(sockfd, 5) < 0) { sys_sockerror("tcpserver: listen"); sys_closesocket(sockfd); sockfd = -1; outlet_anything(x->x_statout, gensym("port"), 1, ap); return; } else { sys_addpollfn(sockfd, (t_fdpollfn)tcpserver_connectpoll, x); // wait for new connections } x->x_connectsocket = sockfd; x->x_port = portno; // find out which port is actually used (useful when assigning "0") if(!getsockname(sockfd, (struct sockaddr *)&server, &serversize)) { x->x_port=ntohs(server.sin_port); } SETFLOAT(ap, x->x_port); outlet_anything(x->x_statout, gensym("port"), 1, ap); } static void tcpserver_serialize(t_tcpserver *x, t_floatarg doit) { x->x_serialize=doit; } static void *tcpserver_new(t_floatarg fportno) { t_tcpserver *x; int i; x = (t_tcpserver *)pd_new(tcpserver_class); x->x_msgout = outlet_new(&x->x_obj, 0); /* 1st outlet for received data */ x->x_connectout = outlet_new(&x->x_obj, gensym("float")); /* 2nd outlet for number of connected clients */ x->x_sockout = outlet_new(&x->x_obj, gensym("float")); x->x_addrout = outlet_new(&x->x_obj, gensym("list" )); x->x_statout = outlet_new(&x->x_obj, 0);/* 5th outlet for everything else */ x->x_serialize=1; x->x_connectsocket = -1; x->x_port = -1; x->x_nconnections = 0; for(i = 0; i < MAX_CONNECT; i++) { x->x_sr[i] = NULL; } x->x_defaulttarget=0; x->x_floatlist=iemnet__floatlist_create(1024); tcpserver_port(x, fportno); return (x); } static void tcpserver_free(t_tcpserver *x) { int i; for(i = 0; i < MAX_CONNECT; i++) { if (NULL!=x->x_sr[i]) { DEBUG("[%s] free %x", objName, x); tcpserver_socketreceiver_free(x->x_sr[i]); x->x_sr[i]=NULL; } } if (x->x_connectsocket >= 0) { sys_rmpollfn(x->x_connectsocket); sys_closesocket(x->x_connectsocket); } if(x->x_floatlist)iemnet__floatlist_destroy(x->x_floatlist);x->x_floatlist=NULL; } IEMNET_EXTERN void tcpserver_setup(void) { if(!iemnet__register(objName))return; tcpserver_class = class_new(gensym(objName),(t_newmethod)tcpserver_new, (t_method)tcpserver_free, sizeof(t_tcpserver), 0, A_DEFFLOAT, 0); class_addmethod(tcpserver_class, (t_method)tcpserver_disconnect_client, gensym("disconnectclient"), A_DEFFLOAT, 0); class_addmethod(tcpserver_class, (t_method)tcpserver_disconnect_socket, gensym("disconnectsocket"), A_DEFFLOAT, 0); class_addmethod(tcpserver_class, (t_method)tcpserver_disconnect_all, gensym("disconnect"), 0); class_addmethod(tcpserver_class, (t_method)tcpserver_send_socket, gensym("send"), A_GIMME, 0); class_addmethod(tcpserver_class, (t_method)tcpserver_send_client, gensym("client"), A_GIMME, 0); class_addmethod(tcpserver_class, (t_method)tcpserver_broadcast, gensym("broadcast"), A_GIMME, 0); class_addmethod(tcpserver_class, (t_method)tcpserver_defaulttarget, gensym("target"), A_DEFFLOAT, 0); class_addmethod(tcpserver_class, (t_method)tcpserver_targetsocket, gensym("targetsocket"), A_DEFFLOAT, 0); class_addlist (tcpserver_class, (t_method)tcpserver_defaultsend); class_addmethod(tcpserver_class, (t_method)tcpserver_serialize, gensym("serialize"), A_FLOAT, 0); class_addmethod(tcpserver_class, (t_method)tcpserver_port, gensym("port"), A_DEFFLOAT, 0); class_addbang (tcpserver_class, (t_method)tcpserver_info); DEBUGMETHOD(tcpserver_class); } IEMNET_INITIALIZER(tcpserver_setup); /* end of tcpserver.c */ iemnet-0.1/udpreceive-help.pd0000644000175100001440000000307311453711672016017 0ustar zmoelnigusers#N canvas 60 148 478 280 12; #X floatatom 158 142 3 0 0 0 - - -; #X floatatom 185 142 3 0 0 0 - - -; #X floatatom 212 142 3 0 0 0 - - -; #X floatatom 239 142 3 0 0 0 - - -; #X text 115 141 from; #X obj 107 186 print message; #X obj 107 91 udpreceive 9997; #X text 32 16 udpreceive receives bytes over a udp connection.; #X floatatom 267 142 5 0 0 0 - - -; #X obj 158 116 unpack 0 0 0 0 0; #X msg 107 50 port 8888; #X text 182 48 change the port; #X text 34 229 copyright (c) 2008 Martin Peach; #X text 34 249 copyright (c) 2010 IOhannes m zmoelnig (for iemnet) ; #N canvas 3 45 450 300 get.info 0; #X text 29 31 NOTE outlet #2 is for compatibility with mrpeach's net objects. you can get all info (and more) via outlet #3 as well.; #X obj 40 95 inlet outlet#3; #X obj 40 119 route address; #X floatatom 190 177 3 0 0 0 - - -; #X floatatom 217 177 3 0 0 0 - - -; #X floatatom 244 177 3 0 0 0 - - -; #X floatatom 271 177 3 0 0 0 - - -; #X text 147 176 from; #X floatatom 299 177 5 0 0 0 - - -; #X obj 190 151 unpack 0 0 0 0 0; #X obj 35 213 route port; #X floatatom 197 245 5 0 0 0 - - -; #X text 236 244 listening port; #X connect 1 0 2 0; #X connect 2 0 9 0; #X connect 2 1 10 0; #X connect 9 0 3 0; #X connect 9 1 4 0; #X connect 9 2 5 0; #X connect 9 3 6 0; #X connect 9 4 8 0; #X connect 10 0 11 0; #X restore 317 111 pd get.info; #X text 373 159 check also:; #X obj 375 182 udpsend; #X obj 375 208 udpserver; #X connect 6 0 5 0; #X connect 6 1 9 0; #X connect 6 2 14 0; #X connect 9 0 0 0; #X connect 9 1 1 0; #X connect 9 2 2 0; #X connect 9 3 3 0; #X connect 9 4 8 0; #X connect 10 0 6 0; iemnet-0.1/tcpclient.c0000644000175100001440000002207011445657050014540 0ustar zmoelnigusers/* tcpclient.c * copyright (c) 2010 IOhannes m zmölnig, IEM * copyright (c) 2006-2010 Martin Peach * copyright (c) 2004 Olaf Matthes */ /* */ /* A client for bidirectional communication from within Pd. */ /* */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version 2 */ /* of the License, or (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* */ /* ---------------------------------------------------------------------------- */ #define DEBUGLEVEL 1 #include "iemnet.h" #include #include static t_class *tcpclient_class; static char objName[] = "tcpclient"; typedef struct _tcpclient { t_object x_obj; t_clock *x_clock; t_outlet *x_msgout; t_outlet *x_addrout; t_outlet *x_connectout; t_outlet *x_statusout; t_iemnet_sender *x_sender; t_iemnet_receiver*x_receiver; int x_serialize; int x_fd; // the socket char *x_hostname; // address we want to connect to as text int x_connectstate; // 0 = not connected, 1 = connected int x_port; // port we're connected to long x_addr; // address we're connected to as 32bit int /* multithread stuff */ pthread_t x_threadid; /* id of child thread */ pthread_attr_t x_threadattr; /* attributes of child thread */ t_iemnet_floatlist *x_floatlist; } t_tcpclient; static void tcpclient_receive_callback(void *x, t_iemnet_chunk*); static void tcpclient_info(t_tcpclient *x) { // "server " // "bufsize " static t_atom output_atom[3]; if(x&&x->x_connectstate) { int sockfd = x->x_fd; unsigned short port = x->x_port; const char*hostname=x->x_hostname; int insize =iemnet__receiver_getsize(x->x_receiver); int outsize=iemnet__sender_getsize (x->x_sender ); SETFLOAT (output_atom+0, sockfd); SETSYMBOL(output_atom+1, gensym(hostname)); SETFLOAT (output_atom+2, port); outlet_anything( x->x_statusout, gensym("server"), 3, output_atom); SETFLOAT (output_atom+0, insize); SETFLOAT (output_atom+1, outsize); outlet_anything( x->x_statusout, gensym("bufsize"), 2, output_atom); } } /* connection handling */ static void *tcpclient_child_connect(void *w) { t_tcpclient *x = (t_tcpclient*) w; struct sockaddr_in server; struct hostent *hp; int sockfd; t_iemnet_sender*sender; t_iemnet_receiver*receiver; if (x->x_fd >= 0) { error("%s_connect: already connected", objName); return (x); } /* create a socket */ sockfd = socket(AF_INET, SOCK_STREAM, 0); DEBUG("send socket %d\n", sockfd); if (sockfd < 0) { sys_sockerror("tcpclient: socket"); return (x); } /* connect socket using hostname provided in command line */ server.sin_family = AF_INET; hp = gethostbyname(x->x_hostname); if (hp == 0) { sys_sockerror("tcpclient: bad host?\n"); return (x); } memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); /* assign client port number */ server.sin_port = htons((u_short)x->x_port); /* try to connect */ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) { sys_sockerror("tcpclient: connecting stream socket"); sys_closesocket(sockfd); return (x); } sender=iemnet__sender_create(sockfd); receiver=iemnet__receiver_create(sockfd, x, tcpclient_receive_callback); /* update the tcpclient-object (thread safe) */ sys_lock(); x->x_fd = sockfd; x->x_addr = ntohl(*(long *)hp->h_addr); x->x_sender=sender; x->x_receiver=receiver; x->x_connectstate = 1; /* use callback to set outlet in main thread */ clock_delay(x->x_clock, 0); sys_unlock(); return (x); } static void tcpclient_tick(t_tcpclient *x) { outlet_float(x->x_connectout, 1); } static void tcpclient_disconnect(t_tcpclient *x); static void tcpclient_connect(t_tcpclient *x, t_symbol *hostname, t_floatarg fportno) { if(x->x_fd>=0)tcpclient_disconnect(x); /* we get hostname and port and pass them on to the child thread that establishes the connection */ x->x_hostname = hostname->s_name; x->x_port = fportno; x->x_connectstate = 0; /* start child thread */ if(pthread_create(&x->x_threadid, &x->x_threadattr, tcpclient_child_connect, x) < 0) error("%s: could not create new thread", objName); } static void tcpclient_disconnect(t_tcpclient *x) { if (x->x_fd >= 0) { int fd=x->x_fd; x->x_fd = -1; DEBUG("disconnecting %x", x); if(x->x_sender)iemnet__sender_destroy(x->x_sender); x->x_sender=NULL; if(x->x_receiver)iemnet__receiver_destroy(x->x_receiver); x->x_receiver=NULL; DEBUG("disconnect cleaning up %x", x); sys_closesocket(fd); x->x_connectstate = 0; outlet_float(x->x_connectout, 0); } else pd_error(x, "%s: not connected", objName); } /* sending/receiving */ static void tcpclient_send(t_tcpclient *x, t_symbol *s, int argc, t_atom *argv) { int size=0; t_atom output_atom; t_iemnet_sender*sender=x->x_sender; t_iemnet_chunk*chunk=iemnet__chunk_create_list(argc, argv); if(sender && chunk) { size=iemnet__sender_send(sender, chunk); } iemnet__chunk_destroy(chunk); SETFLOAT(&output_atom, size); outlet_anything( x->x_statusout, gensym("sent"), 1, &output_atom); if(size<0) { tcpclient_disconnect(x); } } static void tcpclient_receive_callback(void*y, t_iemnet_chunk*c) { t_tcpclient *x=(t_tcpclient*)y; if(c) { iemnet__addrout(x->x_statusout, x->x_addrout, x->x_addr, x->x_port); x->x_floatlist=iemnet__chunk2list(c, x->x_floatlist); // get's destroyed in the dtor iemnet__streamout(x->x_msgout, x->x_floatlist->argc, x->x_floatlist->argv, x->x_serialize); } else { // disconnected tcpclient_disconnect(x); } } static void tcpclient_serialize(t_tcpclient *x, t_floatarg doit) { x->x_serialize=doit; } /* constructor/destructor */ static void *tcpclient_new(void) { int i; t_tcpclient *x = (t_tcpclient *)pd_new(tcpclient_class); x->x_msgout = outlet_new(&x->x_obj, 0); /* received data */ x->x_addrout = outlet_new(&x->x_obj, gensym("list")); x->x_connectout = outlet_new(&x->x_obj, gensym("float")); /* connection state */ x->x_statusout = outlet_new(&x->x_obj, 0);/* last outlet for everything else */ x->x_serialize=1; x->x_fd = -1; x->x_addr = 0L; x->x_port = 0; x->x_sender=NULL; x->x_receiver=NULL; x->x_clock = clock_new(x, (t_method)tcpclient_tick); x->x_floatlist=iemnet__floatlist_create(1024); /* prepare child thread */ if(pthread_attr_init(&x->x_threadattr) < 0) error("%s: warning: could not prepare child thread", objName); if(pthread_attr_setdetachstate(&x->x_threadattr, PTHREAD_CREATE_DETACHED) < 0) error("%s: warning: could not prepare child thread", objName); return (x); } static void tcpclient_free(t_tcpclient *x) { tcpclient_disconnect(x); if(x->x_clock)clock_free(x->x_clock);x->x_clock=NULL; if(x->x_floatlist)iemnet__floatlist_destroy(x->x_floatlist);x->x_floatlist=NULL; } IEMNET_EXTERN void tcpclient_setup(void) { if(!iemnet__register(objName))return; tcpclient_class = class_new(gensym(objName), (t_newmethod)tcpclient_new, (t_method)tcpclient_free, sizeof(t_tcpclient), 0, A_DEFFLOAT, 0); class_addmethod(tcpclient_class, (t_method)tcpclient_connect, gensym("connect") , A_SYMBOL, A_FLOAT, 0); class_addmethod(tcpclient_class, (t_method)tcpclient_disconnect, gensym("disconnect"), 0); class_addmethod(tcpclient_class, (t_method)tcpclient_serialize, gensym("serialize"), A_FLOAT, 0); class_addmethod(tcpclient_class, (t_method)tcpclient_send, gensym("send"), A_GIMME, 0); class_addlist(tcpclient_class, (t_method)tcpclient_send); class_addbang(tcpclient_class, (t_method)tcpclient_info); DEBUGMETHOD(tcpclient_class); } IEMNET_INITIALIZER(tcpclient_setup); /* end of tcpclient.c */ iemnet-0.1/udpreceive.c0000644000175100001440000001255311454632564014716 0ustar zmoelnigusers/* udpreceive.c * copyright (c) 2010 IOhannes m zmölnig, IEM * copyright (c) 2006-2010 Martin Peach * copyright (c) Miller Puckette */ /* */ /* A server for unidirectional communication from within Pd. */ /* */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version 2 */ /* of the License, or (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define DEBUGLEVEL 1 static const char objName[] = "udpreceive"; #include "iemnet.h" /* ----------------------------- udpreceive ------------------------- */ static t_class *udpreceive_class; typedef struct _udpreceive { t_object x_obj; t_outlet *x_msgout; t_outlet *x_addrout; t_outlet *x_statout; int x_connectsocket; int x_port; t_iemnet_receiver*x_receiver; t_iemnet_floatlist *x_floatlist; } t_udpreceive; static void udpreceive_read_callback(void*y, t_iemnet_chunk*c) { t_udpreceive*x=(t_udpreceive*)y; if(c) { iemnet__addrout(x->x_statout, x->x_addrout, c->addr, c->port); x->x_floatlist=iemnet__chunk2list(c, x->x_floatlist); // gets destroyed in the dtor outlet_list(x->x_msgout, gensym("list"), x->x_floatlist->argc, x->x_floatlist->argv); } else { post("[%s] nothing received", objName); } } static void udpreceive_port(t_udpreceive*x, t_floatarg fportno) { static t_atom ap[1]; int portno = fportno; struct sockaddr_in server; socklen_t serversize=sizeof(server); int sockfd = x->x_connectsocket; int intarg; SETFLOAT(ap, -1); if(x->x_port == portno) { return; } /* cleanup any open ports */ if(sockfd>=0) { iemnet__receiver_destroy(x->x_receiver); x->x_connectsocket=-1; x->x_port=-1; } sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd<0) { error("[%s]: unable to create socket", objName); return; } /* ask OS to allow another Pd to reopen this port after we close it. */ #ifdef SO_REUSEADDR intarg = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&intarg, sizeof(intarg)) < 0) { error("[%s]: setsockopt (SO_REUSEADDR) failed", objName); } #endif /* SO_REUSEADDR */ #ifdef SO_REUSEPORT intarg = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (char *)&intarg, sizeof(intarg)) < 0) { error("[%s]: setsockopt (SO_REUSEPORT) failed", objName); } #endif /* SO_REUSEPORT */ server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons((u_short)portno); /* name the socket */ if (bind(sockfd, (struct sockaddr *)&server, serversize) < 0) { sys_sockerror("[udpreceive] bind failed"); sys_closesocket(sockfd); sockfd = -1; outlet_anything(x->x_statout, gensym("port"), 1, ap); return; } x->x_connectsocket = sockfd; x->x_port = portno; // find out which port is actually used (useful when assigning "0") if(!getsockname(sockfd, (struct sockaddr *)&server, &serversize)) { x->x_port=ntohs(server.sin_port); } x->x_receiver=iemnet__receiver_create(sockfd, x, udpreceive_read_callback); SETFLOAT(ap, x->x_port); outlet_anything(x->x_statout, gensym("port"), 1, ap); } static void *udpreceive_new(t_floatarg fportno) { t_udpreceive*x = (t_udpreceive *)pd_new(udpreceive_class); x->x_msgout = outlet_new(&x->x_obj, 0); x->x_addrout = outlet_new(&x->x_obj, gensym("list")); x->x_statout = outlet_new(&x->x_obj, 0); x->x_connectsocket = -1; x->x_port = -1; x->x_receiver = NULL; x->x_floatlist=iemnet__floatlist_create(1024); udpreceive_port(x, fportno); return (x); } static void udpreceive_free(t_udpreceive *x) { iemnet__receiver_destroy(x->x_receiver); x->x_connectsocket=0; outlet_free(x->x_msgout); outlet_free(x->x_addrout); outlet_free(x->x_statout); if(x->x_floatlist)iemnet__floatlist_destroy(x->x_floatlist);x->x_floatlist=NULL; } IEMNET_EXTERN void udpreceive_setup(void) { if(!iemnet__register(objName))return; udpreceive_class = class_new(gensym(objName), (t_newmethod)udpreceive_new, (t_method)udpreceive_free, sizeof(t_udpreceive), 0, A_DEFFLOAT, 0); class_addmethod(udpreceive_class, (t_method)udpreceive_port, gensym("port"), A_DEFFLOAT, 0); DEBUGMETHOD(udpreceive_class); } IEMNET_INITIALIZER(udpreceive_setup); /* end udpreceive.c */ iemnet-0.1/README.txt0000644000175100001440000000573211354606435014113 0ustar zmoelnigusersiemnet - networking for Pd ========================== this is a fork of martin peach's "net" library, that allows low-level interaction with networks on OSI-layer 5 (transport layer). for a list of features, see FEATURES.txt Why fork? ========= the original library is still actively maintained by martin peach. however: - forking allows me to experiment with new features/techniques more easily - forking allows to remove all the legacy cruft (and not care about compatibility now) - the development mode in the original library would involve the upstream author "signing-off" any changes (debatable; but i don't want to submit experimental code to their stable code base) in practice one of the major drawbacks i see in upstream is, that (in the multithreaded objects), for each message a separate thread is spawned. this leads to excessive use of system ressources (detaching and joining threads takes time), easy DoS (each thread uses one in a limited number of thread handles), and abandons determinism (nobody guarantees that parallel threads are executed "in order"; thus a message in a later-spawned thread might be delivered to the socket earlier than older messages - effectively circumventing one of the promises of TCP/IP: that all packets will reappear in order; i haven't seen this behaviour of mrpeach/net in real life yet; however i don't see any countermeasurements either) on the long run compatibility with the upstream library is intended. (though probably not for all the cruft that is in there) Design: ======= easy to maintain: re-used code is bundled in a small "library" (currently only a single file ienet.c), which is linked statically against the externals. this library handles all the send/receive stuff (whether it uses threads or not and if so how, is an implementation detail) the lib doesn't know anything about the actual transport protocol. it only interacts with a socket. easy to run: think speed, think reliability all known implementations for pd are either slow or will freeze Pd when under _heavy_ load. most do both. iemnet wants to provide objects whih allow you to saturate the network connection and still keep Pd reactive. (sidenote: saturating even a 100MBit network with Pd might lead to audio dropouts; this is not necessarily related to the network but rather to the amount of data processed by Pd...) easy to use: probably not; but at least it has the same (basic) API as mrpeach/net so a switch should be easy. "basic" means "not everything", so messages for special workarounds in mrpeach/net (e.g. the block/unblock stuff) are not supported, as well as debugging features ("dump") and features not related to networking (e.g. the ability to read a file from harddisk) Authors: ======== currently iemnet is developed by IOhannes m zmölnig it (being a fork) is heavily based on code written by Martin Peach, who again has used code by Olaf Matthes and Miller Puckette LICENSE: ======== iemnet is published under the GPL. see LICENSE.txt for more information iemnet-0.1/build/0000755000175100001440000000000011456321231013474 5ustar zmoelnigusersiemnet-0.1/build/w32-vs2003/0000755000175100001440000000000011456321231015042 5ustar zmoelnigusersiemnet-0.1/build/w32-vs2003/iemnet.vcproj0000644000175100001440000000637511355061757017577 0ustar zmoelnigusers iemnet-0.1/build/w32-vs2003/iemnet.sln0000644000175100001440000000134611352433170017046 0ustar zmoelnigusersMicrosoft Visual Studio Solution File, Format Version 8.00 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "iemnet", "iemnet.vcproj", "{6B55773B-3FF5-4F09-B538-2A7007DEC4DB}" ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject Global GlobalSection(SolutionConfiguration) = preSolution Release = Release EndGlobalSection GlobalSection(ProjectConfiguration) = postSolution {6B55773B-3FF5-4F09-B538-2A7007DEC4DB}.Release.ActiveCfg = Release|Win32 {6B55773B-3FF5-4F09-B538-2A7007DEC4DB}.Release.Build.0 = Release|Win32 EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EndGlobalSection GlobalSection(ExtensibilityAddIns) = postSolution EndGlobalSection EndGlobal iemnet-0.1/udpserver-help.pd0000644000175100001440000000723611453713115015702 0ustar zmoelnigusers#N canvas 3 45 858 382 12; #X floatatom 78 159 5 0 0 0 - - -; #X floatatom 102 189 5 0 0 0 - - -; #X floatatom 126 239 3 0 0 0 - - -; #X floatatom 153 239 3 0 0 0 - - -; #X floatatom 180 239 3 0 0 0 - - -; #X floatatom 207 239 3 0 0 0 - - -; #X text 89 238 from; #X text -6 159 connections; #X msg 55 61 broadcast 1 2 3; #X text 173 62 send to all clients; #X text 32 189 on socket; #X text 162 125 argument is port number; #X text 52 350 ***WARNING*** Attempting to print long messages can hang pd!; #X obj 55 290 spigot; #X obj 94 270 tgl 15 0 empty empty enable_print 17 7 0 10 -24198 -258699 -45076 0 1; #X text 54 333 Received messages are output as stream of bytes; #X msg 70 94 11 216 43; #X obj 304 288 spigot; #X obj 343 268 tgl 15 0 empty empty enable_print 17 7 0 10 -24198 -258699 -45076 0 1; #N canvas 3 45 768 526 getting.info 0; #X floatatom 99 162 5 0 0 0 - - -; #X text 139 163 to_client; #X floatatom 390 165 3 0 0 0 - - -; #X symbolatom 422 140 15 0 0 0 - - -; #X floatatom 359 189 5 0 0 0 - - -; #X floatatom 454 115 7 0 0 0 - - -; #X floatatom 180 117 5 0 0 0 - - -; #X text 223 118 on_socket; #X floatatom 139 141 5 0 0 0 - - -; #X obj 99 62 route sent; #X obj 359 61 route client; #X text 533 141 ip; #X text 505 115 port; #X text 416 165 socket; #X text 400 191 client; #X text 184 140 sent_bytes; #X floatatom 99 342 5 0 0 0 - - -; #X floatatom 180 297 5 0 0 0 - - -; #X floatatom 139 321 5 0 0 0 - - -; #X obj 99 242 route bufsize; #X floatatom 93 501 3 0 0 0 - - -; #X floatatom 147 501 3 0 0 0 - - -; #X floatatom 120 501 3 0 0 0 - - -; #X obj 93 421 route address; #X obj 99 271 unpack f f f; #X obj 359 87 unpack f f s f; #X obj 99 91 unpack f f f; #X obj 93 450 unpack f f f f f; #X floatatom 174 501 3 0 0 0 - - -; #X floatatom 202 475 6 0 0 0 - - -; #X text 200 501 ip; #X text 248 475 port; #X text 139 342 socket; #X text 178 322 insize (bytes); #X text 218 298 outsize (bytes); #X text 361 261 info about current inqueue/outqueue; #X text 354 435 NOTE: outlets #3 and #4 are for compatibility with mrpeach's net obects. You can get all information via outlet #5 as well; #X obj 99 36 r \$0.udpserver.o5; #X obj 99 216 r \$0.udpserver.o5; #X obj 93 395 r \$0.udpserver.o5; #X obj 359 35 r \$0.udpserver.o5; #X connect 9 0 26 0; #X connect 10 0 25 0; #X connect 19 0 24 0; #X connect 23 0 27 0; #X connect 24 0 16 0; #X connect 24 1 18 0; #X connect 24 2 17 0; #X connect 25 0 4 0; #X connect 25 1 2 0; #X connect 25 2 3 0; #X connect 25 3 5 0; #X connect 26 0 0 0; #X connect 26 1 8 0; #X connect 26 2 6 0; #X connect 27 0 20 0; #X connect 27 1 22 0; #X connect 27 2 21 0; #X connect 27 3 28 0; #X connect 27 4 29 0; #X connect 37 0 9 0; #X connect 38 0 19 0; #X connect 39 0 23 0; #X connect 40 0 10 0; #X restore 530 236 pd getting.info; #X text 523 306 copyright (c) 2009 Martin Peach; #X text 523 323 copyright (c) 2010 Roman Haefeli; #X text 523 340 copyright (c) 2010 IOhannes m zmoelnig; #X text 510 74 check also:; #X text 21 6 [udpserver] waits for clients to connect to its port. ; #X obj 55 126 udpserver 9997; #X obj 513 96 udpreceive; #X obj 513 121 udpclient; #X obj 150 153 s \$0.udpserver.o5; #X obj 304 243 r \$0.udpserver.o5; #X obj 126 216 unpack f f f f f; #X floatatom 235 240 6 0 0 0 - - -; #X obj 55 313 print udpserver:received; #X obj 304 314 print udpserver:status; #X text 145 94 or without 'broadcast' selector; #X connect 8 0 25 0; #X connect 13 0 32 0; #X connect 14 0 13 1; #X connect 16 0 25 0; #X connect 17 0 33 0; #X connect 18 0 17 1; #X connect 25 0 13 0; #X connect 25 1 0 0; #X connect 25 2 1 0; #X connect 25 3 30 0; #X connect 25 4 28 0; #X connect 29 0 17 0; #X connect 30 0 2 0; #X connect 30 1 3 0; #X connect 30 2 4 0; #X connect 30 3 5 0; #X connect 30 4 31 0; iemnet-0.1/test.txt0000644000175100001440000000002711010276266014117 0ustar zmoelniguserstesting one two three iemnet-0.1/udpsend-help.pd0000644000175100001440000000157111453711672015327 0ustar zmoelnigusers#N canvas 76 70 590 348 12; #X msg 72 182 disconnect; #X msg 16 59 connect 127.0.0.1 9997; #X obj 16 306 tgl 15 0 empty empty connected 20 7 0 8 -24198 -241291 -1 0 1; #X text 220 60 <--first; #X msg 25 101 send 0 1 2 3; #X text 8 5 udpsend sends bytes over a udp connection.; #X text 8 28 Used in conjunction with packOSC will send OSC over udp ; #X obj 16 283 udpsend; #X text 141 101 send raw data; #X msg 40 126 99 98 97; #X text 106 128 'send' prefix is optional; #X text 202 293 (c) copyright 2007 Martin Peach; #X text 201 311 (c) copyright 2010 IOhannes m zmoelnig; #X text 171 208 NOTE: with the iemnet version of [udpsend] you CANNOT send files directly! use mrpeach's [binfile] to read the file.; #X text 406 85 check also:; #X obj 409 110 udpclient; #X obj 409 137 udpreceive; #X connect 0 0 7 0; #X connect 1 0 7 0; #X connect 4 0 7 0; #X connect 7 0 2 0; #X connect 9 0 7 0; iemnet-0.1/udpclient.c0000644000175100001440000002016011445657050014540 0ustar zmoelnigusers/* udpclient.c * copyright (c) 2010 IOhannes m zmölnig, IEM */ /* */ /* A client for bidirectional communication from within Pd. */ /* */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version 2 */ /* of the License, or (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* */ /* ---------------------------------------------------------------------------- */ #define DEBUGLEVEL 1 #include "iemnet.h" #include #include static t_class *udpclient_class; static const char objName[] = "udpclient"; typedef struct _udpclient { t_object x_obj; t_clock *x_clock; t_outlet *x_msgout; t_outlet *x_addrout; t_outlet *x_connectout; t_outlet *x_statusout; t_iemnet_sender*x_sender; t_iemnet_receiver*x_receiver; int x_fd; // the socket char *x_hostname; // address we want to connect to as text int x_connectstate; // 0 = not connected, 1 = connected int x_port; // port we're connected to long x_addr; // address we're connected to as 32bit int /* multithread stuff */ pthread_t x_threadid; /* id of child thread */ pthread_attr_t x_threadattr; /* attributes of child thread */ t_iemnet_floatlist *x_floatlist; } t_udpclient; static void udpclient_receive_callback(void *x, t_iemnet_chunk*); /* connection handling */ static void *udpclient_child_connect(void *w) { t_udpclient *x = (t_udpclient*) w; struct sockaddr_in server; struct hostent *hp; int sockfd; int broadcast = 1;/* nonzero is true */ if (x->x_sender) { error("[%s] already connected", objName); return (x); } /* create a socket */ sockfd = socket(AF_INET, SOCK_DGRAM, 0); DEBUG("send socket %d\n", sockfd); if (sockfd < 0) { sys_sockerror("udpclient: socket"); return (x); } /* Based on zmoelnig's patch 2221504: Enable sending of broadcast messages (if hostname is a broadcast address)*/ #ifdef SO_BROADCAST if( 0 != setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (const void *)&broadcast, sizeof(broadcast))) { error("[%s] couldn't switch to broadcast mode", objName); } #endif /* SO_BROADCAST */ /* connect socket using hostname provided in command line */ server.sin_family = AF_INET; hp = gethostbyname(x->x_hostname); if (hp == 0) { error("[%s] bad host '%s'?", objName, x->x_hostname); return (x); } memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); /* assign client port number */ server.sin_port = htons((u_short)x->x_port); DEBUG("connecting to port %d", x->x_port); /* try to connect. */ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) { sys_sockerror("udpclient: connecting stream socket"); sys_closesocket(sockfd); return (x); } x->x_fd = sockfd; x->x_addr = ntohl(*(long *)hp->h_addr); x->x_sender=iemnet__sender_create(sockfd); x->x_receiver=iemnet__receiver_create(sockfd, x, udpclient_receive_callback); x->x_connectstate = 1; clock_delay(x->x_clock, 0); return (x); } static void udpclient_tick(t_udpclient *x) { outlet_float(x->x_connectout, 1); } static void udpclient_disconnect(t_udpclient *x) { if (x->x_fd >= 0) { DEBUG("disconnect %x %x", x->x_sender, x->x_receiver); if(x->x_receiver)iemnet__receiver_destroy(x->x_receiver); x->x_receiver=NULL; if(x->x_sender)iemnet__sender_destroy(x->x_sender); x->x_sender=NULL; sys_closesocket(x->x_fd); x->x_fd = -1; x->x_connectstate = 0; outlet_float(x->x_connectout, 0); } else pd_error(x, "[%s] not connected", objName); } static void udpclient_connect(t_udpclient *x, t_symbol *hostname, t_floatarg fportno) { if(x->x_fd>=0)udpclient_disconnect(x); /* we get hostname and port and pass them on to the child thread that establishes the connection */ x->x_hostname = hostname->s_name; x->x_port = fportno; x->x_connectstate = 0; /* start child thread */ if(pthread_create(&x->x_threadid, &x->x_threadattr, udpclient_child_connect, x) < 0) error("%s: could not create new thread", objName); } /* sending/receiving */ static void udpclient_send(t_udpclient *x, t_symbol *s, int argc, t_atom *argv) { int size=0; t_atom output_atom; t_iemnet_sender*sender=x->x_sender; t_iemnet_chunk*chunk=iemnet__chunk_create_list(argc, argv); if(sender && chunk) { size=iemnet__sender_send(sender, chunk); } iemnet__chunk_destroy(chunk); SETFLOAT(&output_atom, size); outlet_anything( x->x_statusout, gensym("sent"), 1, &output_atom); } static void udpclient_receive_callback(void*y, t_iemnet_chunk*c) { t_udpclient *x=(t_udpclient*)y; if(c) { iemnet__addrout(x->x_statusout, x->x_addrout, x->x_addr, x->x_port); x->x_floatlist=iemnet__chunk2list(c, x->x_floatlist); // gets destroyed in the dtor outlet_list(x->x_msgout, gensym("list"),x->x_floatlist->argc, x->x_floatlist->argv); } else { // disconnected DEBUG("disconnected"); if(x->x_fd >= 0) { udpclient_disconnect(x); } } } /* constructor/destructor */ static void *udpclient_new(void) { int i; t_udpclient *x = (t_udpclient *)pd_new(udpclient_class); x->x_msgout = outlet_new(&x->x_obj, 0); /* received data */ x->x_addrout = outlet_new(&x->x_obj, gensym("list")); x->x_connectout = outlet_new(&x->x_obj, gensym("float")); /* connection state */ x->x_statusout = outlet_new(&x->x_obj, 0);/* last outlet for everything else */ x->x_fd = -1; x->x_addr = 0L; x->x_port = 0; x->x_sender=NULL; x->x_receiver=NULL; x->x_clock = clock_new(x, (t_method)udpclient_tick); x->x_floatlist=iemnet__floatlist_create(1024); /* prepare child thread */ if(pthread_attr_init(&x->x_threadattr) < 0) verbose(1, "[%s] warning: could not prepare child thread", objName); if(pthread_attr_setdetachstate(&x->x_threadattr, PTHREAD_CREATE_DETACHED) < 0) verbose(1, "[%s] warning: could not prepare child thread", objName); return (x); } static void udpclient_free(t_udpclient *x) { udpclient_disconnect(x); if(x->x_clock)clock_free(x->x_clock);x->x_clock=NULL; if(x->x_floatlist)iemnet__floatlist_destroy(x->x_floatlist);x->x_floatlist=NULL; } IEMNET_EXTERN void udpclient_setup(void) { if(!iemnet__register(objName))return; udpclient_class = class_new(gensym(objName), (t_newmethod)udpclient_new, (t_method)udpclient_free, sizeof(t_udpclient), 0, A_DEFFLOAT, 0); class_addmethod(udpclient_class, (t_method)udpclient_connect, gensym("connect") , A_SYMBOL, A_FLOAT, 0); class_addmethod(udpclient_class, (t_method)udpclient_disconnect, gensym("disconnect"), 0); class_addmethod(udpclient_class, (t_method)udpclient_send, gensym("send"), A_GIMME, 0); class_addlist(udpclient_class, (t_method)udpclient_send); DEBUGMETHOD(udpclient_class); } IEMNET_INITIALIZER(udpclient_setup); /* end of udpclient.c */