/* noblock_conn.c    spedisce stringa, riceve stringa traslata
   su SunOS compilare con gcc -o noblock_conn -lsocket -lnsl noblock_conn.c
   su linux               gcc -o noblock_conn noblock_conn.c                   

   eseguire ad esempio su 137.204.72.49 lanciando la seguente riga di comandi
   noblock_conn 130.136.2.7 5001 
   */

#include	<sys/types.h>   /* basic system data types */
#include	<sys/socket.h>  /* basic socket definitions */
#include	<sys/time.h>	/* timeval{} for select() */
#include	<time.h>		/* timespec{} for pselect() */
#include	<netinet/in.h>  /* sockaddr_in{} and other Internet defns */
#include	<arpa/inet.h>   /* inet(3) functions */
#include	<errno.h>
#include	<sys/select.h>  /* for select */
#include	<sys/stropts.h> /* for INFTIM */
#include	<fcntl.h>	/* for nonblocking */
#include	<netdb.h>
#include	<signal.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
# include	<strings.h>		/* for convenience */
#include	<limits.h>		/* for OPEN_MAX */
#include	<netinet/tcp.h>		/* for TCP_xxx defines */

/*
#include <stdio.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <errno.h>
*/

#define SOCKET_ERROR   ((int)-1)
#define SIZEBUF 10000


void usage(void) 
{  printf ("usage: noblock_conn REMOTE_IP_NUMBER REMOTE_PORT_NUMBER\n");
   exit(1);
}

int main(int argc, char *argv[])
{
  #define MAXSIZE 100 
  struct sockaddr_in Local, Serv;
  char string_remote_ip_address[100];
  short int remote_port_number, local_port_number;
  int socketfd, OptVal, msglen, Fromlen, ris;
  int n, nread, nwrite, len;
  char buf[MAXSIZE];
  char msg[]="012345ABCD";
  int flags;
  fd_set fdr,fdw;

  if(argc!=3) { printf ("necessari 2 parametri\n"); usage(); exit(1);  }
  else {
    strncpy(string_remote_ip_address, argv[1], 99);
    remote_port_number = atoi(argv[2]);
  }

  /* get a datagram socket */
  socketfd = socket(AF_INET, SOCK_STREAM, 0);
  if (socketfd == SOCKET_ERROR) {
    printf ("socket() failed, Err: %d \"%s\"\n", errno,strerror(errno));
    exit(1);
  }

  /* SETTO IL SOCKET COME NON BLOCCANTE  */
  if ( (flags=fcntl(socketfd,F_GETFL,0)) <0 ) {
      printf ("fcntl(F_GETFL) failed, Err: %d \"%s\"\n", errno,strerror(errno));
      exit(1); 
  }
  flags |= O_NONBLOCK; 
  if ( fcntl(socketfd,F_SETFL,flags) <0 ) {
      printf ("fcntl(F_SETFL) failed, Err: %d \"%s\"\n", errno,strerror(errno));
      exit(1);
  }
  /* ora il socket e' non bloccante */

  /* avoid EADDRINUSE error on bind() */
  OptVal = 1;
  printf ("setsockopt()\n");
  ris = setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, (char *)&OptVal, sizeof(OptVal) );
  if (ris == SOCKET_ERROR)  {
    printf ("setsockopt() SO_REUSEADDR failed, Err: %d \"%s\"\n", errno,strerror(errno));
    exit(1);
  }

  /* name the socket */
  memset ( &Local, 0, sizeof(Local) );
  Local.sin_family		=	AF_INET;
  /* indicando INADDR_ANY viene collegato il socket all'indirizzo locale IP     */
  /* dell'interaccia di rete che verrą utilizzata per inoltrare i dati          */
  Local.sin_addr.s_addr	=	htonl(INADDR_ANY);         /* wildcard */
  Local.sin_port	=	htons(0);
  printf ("bind()\n");
  ris = bind(socketfd, (struct sockaddr*) &Local, sizeof(Local));
  if (ris == SOCKET_ERROR)  {
    printf ("bind() failed, Err: %d \"%s\"\n",errno,strerror(errno));
    exit(1);
  }

  /* assign our destination address */
  memset ( &Serv, 0, sizeof(Serv) );
  Serv.sin_family	 =	AF_INET;
  Serv.sin_addr.s_addr  =	inet_addr(string_remote_ip_address);
  Serv.sin_port		 =	htons(remote_port_number);

  /* connection request, NON BLOCCANTE */
  printf ("connect()\n");
  ris = connect(socketfd, (struct sockaddr*) &Serv, sizeof(Serv));
  if (ris == SOCKET_ERROR)  {
     if(errno!=EINPROGRESS) {
        printf ("connect() failed, Err: %d \"%s\"\n",errno,strerror(errno));
        exit(1); 
     }
     printf("connect INPROGRESS\n");
     printf ("dopo connect()\n");
     fflush(stdout);

     FD_ZERO(&fdr);
     FD_SET(socketfd,&fdr);
     fdw=fdr;
     ris=select(socketfd+1,&fdr,&fdw,NULL,NULL);
     if(ris<0) {
        printf("select fallita\n");
        fflush(stdout);
        exit(1);
     } 
     if (ris!=1){
        printf("select: restituito 0, strano\n");
        fflush(stdout);
        exit(1);
     }          
     if ( (!FD_ISSET(socketfd,&fdr)) && (!FD_ISSET(socketfd,&fdw)) )  {
        printf("select: il socketfd non risponde, strano\n");
        fflush(stdout);
        exit(1);
     }

     /* CONTROLLO DELLA AVVENUTA CONNESSIONE, ripetendo la connect non bloccante
        per controllare che ti dia errore dicendo che la connessione e' avvenuta  */
     ris = connect(socketfd, (struct sockaddr*) &Serv, sizeof(Serv));
     if (ris == SOCKET_ERROR)  {
        if(errno==EISCONN)
           printf ("connect(): connessione gia' esistente, OK\n");
        else  {
           printf("connect() di controllo: connessione NON riuscita\n");
           exit(1);  
        }
     }
     printf("connessione OK\n");
     fflush(stdout);

  }  /* fine if(ris==SOCKET_ERROR */
  else {    /* ris==0, connessione riuscita subito */
     printf("connessione riuscita subito\n");
     fflush(stdout);
  }
   
  printf ("devo rendere il socket bloccante: fcntl()\n");
  if ( (flags=fcntl(socketfd,F_GETFL,0)) <0 ) {
      printf ("fcntl(F_GETFL) failed, Err: %d \"%s\"\n", errno,strerror(errno));
      exit(1); 
  }
  flags &= (~O_NONBLOCK); 
  printf ("fcntl()\n");
  if ( fcntl(socketfd,F_SETFL,flags) <0 ) {
      printf ("fcntl(F_SETFL) failed, Err: %d \"%s\"\n", errno,strerror(errno));
      exit(1);
  }
  printf ("ora il socket e' bloccante\n");
 
  /* scrittura */
  len = strlen(msg)+1;
  nwrite=0;
  printf ("write()\n");
  fflush(stdout);
  while( (n=write(socketfd, &(msg[nwrite]), len-nwrite)) >0 )
     nwrite+=n;
  if(n<0) {
    char msgerror[1024];
    sprintf(msgerror,"write() failed [err %d] ",errno);
    perror(msgerror);
    fflush(stdout);
    return(1);
  }

  /* lettura */
  nread=0;
  printf ("read()\n");
  fflush(stdout);
  while( (len>nread) && ((n=read(socketfd, &(buf[nread]), len-nread )) >0))
  {
     nread+=n;
     printf("read effettuata, risultato n=%d  len=%d nread=%d
len-nread=%d\n", n, len, nread, len-nread );
     fflush(stdout);

  }
  if(n<0) {
    char msgerror[1024];
    sprintf(msgerror,"read() failed [err %d] ",errno);
    perror(msgerror);
    fflush(stdout);
    return(1);
  }
  
  /* stampa risultato */
  printf("stringa traslata: %s\n", buf);
  fflush(stdout);

  /* chiusura */
  close(socketfd);

  return(0);
}


