TCP Socket - C Language
Riferimenti per il file comm.h

libreria di comunicazione server / client Continua...

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/poll.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include "msg.h"
#include "errore.h"

Vai al codice sorgente di questo file.

Strutture dati

struct  comm

Definizioni

#define MAXCONN   20
#define INVALID_DESC   -1

Funzioni

commnewServerChannel (const char *service)
int acceptConnection (comm *srvComm)
int closeServerSocket (comm *srvComm)
int receiveMessage (int sc, message_t *msg)
int sendMessage (int sc, message_t *msg)
int closeConnection (int sc)
int retryConnection (const char *hostname, const char *service)

Descrizione dettagliata

libreria di comunicazione server / client

Autore:
Tranchida Giulio, No Matricola 241732
Libreria che definisce l'interfaccia di comunicazione fra client e server (canali di comunicazione realizzati con socket AF_UNIX)

This program is free software; you can redistribuite it and/or modify it under the terms of the GNU/General Pubblic License as published the Free software Foundation; either version 2 of the License, or (at your opinion) any later version.

Definizione nel file comm.h.


Documentazione delle definizioni

#define MAXCONN   20

Numero di connessioni massime accettate

Definizione alla linea 37 del file comm.h.

#define INVALID_DESC   -1

Invalid file descriptor (server socket)

Definizione alla linea 38 del file comm.h.


Documentazione delle funzioni

comm* newServerChannel ( const char *  service)

Crea le sockets di ascolto. Open passive (server) sockets for the indicated inet service & protocol. Notice in the last sentence that "sockets" is plural. During the interim transition period while everyone is switching over to IPv6, the server application has to open two sockets on which to listen for connections... one for IPv4 traffic and one for IPv6 traffic.

Parametri:
servicePointer to a character string representing the well-known port on which to listen (can be a service name or a decimal number).
Restituisce:
  • a pointer to a new comm structure, on succes
  • a NULL pointer, on failure and sets errno

Definizione alla linea 17 del file commv6.c.

                                            {
        struct addrinfo hints, *addrInfo=NULL, *ai=NULL;
        comm *srvComm = NULL;
        int err = 0, v6Only = 1, count = 0, i = 0;
        
        if (!service){
                errno = EINVAL;
                gen_err(__FILE__,__LINE__,
                        "Server: newServerChannel 'service' nullo.\n");
                return NULL;
        }

        memset(&hints, 0, sizeof(hints));
        hints.ai_family   = AF_UNSPEC;          /* Sia ipv4 che ipv6 */
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;
        hints.ai_flags    = AI_PASSIVE;

        if ((err = getaddrinfo(NULL, service, &hints, &addrInfo)) != 0){
                gen_err(__FILE__,__LINE__,
                        "Server: newServerChannel getaddrinfo: %s\n",gai_strerror(err));
                return NULL;
        }

        for (ai=addrInfo; ai; ai = ai->ai_next) count++;

        /* Inzializzazione dei parametri di ritorno. */
        if (!(srvComm = malloc(sizeof(comm)))){
                sys_err(__FILE__,__LINE__,"Server: newServerChannel malloc: ");
                freeaddrinfo(addrInfo);
                return NULL;
        }

        srvComm->skDim = count;

        if (!(srvComm->sk = calloc(count, sizeof(struct pollfd)))){
                sys_err(__FILE__,__LINE__,"Server: newServerChannel calloc: ");
                free (srvComm);
                freeaddrinfo(addrInfo);
                return NULL;
        }

        /* Inizializziamo le sockets */
        for (ai=addrInfo, i=0; ai; ai = ai->ai_next, i++){
                srvComm->sk[i].fd = INVALID_DESC;
                srvComm->sk[i].events = POLLIN;
                srvComm->sk[i].revents = 0;
        
                /* Creo il socket usando le informazioni della struttura addrinfo */
                if ((srvComm->sk[i].fd =
                                socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))<0) {
                        sys_err(__FILE__,__LINE__,
                                "Server: newServerChannel socket create: ");
                        free (srvComm->sk); free (srvComm);
                        freeaddrinfo(addrInfo);
                        return NULL;
                }

                /* Here is the code that prevents "IPv4 mapped addresses",
                 * as discussed in Section 22.1.3.1.
                 * @see http://tldp.org/HOWTO/Linux+IPv6-HOWTO/chapter-section-using-api.html
                 * If an IPv6 socket was just created, then set the IPV6_V6ONLY
                 * socket option.
                 * (IPV6_V6ONLY @see /usr/include/... ../bits/in.h) */
                if (ai->ai_family == PF_INET6){
#if defined(IPV6_V6ONLY)
                        /* Disable IPv4 mapped addresses. */
                        if((setsockopt(srvComm->sk[i].fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6Only, sizeof(v6Only))) == -1){
                                sys_err(__FILE__,__LINE__,
                                        "Server: newServerChannel socket create: ");
                                free (srvComm->sk); free (srvComm);
                                freeaddrinfo(addrInfo);
                        return NULL;
                        }
#else
                        /* If IPV6_V6ONLY is not defined, then the socket option can't
                         * be set and thus IPv4 mapped addresses can't be disabled. */
                        gen_err(__FILE__,__LINE__,
                                "Server: newServerChannel Cannot set IPV6_V6ONLY socket option. Closing IPv6 TCP socket.\n");
                        if (close(srvComm->sk->fd[i])) {
                                sys_err(__FILE__,__LINE__,
                                        "Server: newServerChannel socket close: ");
                        }
                        continue;
#endif
                }
                /* Rendiamo il socket visibile nel dominio */
                if (bind(srvComm->sk[i].fd, ai->ai_addr, ai->ai_addrlen) < 0) {
                        sys_err(__FILE__,__LINE__,
                                "Server: newServerChannel socket bind: ");
                        free (srvComm->sk); free (srvComm);
                        freeaddrinfo(addrInfo);
                        return NULL;
                }

                /* Mettiamo il socket in attesa di connessioni */
                if (listen(srvComm->sk[i].fd, MAXCONN) < 0) {
                        sys_err(__FILE__,__LINE__,
                                "Server: newServerChannel socket listen: ");
                        close(srvComm->sk[i].fd);
                        free (srvComm->sk); free (srvComm);
                        freeaddrinfo(addrInfo);
                        return NULL;
                }
        }

        if (ai){
                gen_err(__FILE__,__LINE__,
                        "Server: Some address records were not processed due to insufficient array space.\n");
        }
        freeaddrinfo(addrInfo);
        return srvComm;
}
int acceptConnection ( comm srvComm)

Accetta una connessione da parte di un client

Parametri:
srvCommStruttura delle sockets di ascolto del server
Restituisce:
  • il descrittore del canale di trasmissione con il client
  • -1 in casi di errore (sets errno)

Definizione alla linea 131 del file commv6.c.

                                    {
        struct sockaddr_in6 cli_addr; /* socket del client */
        int status=0, i=0;
        /* Salva la dimensione dell'indirizzo del client.
         * This is needed for the accept system call.
         * Valgrind suggerisce di inizializzare il valore. */
        socklen_t clilen = 0;
        int cli_sk = -1;

        if (!srvComm){
                errno = EINVAL;
                gen_err(__FILE__,__LINE__,
                        "Server: acceptConnection 'srvComm' nullo\n");
                return -1;
        }

        /* Wait indefinitely for input. */
        if ((status = poll(srvComm->sk, srvComm->skDim, -1)) == -1){
                sys_err(__FILE__,__LINE__,"Server: acceptConnection poll: ");
                return -1;
        }

        for (i=0; i<srvComm->skDim; i++){
                switch (srvComm->sk[i].revents){
                        case 0:                 /* Nessuna attivita' su questa socket; proviamo la successiva. */
                        continue;
                        case POLLIN:    /* Attivita' di rete.  Go process it. */
                                if ((cli_sk = accept(srvComm->sk[i].fd, (struct sockaddr *) &cli_addr, &clilen)) == -1){
                                        sys_err(__FILE__,__LINE__,
                                                "Server: acceptConnection accept: ");
                                        return -1;
                                }
                                return cli_sk;
                        break;
                        default:
                                gen_err(__FILE__,__LINE__,
                                        "Server: acceptConnection Invalid poll event: %d.\n",
                                        srvComm->sk[i].revents);
                                return -1;
                }
        }
        return -1;
}
int closeServerSocket ( comm srvComm)

Distrugge le sockets di ascolto del server

Parametri:
srvCommStruttura delle sockets del server da distruggere
Restituisce:
  • 0 se tutto ok,
  • -1 se errore (sets errno)

Definizione alla linea 175 del file commv6.c.

                                     {
        int i = 0;

        if (!srvComm){
                errno = EINVAL;
                sys_err(__FILE__,__LINE__,
                        "Server: closeServerSocket 'srvComm' nullo: ");
                return -1;
        }

        for (i=0; i<srvComm->skDim; i++){
                if (close(srvComm->sk[i].fd)){
                        sys_err(__FILE__,__LINE__,"Server: socket close: ");
                        return -1;
                }
        }
        free (srvComm->sk);
        free (srvComm);
        srvComm = NULL;
        return 0;
}
int receiveMessage ( int  sc,
message_t msg 
)

Legge un messaggio dal canale di trasmissione

Parametri:
scchannel di trasmissione
msgstruttura che conterra' il messagio letto (deve essere allocata all'esterno della funzione, tranne il campo buffer)
Restituisce:
  • -1 se errore (errno settato),
  • SEOF se EOF sul socket
  • lung lunghezza del buffer letto, se OK

Definizione alla linea 229 del file commv6.c.

                                          {
        int ricevuti=0, tmp_cnt=0, read_cnt=0;
        char * buffer = NULL;
#ifdef _DEBUG_MODE
        int cycle_count=0;
#endif

        /* Se la struttura del messaggio non e' allocata.. */
        if (!msg && !(msg = calloc(1, sizeof(message_t)))){
                sys_err(__FILE__,__LINE__,"Error calloc 'msg': ");
                return -1;
        }

        /* Leggo il tipo del messaggio */
        if ((ricevuti = read(sc, &(msg->type), sizeof(char))) < 1) {
                if (ricevuti==0) return SEOF;
                sys_err(__FILE__,__LINE__,"Error read message 'type': ");
                return -1;
        }
        read_cnt = ricevuti;

        /* Leggo la dimensione del messaggio */
        if ((ricevuti = read(sc, &(msg->length), sizeof(unsigned int))) < 1){
                if (ricevuti==0) return SEOF;
                sys_err(__FILE__,__LINE__,"Error read message 'length': ");
                return -1;
        }
        read_cnt += ricevuti;

        /* Leggo il messaggio reale */
        if (msg->length!=0) {
                if (!(buffer = calloc(msg->length+1, sizeof(char)))) {
                        sys_err(__FILE__,__LINE__,"Error calloc 'buffer': ");
                        return -1;
                }
                if (!(msg->buffer = calloc(msg->length+1, sizeof(char)))) {
                        sys_err(__FILE__,__LINE__,"Error calloc 'msg->buffer': ");
                        free (buffer);
                        return -1;
                }
                while(tmp_cnt < msg->length) {
                        if ((ricevuti = read(sc, buffer, msg->length-tmp_cnt)) < 1){
                                free (buffer); free (msg->buffer); msg->buffer = NULL;
                                if (ricevuti==0) return SEOF;
                                sys_err(__FILE__,__LINE__,"Error read message 'buffer': ");
                                return -1;
                        }
                        tmp_cnt += ricevuti;
                        /* Pelagatti docet: usare sempre le fuonzioni con la 'n' */
                        if (ricevuti > 0) strncat (msg->buffer, buffer, ricevuti);
#ifdef _DEBUG_MODE
                        fprintf(stderr,
                                "Esecuzioni della read = %i, ric = %i, ricTot = %i\n",
                                        ++cycle_count, ricevuti, tmp_cnt);
#endif
                }
                free (buffer);
                read_cnt += tmp_cnt;
        }
        return read_cnt;
}
int sendMessage ( int  sc,
message_t msg 
)

Scrive un messaggio sul canale di trasmissione

Parametri:
scchannel di trasmissione
msgstruttura che contiene il messaggio da scrivere
Restituisce:
  • -1 se errore (sets errno)
  • n il numero di caratteri inviati (se OK)

Definizione alla linea 197 del file commv6.c.

                                       {
        int char_sent=0, sent=0;

        if (!msg){
                errno = EINVAL;
                sys_err(__FILE__,__LINE__,"Error send message 'msg' nullo: ");
                return -1;
        }

        if ((sent = write(sc, &(msg->type), sizeof(char))) == -1) {
                sys_err(__FILE__,__LINE__,"Error send message 'type': ");
                return -1;
        }
        char_sent = sent;

        if ((sent = write(sc, &(msg->length), sizeof(unsigned int))) == -1) {
                sys_err(__FILE__,__LINE__,"Error send message 'length': ");
                return -1;
        }
        char_sent += sent;

        if (msg->length!=0) {
                if ((sent = write(sc, msg->buffer, msg->length)) == -1) {
                        sys_err(__FILE__,__LINE__,"Error send message 'buffer': ");
                        return -1;
                }
                char_sent += sent;
        }

        return char_sent;
}
int closeConnection ( int  sc)

Chiude la socket del client

Parametri:
scil descrittore del channel da chiudere
Restituisce:
  • 0 se tutto ok
  • -1 se errore (sets errno)

Definizione alla linea 291 del file commv6.c.

                           {
        if (close(sc)){
                sys_err(__FILE__,__LINE__,"client: socket close: ");
                return -1;
        }
        return 0;
}
int retryConnection ( const char *  hostname,
const char *  service 
)

Crea una socket di trasmissione verso il server, tentando la connessione per un massimo di due minuti se MAXSLEEP e' 128

Parametri:
hostnameIndirizzo del server
serviceNome del servizio (noto) o numero di porta del server
Restituisce:
  • c (c>0) la socket di comunicazione del client
  • -1 in casi di errore (sets errno)

Definizione alla linea 299 del file commv6.c.

                                                              {
        int sk = -1, err = 0;
        struct addrinfo addrinfo, *res = NULL, *r = NULL;
        
        if (!hostname){
                errno = EINVAL;
                gen_err(__FILE__,__LINE__,
                        "Server: retryConnection 'hostname' nullo.\n");
                return -1;
        }
        if (!portno){
                errno = EINVAL;
                gen_err(__FILE__,__LINE__,
                        "Server: retryConnection 'portno' nullo.\n");
                return -1;
        }
        
        memset(&addrinfo, 0, sizeof(addrinfo));
        addrinfo.ai_flags = 0;
        addrinfo.ai_family = AF_UNSPEC;                 /* sia ipv4 che ipv6 */
        addrinfo.ai_socktype = SOCK_STREAM;
        addrinfo.ai_protocol = IPPROTO_TCP;             /* tcp */
        
        if ((err = getaddrinfo(hostname, portno, &addrinfo, &res)) != 0) {
                gen_err(__FILE__,__LINE__,
                        "Client: getaddrinfo: %s\n",gai_strerror(err));
                return -1;
        }

        /* getaddrinfo() returns a list of address structures.
         * Try each address until we successfully connect(2).
         * If socket(2) (or connect(2)) fails, we (close the socket and)
         * try the next address. */
        for (r = res; r; r = r->ai_next) {
                if ((sk = socket(r->ai_family, r->ai_socktype, r->ai_protocol)) < 0){
                        sys_err(__FILE__,__LINE__,"Client: socket create: ");
                        continue;
                }               
                if (connect(sk, r->ai_addr, r->ai_addrlen) == 0) {
                        freeaddrinfo(res);
                        return sk;
                }
                sys_err(__FILE__,__LINE__-3,"Client: failed attempt to connected: ");
                close(sk);
        }       
        sys_err(__FILE__,__LINE__-6,"Client: failed to connect: ");
        freeaddrinfo(res);
        return -1;
}