From c4d13b9652c9b64106e4d809017cbbdfecef2252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 26 Mar 2010 14:34:50 +0000 Subject: a simple udpclient bidirectional communication using UDP svn path=/trunk/externals/iem/iemnet/; revision=13284 --- Makefile | 10 +-- udpclient.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 271 insertions(+), 5 deletions(-) create mode 100644 udpclient.c diff --git a/Makefile b/Makefile index ecbbd7b..8045822 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ 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 +SOURCES = tcpserver.c tcpclient.c tcpsend.c tcpreceive.c udpreceive.c udpsend.c udpclient.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 @@ -29,7 +29,7 @@ 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 = README.txt +EXTRA_DIST = #------------------------------------------------------------------------------# @@ -181,9 +181,9 @@ install-doc: test -z "$(PDOBJECTS)" || \ $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) \ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) - $(INSTALL_FILE) README $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/README.txt - $(INSTALL_FILE) VERSION $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/VERSION.txt - $(INSTALL_FILE) CHANGES $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/CHANGES.txt + $(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: diff --git a/udpclient.c b/udpclient.c new file mode 100644 index 0000000..1a1a2ac --- /dev/null +++ b/udpclient.c @@ -0,0 +1,266 @@ +/* udpclient.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. */ +/* */ + +/* ---------------------------------------------------------------------------- */ + +#include "iemnet.h" +#include + +#include + + + +static t_class *udpclient_class; +static char objName[] = "udpclient"; + + +typedef struct _udpclient +{ + t_object x_obj; + t_clock *x_clock; + t_clock *x_poll; + 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 + t_atom x_addrbytes[4]; // address we're connected to as 4 bytes + + + /* multithread stuff */ + pthread_t x_threadid; /* id of child thread */ + pthread_attr_t x_threadattr; /* attributes of child thread */ +} t_udpclient; + + +static void udpclient_receive_callback(void *x, + t_iemnet_chunk*, + int argc, t_atom*argv); + + + +/* 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("udpsend: already connected"); + return (x); + } + + /* create a socket */ + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + DEBUG("send socket %d\n", sockfd); + if (sockfd < 0) + { + sys_sockerror("udpsend: 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("udpsend: couldn't switch to broadcast mode"); + } +#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("udpsend: bad host '%s'?", 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("udpsend: connecting stream socket"); + sys_closesocket(sockfd); + return (x); + } + 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) + { + 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; + + 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) + post("%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, int argc, t_atom*argv) { + t_udpclient *x=(t_udpclient*)y; + + if(argc) { + outlet_list(x->x_msgout, gensym("list"), argc, argv); + } else { + // disconnected + DEBUG("disconnected"); + 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; + + for (i = 0; i < 4; ++i) + { + SETFLOAT(x->x_addrbytes+i, 0); + } + x->x_addr = 0L; + + x->x_sender=NULL; + x->x_receiver=NULL; + + + x->x_clock = clock_new(x, (t_method)udpclient_tick); + + /* prepare child thread */ + if(pthread_attr_init(&x->x_threadattr) < 0) + post("%s: warning: could not prepare child thread", objName); + if(pthread_attr_setdetachstate(&x->x_threadattr, PTHREAD_CREATE_DETACHED) < 0) + post("%s: warning: could not prepare child thread", objName); + + + return (x); +} + +static void udpclient_free(t_udpclient *x) +{ + udpclient_disconnect(x); + clock_free(x->x_poll); + clock_free(x->x_clock); +} + +IEMNET_EXTERN void udpclient_setup(void) +{ + post("udpclient"); + //static int again=0; if(again)return; again=1; + + 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); + + post("iemnet: networking with Pd :: %s", objName); + post(" (c) 2010 IOhannes m zmoelnig, IEM"); + post(" based on mrpeach/net, based on maxlib"); +} + + +IEMNET_INITIALIZER(udpclient_setup); + +/* end of udpclient.c */ -- cgit v1.2.1