/* udpreceive.c * copyright © 2010-2015 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, see */ /* http://www.gnu.org/licenses/ */ #define DEBUGLEVEL 1 static const char objName[] = "udpreceive"; #include "iemnet.h" #include /* ----------------------------- 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_fd; int x_port; t_iemnet_receiver *x_receiver; t_iemnet_floatlist*x_floatlist; int x_reuseport, x_reuseaddr; } 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 { iemnet_log(x, IEMNET_VERBOSE, "nothing received"); } } static int udpreceive_setport(t_udpreceive*x, unsigned short portno) { struct sockaddr_in server; socklen_t serversize=sizeof(server); int sockfd = x->x_fd; int intarg; memset(&server, 0, sizeof(server)); if(x->x_port == portno) { iemnet_log(x, IEMNET_VERBOSE, "skipping re-binding to port:%d", portno); return 1; } /* cleanup any open ports */ if(x->x_receiver) { iemnet__receiver_destroy(x->x_receiver, 0); x->x_receiver=NULL; } if(sockfd>=0) { iemnet__closesocket(sockfd, 1); x->x_fd=-1; x->x_port=-1; } sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd<0) { iemnet_log(x, IEMNET_ERROR, "unable to create socket"); sys_sockerror("socket"); return 0; } /* ask OS to allow another Pd to reopen this port after we close it. */ #ifdef SO_REUSEADDR if(x->x_reuseaddr) { intarg = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&intarg, sizeof(intarg)) < 0) { iemnet_log(x, IEMNET_ERROR, "unable to enable address re-using"); sys_sockerror("setsockopt:SO_REUSEADDR"); } } #endif /* SO_REUSEADDR */ #ifdef SO_REUSEPORT if(x->x_reuseport) { intarg = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (void *)&intarg, sizeof(intarg)) < 0) { iemnet_log(x, IEMNET_ERROR, "unable to enable port re-using"); sys_sockerror("setsockopt:SO_REUSEPORT"); } } #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) { iemnet_log(x, IEMNET_ERROR, "unable to bind to socket"); sys_sockerror("bind"); iemnet__closesocket(sockfd, 1); sockfd = -1; return 0; } x->x_fd = 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, 0); return 1; } static void udpreceive_port(t_udpreceive*x, t_symbol*s, int argc, t_atom*argv) { t_atom ap[1]; if(argc) { if(argc>1 || A_FLOAT != argv->a_type) { iemnet_log(x, IEMNET_ERROR, "usage: %s []", s->s_name); return; } SETFLOAT(ap, -1); if(!udpreceive_setport(x, atom_getint(argv))) { outlet_anything(x->x_statout, gensym("port"), 1, ap); } } SETFLOAT(ap, x->x_port); outlet_anything(x->x_statout, gensym("port"), 1, ap); } static void udpreceive_optionI(t_udpreceive*x, t_symbol*s, int argc, t_atom*argv) { int*reuse=NULL; if(gensym("reuseport")==s) { reuse=&x->x_reuseport; } if(gensym("reuseaddr")==s) { reuse=&x->x_reuseaddr; } if(!reuse) { iemnet_log(x, IEMNET_ERROR, "unknown option '%s'", s->s_name); return; } if(argc) { if(1==argc && A_FLOAT == argv->a_type) { *reuse=atom_getint(argv); return; } else { iemnet_log(x, IEMNET_ERROR, "usage: %s []", s->s_name); return; } } else { t_atom ap[1]; SETFLOAT(ap, *reuse); outlet_anything(x->x_statout, s, 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_fd = -1; x->x_port = -1; x->x_receiver = NULL; x->x_floatlist=iemnet__floatlist_create(1024); x->x_reuseaddr = 1; x->x_reuseport = 0; udpreceive_setport(x, fportno); return (x); } static void udpreceive_free(t_udpreceive *x) { if(x->x_receiver) { iemnet__receiver_destroy(x->x_receiver, 0); } x->x_receiver=NULL; if(x->x_fd >= 0) { iemnet__closesocket(x->x_fd, 0); } x->x_fd=-1; 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_GIMME, 0); /* options for opening new sockets */ class_addmethod(udpreceive_class, (t_method)udpreceive_optionI, gensym("reuseaddr"), A_GIMME, 0); class_addmethod(udpreceive_class, (t_method)udpreceive_optionI, gensym("reuseport"), A_GIMME, 0); DEBUGMETHOD(udpreceive_class); } IEMNET_INITIALIZER(udpreceive_setup); /* end udpreceive.c */