From 9904255b1b2e79b19549df633308a356f038d61c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Thu, 1 Apr 2010 07:22:13 +0000 Subject: non-functional crashy copy of tcpserver for udp svn path=/trunk/externals/iem/iemnet/; revision=13334 --- Makefile | 2 +- udpserver.c | 643 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 644 insertions(+), 1 deletion(-) create mode 100644 udpserver.c diff --git a/Makefile b/Makefile index 90d8b36..62e7bc4 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 udpclient.c +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 diff --git a/udpserver.c b/udpserver.c new file mode 100644 index 0000000..1844c3c --- /dev/null +++ b/udpserver.c @@ -0,0 +1,643 @@ +/* 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 DEBUG +#include "iemnet.h" +#include + +#define MAX_CONNECT 32 /* maximum number of connections */ + +/* ----------------------------- udpserver ------------------------- */ + +static t_class *udpserver_class; +static const char objName[] = "udpserver"; + +typedef struct _udpserver_socketreceiver +{ + struct _udpserver *sr_owner; + + long sr_host; + unsigned short sr_port; + t_int sr_fd; + t_iemnet_sender*sr_sender; + t_iemnet_receiver*sr_receiver; +} t_udpserver_socketreceiver; + +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_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_receiver*x_receiver; +} t_udpserver; + +static void udpserver_receive_callback(void*x, t_iemnet_chunk*,int argc, t_atom*argv); + +static t_udpserver_socketreceiver *udpserver_socketreceiver_new(t_udpserver *owner, int sockfd, struct sockaddr_in*addr) +{ + t_udpserver_socketreceiver *x = (t_udpserver_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, udpserver_receive_callback); + } + return (x); +} + +static void udpserver_socketreceiver_free(t_udpserver_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; + + freebytes(x, sizeof(*x)); + + if(sender) iemnet__sender_destroy(sender); + if(receiver)iemnet__receiver_destroy(receiver); + + sys_closesocket(sockfd); + } + DEBUG("freeed %x", x); +} + +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); +} + + +/* ---------------- 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_sr[client]->sr_receiver); + int outsize=iemnet__sender_getsize (x->x_sr[client]->sr_sender ); + + snprintf(hostname, MAXPDSTRING-1, "%d.%d.%d.%d", + (address & 0xFF000000)>>24, + (address & 0x0FF0000)>>16, + (address & 0x0FF00)>>8, + (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 + 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 udpserver_info_connection(t_udpserver *x, t_udpserver_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 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; + + 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); + + /* 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; + 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) + { + 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); + 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); + udpserver_info_connection(x, x->x_sr[client]); + + udpserver_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 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); + } +} + +/* ---------------- main udpserver (receive) stuff --------------------- */ +static void udpserver_receive_callback(void *y0, + t_iemnet_chunk*c, + int argc, t_atom*argv) { + t_udpserver_socketreceiver *y=(t_udpserver_socketreceiver*)y0; + t_udpserver*x=NULL; + if(NULL==y || NULL==(x=y->sr_owner))return; + + if(argc) { + udpserver_info_connection(x, y); + iemnet__streamout(x->x_msgout, argc, argv); + } else { + // disconnected + int sockfd=y->sr_fd; + udpserver_disconnect_socket(x, sockfd); + } + + // post("udpserver: %d bytes in %d packets", bytecount, packetcount); +} + +static void udpserver_connectpoll(t_udpserver *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_udpserver_socketreceiver *y = udpserver_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; + + 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; + + udpserver_port(x, fportno); + + 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_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); + } +} + +IEMNET_EXTERN void udpserver_setup(void) +{ + if(!iemnet__register(objName))return; + + 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_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); +} + +IEMNET_INITIALIZER(udpserver_setup); + + +/* end of udpserver.c */ -- cgit v1.2.1