Newsgroup: it.comp.lang.c++
Titolo: Creare Socket senza rete

Salve,
ho cominciato a scuola ha occuparmi di socket e della loro programmazione
sotto Linux. Volevo però riuscire a fare qualcosa anche sotto Windows, però
lo devo fare a casa.
Il problema è che non ho nessuna rete, ma so che la si può simulare tramite
l'indirizzo 127.00.01.
Qualcuno sa spiegarmi come possa fare ???

l'indirizzo 127.00.01.
Esatto, e' sufficiente che utilizzi 127.0.0.1 oppure "localhost" come
indirizzo quandro crei e/o ti connetti alla socket.
Buona fortuna.
Ciao, Angelo.
----------
Rimuovere la protezione anti-spam dall'indirizzo di posta prima di
rispondere. Grazie.
Please remove anti-spam protection from my e-mail address before answering.
Thank you.
----------









Naturalmente deve avere anche qualche server che risponde ad una
data porta di quell'indirizzo.
E' necessario avere installato il protocollo TCP-IP e mi pare che per
fare questo sia necessario installare almeno il driver per
accesso remoto.


lo devo fare a casa.
Ho un "esempietto standard" di uso di socket e winsock che ha gia`
fatto comodo a tanti altri.  E` un po' lungo, quasi 400 righe, ma
penso ci sia abbastanza interesse in giro da meritare di postarlo.
Metto i sorgenti alla fine di questo post.
Seguendo le istruzioni "-- cut nomefile" si otterranno i seguenti
sorgenti ed header:
	sockhead.h	tutti gli header che servono; precompilabile
	sus.c		implementa startup_socket
	cliemain.c	implementa main per un generico cliente (console)
	servmain.c	implementa main per un generico server (console)
	cliexam.c	un esempio di codice applicativo cliente
	servexam.c	un esempio di codice applicativo servente
Generare due file .EXE:
	SERV.EXE	con: sockhead.h, sus.c, servmain.c, servexam.c
	CLIE.EXE	con: sockhead.h, sus.c, cliemain.c, cliexam.c
come applicazioni di console; inserire anche la libreria wsock32.lib,
naturalmente (il VC++5.0 per default non la mette nell'elenco).
Su di una finestra DOS, far partire:
	SERV -V
rispondera` "server: listening on port 6131".
Da un'altra finestra DOS, far partire:
	CLIE 127.0.0.1
Digitando arbitrarie righe di testo al cliente, esso le spedira`
al servente, che le elaborera` (trasformandole in maiuscole e
racchiudendole fra segni ">" e "<") e le rispedira` come risposte;
il cliente visualizzera` le risposte ricevute.
Spero che questo esempietto risponda a molte domandi e dubbi
sull'uso di Winsock (e delle socket in generale) e permetta
di sperimentare con esse a proprio piacimento.
Ecco infine i sorgenti da usare:
-- cut cliemain.c
/*
 * Generic program for TCP/IP clients -- (C) A. Martelli, 
al...@magenta.com
 * 1992-1996 -- use freely as long as credit is given -- NO WARRANTY!
 *
 * Port number is taken from environment variable TCP_PORT, else it
 * uses as default 6131.  Function cliedef() can change default port.
 *
 * Function client() is called with the socket handle of the already
 * connected socket and must handle all interactions, while main() as
 * present in this file only handles connections and disconnections.
 *
 * Flag: -v == verbose (by default, it's quiet)
 * Args: id of host to connect to
 * Other args if any are passed on to function client().
 */
#include "sockhead.h"
#define SELECT_TIMEOUT	30	/* seconds */
#define GENERIC_DEFAULT 6131    /* default for default tcp_port */
extern int client(SOCKET sockfd, char *pname, int nargs, char *argv[]);
extern void cliedef(u_short *defaultport);
SOCKET call_host_port(const char *host, int portnum);
u_short SERV_TCP_PORT=GENERIC_DEFAULT;      /* default, modifiable */
int v=0;
int
main(int argc, char *argv[])
{
    SOCKET  	sockfd;
    const char *p;
    int rc;
    if(!startup_sockets()) return 1;
    if(argc>1 && strcmp(argv[1],"-v")==0) {v=1; --argc; ++argv;}
    if(argc<2) return 1;
    /*
     * Choice of TCP port number to use
     */
    cliedef(&SERV_TCP_PORT);
    if((p=getenv("TCP_PORT"))) {
        int specified=atoi(p);
        if(specified) SERV_TCP_PORT=specified;
    }
    /*
     * Open a TCP socket (internet, stream) to the host
     */
    sockfd = call_host_port(argv[1], SERV_TCP_PORT);
    if(!sockfd) return 1;
    rc=client(sockfd, argv[0], argc-1, argv+1);
    if(rc&&v) fprintf(stderr,"client: after connection, rc=%d\n",rc);
    soclose(sockfd);             /* close secondary socket */
    return 0;
}
/*
 * given a host name (either name or internet address), return the
 * corresponding internet address as an unsigned long, or 0 if
 * the name/address given is invalid
 */
u_long
host_name_to_address(const char *host)
{
    u_long haddr;
    if(!host) return 0;
    if(isdigit(*host)) {
        haddr = inet_addr(host);
    } else {
        struct hostent  *hstp = gethostbyname(host);
        if(!hstp) return 0;
	haddr = **(u_long**)(hstp->h_addr_list);
    }
    return haddr;
}
/*
 * given a host name (either name or internet address) and port number,
 * return a stream socket opened to that host and port, or -1 for errors
 */
SOCKET
call_host_port(const char *host, int portnum)
{
    SOCKET sock;
    u_long hadr;
    struct sockaddr_in serv_addr;
    short sportnum=portnum;
    hadr = host_name_to_address(host);
    if(!hadr) return 0;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family      = AF_INET;
    serv_addr.sin_addr.s_addr = hadr;
    serv_addr.sin_port        = htons(sportnum);
    if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
	return 0;
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
	return 0;
    } else
	return sock;
}
-- cut cliexam.c
/*
 * Generic program for TCP/IP clients -- (C) A. Martelli, 
al...@magenta.com
 * 1992-1996 -- use freely as long as credit is given -- NO WARRANTY!
 *
 * An example of the "client()" function called to implement a generic
 * TCP/IP client; its argument are the socket, already connected, and
 * the program's startup args, and it handles all interactions
 */
#include "sockhead.h"
/*
 * this gives you a chance to change the server's default port from 6131
 */
void
cliedef(u_short *defaultport)
{
    return;
}
/*
 * this sends to the server every line it gets from stdin, and
 * after each expects and prints to stdout a response from the
 * server
 */
int 
client(int sockfd, int nargs, char *argv[])
{
    char buf[300];
    int received;
    while(gets(buf)) {
	send(sockfd, buf, 1+strlen(buf), 0);
	received=recv(sockfd, buf, sizeof(buf), 0);
	buf[received]=0;
	printf("%s\n", buf);
    }
    return 0;
}
-- cut servexam.c
/*
 * Generic program for TCP/IP clients -- (C) A. Martelli, 
al...@magenta.com
 * 1992-1996 -- use freely as long as credit is given -- NO WARRANTY!
 *
 * An example of the "server()" function called to implement generic
 * TCP/IP server; its argument are the socked, already connected, and
 * the program's startup args, and it handles all interactions
 */
#include "sockhead.h"
/*
 * this gives you a chance to change the server's default port from 6131
 */
void
servdef(u_short *defaultport)
{
    return;
}
/*
 * this uppercases-and-echoes each and every message it receives,
 * and also encloses it in '>' and '<' marks.
 */
int 
server(int sockfd, int nargs, char *argv[])
{
    char buf[300];
    int received;
    while((received=recv(sockfd, &buf[1], sizeof(buf)-4, 0))>0) {
	int i;
	for(i=1;i<received && buf[i] && buf[i]!='\n';i++)
	    if(islower(buf[i])) buf[i]=toupper(buf[i]);
	    else if(isupper(buf[i])) buf[i]=tolower(buf[i]);
	buf[0] = '>';
	buf[i] = '<';
	buf[i+1] = '\n';
	buf[i+2] = 0;
	send(sockfd, buf, i+2, 0);
    }
    return 0;
}
-- cut servmain.c
/*
 * Generic program for TCP/IP servers -- (C) A. Martelli, 
al...@magenta.com
 * 1992-1996 -- use freely as long as credit is given -- NO WARRANTY!
 *
 * Port number is taken from environment variable TCP_PORT, else it
 * uses as default 6131.  Function servdef() can change default port.
 *
 * Function server() is called with the socket handle of the already
 * connected socket and must handle all interactions, while main() as
 * present in this file only handles connections and disconnections.
 *
 * Flag: -v == verbose (by default, it's quiet)
 * Args: no args are directly consumed by this code.
 * Other args if any are passed on to function server().
 * NB: this is a single-threaded (non-forking) server.
 */
#include "sockhead.h"
#define SELECT_TIMEOUT	30	/* seconds */
#define GENERIC_DEFAULT 6131    /* default for default tcp_port */
extern int server(SOCKET sockfd, int nargs, char *argv[]);
extern void servdef(u_short *defaultport);
u_short SERV_TCP_PORT=GENERIC_DEFAULT;      /* default, modifiable */
int v=0;
int
main(int argc, char *argv[])
{
    SOCKET  	sockfd, newsockfd;
    int		clilen, rc, ler;
    struct	sockaddr_in  cli_addr, serv_addr;
    fd_set	readready;
    static	struct timeval timeo={SELECT_TIMEOUT+1,0};
    const char *p;
    if(!startup_sockets()) return 1;
    if(argc>1 && strcmp(argv[1],"-v")==0) {v=1; --argc; ++argv;}
    /*
     * Choice of TCP port number to use
     */
    servdef(&SERV_TCP_PORT);
    if((p=getenv("TCP_PORT"))) {
        int specified=atoi(p);
        if(specified) SERV_TCP_PORT=specified;
    }
    /*
     * Open a TCP socked (internet, stream)
     */
    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
	ler=errno;
        fprintf(stderr,"server: can't open stream socket");
	errno=ler;
	perror("reason:");
	return 1;
    }
    /*
     * Bind local address
     */
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family      = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port        = htons(SERV_TCP_PORT);
    while( bind(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) {
	ler=errno;
        fprintf(stderr,"server: can't bind local address %d",SERV_TCP_PORT);
	errno=ler;
	perror(" reason:");
#ifndef WIN32
	sleep(2);
#endif
    }
    fprintf(stderr,"server: listening on port %d\n",SERV_TCP_PORT);
    listen(sockfd, 5);
    /*
     * Loop waiting for connection
     */
    for(;;) {
	FD_ZERO(&readready);
	FD_SET(sockfd,&readready);
        /*
         * wait for a socket with something to read, or a timeout
         */
        rc = select(FD_SETSIZE, (FDT *)&readready, (FDT *)0, (FDT *)0, &timeo);
        /*
         * analyze select()'s result
         */
        if(rc<0) {                            /* an error of some kind */
            if(errno!=EINTR) {
                perror("select");
                break;
            }
            continue;
        } else if(rc==0) {                    /* 0 sock ready -> timeout */
	    /*
	     * here, we can do some periodic cleanup if need be
	     */
            continue;
        }
        clilen = sizeof(cli_addr);
	/*
	 * here accept() "sleeps" waiting for client's connect()
	 */
        newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr,
                                     &clilen);
        if (newsockfd < 0) {                          /* some error */
	    ler = errno;
            fprintf(stderr,"server: accept error");
	    errno = ler;
	    perror("reason:");
	    return 3;
	}
	rc=server(newsockfd, argc, argv);
	if(rc&&v) fprintf(stderr,"server: serving request, rc=%d\n",rc);
        soclose(newsockfd);             /* close secondary socket */
    }
    /* 
     * note that this process never terminates, except with errors
     */
    return 0;
}
-- cut sockhead.h
/*
 * Generic program for TCP/IP clients -- (C) A. Martelli, 
al...@magenta.com
 * 1992-1996 -- use freely as long as credit is given -- NO WARRANTY!
 *
 * All needed headers (and then some:-), can be precompiled.
 */
#ifndef _SOCKHEAD_H
#define _SOCKHEAD_H
#include    <stdio.h>
#include    <ctype.h>
#include    <stdlib.h>
#if WIN32
#    include <windows.h>
#    include <winsock.h>
#    define	soclose(fd)           closesocket(fd)
#    define EINTR 0
#else /* on Unix machines */
#   include    <unistd.h>
#   include    <signal.h>
#   include    <sys/types.h>
#   include    <sys/time.h>
#   include    <sys/wait.h>
#   include    <time.h>
#   include    <errno.h>
#   include    <arpa/inet.h>
#   include    <sys/socket.h>
#   include	<netdb.h>
#   define	soclose(fd)           close(fd)
    typedef int SOCKET;
#endif
    typedef fd_set FDT;
    extern int startup_sockets();
#endif
-- cut sus.c
/*
 * Generic program for TCP/IP clients -- (C) A. Martelli, 
al...@magenta.com
 * 1992-1996 -- use freely as long as credit is given -- NO WARRANTY!
 *
 * Implementation of socket startup.
 */
#include "sockhead.h"
/*
 * returns 0 on failure to start-up the socket system
 */
int
startup_sockets()
{
#if WIN32
WORD wVersionRequested; 
WSADATA wsaData; 
int err; 
wVersionRequested = MAKEWORD(1, 1); 
 
err = WSAStartup(wVersionRequested, &wsaData); 
 
if (err != 0) 
    /* Tell the user that we couldn't find a useable */ 
    /* winsock.dll.     */ 
    return 0; 
 
/* Confirm that the Windows Sockets DLL supports 1.1.*/ 
/* Note that if the DLL supports versions greater */ 
/* than 1.1 in addition to 1.1, it will still return */ 
/* 1.1 in wVersion since that is the version we */ 
/* requested. */ 
 
if ( LOBYTE( wsaData.wVersion ) != 1 || 
        HIBYTE( wsaData.wVersion ) != 1 ) { 
    /* Tell the user that we couldn't find a useable */ 
    /* winsock.dll. */ 
    WSACleanup(); 
    return 0; 
} 
/* The Windows Sockets DLL is acceptable. Proceed. */ 
    return 1;
#else
    return 1;
#endif
}
-- end cut
Alex
--
 ____    Alex Martelli, Bologna, Italia -- email: 
al...@magenta.com
 \SM/___                                 
http://magenta.com/~alex
  \/\bi/     You never know what is enough unless you know what
     \/          is more than enough.


Ho letto e provato i tuoi esempi e sono estremamente interessanti.
Premetto che sono digiuno di socket. :-)
Ho provato a lanciare piu' client, ma finche' non se ne termina uno l'altro non ha
risposta.
Ho modificato la connessione del client, in modo che la apra solo quando riceve un invio e
la chiude subito dopo.
In questo modo tutti i client possono ricevere risposta.
Pero' questo non e' soddisfacente. Un client potrebbe impegnare il server per troppo
tempo, se questo dovesse effettuare molte operazioni al fine di tornare un risultato.
Perche', nonostante il server chiuda il collegamento, non puo' ricevere un altro client se
il client precedente non chiude la connessione?
C'e' la possibilita' di mantenere il client collegato e nello stesso tempo rispondere ad
altri client?
Grazie
Nazzareno

altri client?
Certo, ci sono tantissimi metodi.  Ad esempio, molto spesso la cosa piu`
semplice e` che il server crei un nuovo thread per ogni cliente da servire;
cosi` ogni thread si fa i fatti suoi, bastera` al massimo garantire che i
vari
thread si sequenzino e sincronizzino correttamente quando accedono a
strutture dati condivise da tutti (Win32 fornisce tanti schemi validi per la
gestione del multithreading).  In molti Unix, dove la creazione di processi
e` abbastanza leggera, si puo` spesso fare lo stesso con una fork() (un
processo separato, invece che un thread separato).
Naturalmente si puo` anche servire tanti clienti con un solo thread.  Puo`
diventare un po' complicato perche` bisogna che il thread eviti qualsiasi
operazione "bloccante".  La select() permette di "aspettare" in simultanea
su molte socket (compresa quella su cui si accettano nuove connessioni)
e andare a servire ogni volta solo quelle che si sono attivate, ma occorre
comunque che il server si faccia carico di tante precauzioni (input solo
"parziali" da una socket, ecc ecc).  WinSock offre anche tecniche, non
pero` portabili a Unix, basate su "socket asincrone", gestione di messaggi,
ecc (le MFC offrono alcune classi che facilitano questo tipo di lavoro, se
si sceglie di adottarle).  Ci sono anche *ottime* librerie gratuite in rete
per
fare questo tipo di lavoro in modo portatile (gli "ACE Wrappers", per
esempio).
Ma il multithreading resta di gran lunga la cosa piu` semplice...!
Alex