Codebase list pd-iemnet / fresh-snapshots/main tcpclient.c
fresh-snapshots/main

Tree @fresh-snapshots/main (Download .tar.gz)

tcpclient.c @fresh-snapshots/mainraw · history · blame

/* tcpclient.c
 * copyright © 2010-2015 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, see                                         */
/*     http://www.gnu.org/licenses/                                             */
/*                                                                              */

/* ---------------------------------------------------------------------------- */
#define DEBUGLEVEL 1

#include "iemnet.h"
#include <string.h>

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
  const 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_iemnet_floatlist         *x_floatlist;
} t_tcpclient;


static void tcpclient_receive_callback(void *x, t_iemnet_chunk*);

static void tcpclient_info(t_tcpclient *x)
{
  // "server <socket> <IP> <port>"
  // "bufsize <insize> <outsize>"
  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 int tcpclient_do_disconnect(int fd, t_iemnet_sender*sender,
                                   t_iemnet_receiver*receiver)
{
  if(sender) {
    iemnet__sender_destroy(sender, 0);
    sender=NULL;
  }
  if(receiver) {
    iemnet__receiver_destroy(receiver, 0);
    receiver=NULL;
  }
  if (fd >= 0) {
    iemnet__closesocket(fd, 1);
    return 1;
  }
  return 0;
}
static int tcpclient_do_connect(const char*host, unsigned short port,
                                t_tcpclient*x,
                                t_iemnet_sender**senderOUT, t_iemnet_receiver**receiverOUT, long*addrOUT)
{
  struct sockaddr_in  server;
  struct hostent      *hp;
  int                 sockfd=-1;
  t_iemnet_sender*  sender;
  t_iemnet_receiver*receiver;

  /* connect socket using hostname provided in command line */
  memset(&server, 0, sizeof(server));
  server.sin_family = AF_INET;
  hp = gethostbyname(host);
  if (hp == 0) {
    iemnet_log(x, IEMNET_ERROR, "bad host '%s'?", host);
    return (-1);
  }
  memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);

  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0) {
    iemnet_log(x, IEMNET_ERROR, "unable to open socket");
    sys_sockerror("socket");
    return (sockfd);
  }

  /* assign client port number */
  server.sin_port = htons((u_short)port);

  /* try to connect */
  if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) {
    iemnet_log(x, IEMNET_ERROR, "unable to connect to stream socket");
    sys_sockerror("connect");
    iemnet__closesocket(sockfd, 1);
    return (-1);
  }

  sender=iemnet__sender_create(sockfd, NULL, NULL, 0);
  receiver=iemnet__receiver_create(sockfd, x,  tcpclient_receive_callback,
                                   0);

  if(addrOUT) {
    *addrOUT= ntohl(*(long *)hp->h_addr);
  }
  if(senderOUT) {
    *senderOUT=sender;
  }
  if(receiverOUT) {
    *receiverOUT=receiver;
  }
  return sockfd;
}

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)
{
  long addr=0;
  int state;

  // first disconnect any active connection
  if(x->x_hostname || x->x_port) {
    state=tcpclient_do_disconnect(x->x_fd, x->x_sender, x->x_receiver);
    if(state) {
      outlet_float(x->x_connectout, 0);
    }
  }

  /* 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;


  state=tcpclient_do_connect(x->x_hostname, x->x_port, x,
                             &x->x_sender, &x->x_receiver,
                             &x->x_addr);
  x->x_connectstate=(state>0);
  x->x_fd=state;
  if(state>0) {
    outlet_float(x->x_connectout, 1);
  }
}

static void tcpclient_disconnect(t_tcpclient *x)
{
  if(x->x_hostname || x->x_port) {
    int state=tcpclient_do_disconnect(x->x_fd, x->x_sender, x->x_receiver);

    if(!state && !x->x_port) {
      iemnet_log(x, IEMNET_ERROR, "not connected");
    }
  }
  outlet_float(x->x_connectout, 0);

  x->x_port=0;
  x->x_hostname=NULL;
}

/* 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("sendbuffersize"), 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_free_simple(t_tcpclient *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;

  if(x->x_msgout) {
    outlet_free(x->x_msgout);
  }
  if(x->x_addrout) {
    outlet_free(x->x_addrout);
  }
  if(x->x_connectout) {
    outlet_free(x->x_connectout);
  }
  if(x->x_statusout) {
    outlet_free(x->x_statusout);
  }
}

static void *tcpclient_new(void)
{
  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);

  return (x);
}

static void tcpclient_free(t_tcpclient *x)
{
  tcpclient_disconnect(x);
  tcpclient_free_simple(x);
}

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