Dove posso prendere quei file header?
se non li avete sul vostro sistema probabilmente non vi servono. Controllate il manuale per la specifica piattaforma.Se state lavorando su Windows, avrete bisogno solo di #include winsock.h.
Cosa faccio quando bind() riporta un errore "Address already in use"?
Dovete usare setsockopt() con l'opzione SO_REUSEADDR sul socket in ascolto. Controllate la sezione su bind() e la sezione su select() per un esempio.
Come faccio ad ottenere una lista di socke aperti nel sistema?
Usate il comando netstat. verificate la pagina man per tutti i dettagli, ma dovreste ottenere qualcosa di buono scrivendo:
$ netstat |
il solo problema è determinare quale socket è associato con quale programma. :-)
Come faccio a vedere la tabella di routing ?
eseguite il comando route (in /sbin sulla maggior parte dei Linux) o il comando netstat -r.
come posso lanciare i programmi client e server se ho solo un computer? non ho bisogno di una rete per scrievere programmi di rete?
Fortunatamente per voi, praticamente tutte le macchine implementano un dispositivo di loopback di rete che sta nel kernel e simula una scheda di rete. (questa è l'interfaccia che corrisponde a "lo" nella tabella di routing.)
Supponiamo che siate loggati iun una macchina di nome "goat". Eseguite il client in una finestra ed il server in un altra. O avviate il server in background ("server ") ed eseguite il client nella stessa finestra. La cosa in più dell'interfaccia di loopback è che potete usarla con client goat o client localhost (poichè "localhost" probabilmente è definita in /etc/hosts ) ed avrete il vostro client che comunica con il server senza una rete!
In breve, nessun cambiamento è necessario al codice per fare in modo che funzioni su una macchine non connessa in rete! Hurrah!
Come faccio a dire se il lato remoto ha chiuso la connessione?
Potete dirlo perchè recv() restituirà 0.
Come implemento una utilità "ping"? Cos'è l'ICMP? Dove posso trovare altro materiale sui socket a basso livello e SOCK_RAW?
Tutte le vostre domande sui socket a basso livello troveranno risposta nei libri di W. Richard Stevens' UNIX Network Programming . Vedere la sezione libri di questa guida.
Come faccio a lavorare con Windows?
Primo, cancellate Windows e installate Linux o BSD. };-). No, seriamente, date un'occhiata alla sezione sul lavorare con Windows nell'introduzione.
Come funziona con Solaris/SunOS? Continuo a ottenere degli errori dal linker quando cerco di compilare!
gli errori del linker accadono perchè le macchine Sun non compilano automaticamente le librerie dei socket. Date un'occhiata alla sezione dulla compilazione su Solaris/SunOS nell'introduzione per un esempio di come farlo.
Perchè select() continua a fallire su un segnale?
I Segnali tendono a far ritornare le chiamate di sistema bloccate con -1 e con errno impostato a EINTR. Quando registrate un gestore del segnale con sigaction(), poteteimpostare la flag SA_RESTART, che ci si aspetta riavvii la chiamata di sistema dopo che è stata interrotta.
Naturalmente, questo non funziona sempre.
La mia soluzione preferita a questo implica un goto . Sapete bene che questo irriterà i professori all'infinito, quindi andiamo subito a vedere come si fa!
select_restart: if ((err = select(fdmax+1, &readfds, NULL, NULL, NULL)) == -1) { if (errno == EINTR) { // alcuni segnali ci interrompono e basta, quindi riavviamo goto select_restart; } // gestiamo qui il vero errore: perror("select"); } |
Sicuro, non avete bisogno di usare goto in questo caso; potete usare altre strutture per controllare il tutto. Ma io ritengo che goto sia alla fine più pulito.
Come posso implementare un timeout su una chiamata a recv()?
Usate select()! Essavi permette di specificare un parametro timeout per il descrittore di socket dal quale state tentando di leggere. Oppure , potreste mettere l'intera funzionalità in una singola funzione, così:
#include <unistd.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> int recvtimeout(int s, char *buf, int len, int timeout) { fd_set fds; int n; struct timeval tv; // imposta l'insieme di descrittori FD_ZERO(&fds); FD_SET(s, &fds); // imposta la struct timeval per il timeout tv.tv_sec = timeout; tv.tv_usec = 0; // attende fino al timeout o finchè non vengono ricevuti dati n = select(s+1, &fds, NULL, NULL, &tv); if (n == 0) return -2; // timeout! if (n == -1) return -1; // errore // i dati dovrebbero essere qui, dunque usate una normale recv() return recv(s, buf, len, 0); } // chiamate d'esempio a recvtimeout(): . . n = recvtimeout(s, buf, sizeof(buf), 10); // timeout di 10 secondi if (n == -1) { // è capitato un errore perror("recvtimeout"); } else if (n == -2) { // è scaduto il timeout } else { // abbiamo dei dati nel buffer } . . |
Notate che recvtimeout() restituisce -2 in caso di timeout. Perchè non restituire 0? Bene, se ricordate, un valore di ritorno di 0 su una chiamata recv() significa che il lato remoto ha chiuso la connessione. Dunque quel valore di ritorno è già usato, e -1 significa "errore", dunque ho scelto -2 come indicatore di timeout.
come posso cifrare o comprimere i dati prima di inviarli attraverso il socket?
Un modo semplice per cifrarli è usare SSL (secure sockets layer), ma questo va aldilà dello scopo di questa guida.
Ma assumendo che vogliate applicarvi nell'implementare il vostro sistema di compressione o crittazione, è semplicemente questione di pensare che vostri dati debbano passare attraverso una sequenza di passi tra i due estremi. Ogni passo cambia i dato in qualche maniera.
il server legge i dati da un file (o da qualsiasi altra cosa)
il server cifra i dati (voi dovrete aggiungere questa parte)
il server invia con send() i dat cifrati
Ora, la seconda parte:
il client riceve con recv()i dati cifrati
il client decifra i dati (voi dovete aggiungere questa parte)
il client scrive i dati su file (o da qualsiasi altra parte)
Potete effettuare anche una compressione nel punto dove effettuate la de/cifratura, qui sopra. O potreste effettuarle entrambe! Ricordate solo di comprimere prima di cifrare. :)
Finchè il client riesce a fare il contrario di quello che ha fatto il server, i dat alla fine saranno corretti, non importano i passi intermedi.
Dunque tutto ciò di cui avete bisogno per usare il mio codice è trovare il punto dove i dati vengono letti e quello nel quale vengono inviati (usando send()) sulla rete, ed infilarci in mezzo del codice che effettui la crittazione.
Cos'è questo "PF_INET" che continuo a trovare? E' correlato a AF_INET?
Si, lo è. Andate a vedere la sezione sulla funzione socket() per i dettagli.
Come posso scrivere un server che accetti comandi di shell da un client e li esegua?
Per semplicità, poniamo che il client effettui una connect(), una send(), e una close() (insomma, non ci sono ulteriori chiamate di sistema se il client non si connette ancora.)
Il processo client segue questi passi:
connect() verso il server
send("/sbin/ls /tmp/client.out")
usa close() e termina la connessione
Nel frattempo, il server sta gestendo i dati e li sta eseguendo così:
accept() e inizia la connessione verso il client
legge con recv(str) la riga di comando
usa close() per chiudere la connessione
usa system(str) per eseguire il comando
Attenzione!avere un server che esegua quello che il client gli dice è come fornire un accesso remoto a una shell, la gente potrebbe usare il vostro account liberamente connettendosi al server. Ad esempio, nell'esempio precedente, cosa accadrebbbe se il client inviasse un "rm -rf ~"? Cancellerebbe tutto quel che c'è nel vostro account, ecco cosa!
dunque sarete saggi, ed impedirete al client di usare qualsiasi comando,eccetto alcune utility che sapete essere sicure, come il programma foobar :
if (!strcmp(str, "foobar")) { sprintf(sysstr, "%s > /tmp/server.out", str); system(sysstr); } |
Ma non sarete ancora sicuri, sfortunatamente: cosa accadrebbe se il client inserisse "foobar; rm -rf ~"? La cosa più sicura è scrivere una piccola routine che mettà un carattere d'escape ("\") davanti a tutti quelli non alfanumerici (inclusi gli spazi, se necessario) negli argomenti per il comando.
Come potete vedere, la sicurezza è un problema grosso quando il server comincia ad eseguire le cose che il client gli senda.
Io sto inviando un mare di dati, ma quando li ricevo con recv(), ho solo 536 byte o 1460 byte alla volta. Ma se lo eseguo in locale, ricevo tutti i dati insieme. che sta succedendo?
Vi state scontrando con l'MTU--la dimensione massima che il mezzo fisico accetta. Sulla macchina locale, state usando il dispositivo di loopback che può gestire 8K senza problemi, ma su ethernet, che può gestire solo 1500 con un header, avete quel limite. Su di un modem , con una MTU da 576 (ancora, con l'header ), vi scontarte con un limite ancora inferiore.
Dovete assicurarvi che tutti i dati siano inviati, anzitutto. (Vedere l'implementazione di sendall() per i dettagli.) Una volta che vi sarete assicurati di questo, avrete bisogno di chiamare recv() in un loop finchè non avrete letto tutti i dati.
Leggete la sezione Fgli dell'incapsulazione Dati per i dettagli sul ricevimento completo dei pacchetti tramite chiamate multiple a recv().
Sono su una macchina Windows e non ho la chiamat di sistema fork() e nessuna struct sigaction. Come faccio?
Se sono da qualche parte, saranno nelle librerie POSIX che potrebbero essere distribuite nel vostro compilatore. Poichè non ho una macchina Windows, non so darvi una risposta, ma mi sembra di ricordare che la Microsoft ha un livello di compatibilità POSIX ed è li che dovrebbe essere la funzione fork(). (e forse anche sigaction.)
Cercate nell'aiuto in linea che vi è stato fornito con il VC++ "fork" o "POSIX" e vedete se trovate degli indizi.
Se non c'è modo,abbandonate fork()/sigaction e rimpiazzatele con gli equivalenti Win32: CreateProcess(). Io non so usare CreateProcess()--prende in input un fantastiliardo di argomenti, ma dovrebbe essere affrontata nei documenti che sono stati forniti con il VC++.
Come invio dati in maniera sicura con il TCP/IP usando la crittografia?
Andate a vedere il progetto OpenSSL.
Sono dietro un firewall--come permetto alla gente da fuori di conoscere il mio indirizzo IP in maniera che si colleghino alla mia macchina?
Sfortunatamente, lo scopo di un firewall è impedire alla gente da fuori di collegarsi alle amcchine all'interno, quindi permettere questo viene generalmente consierato un problema di sicurezza.
Ciò non vuol cire che tutto sia perduto. Ad esempio, potete ancora usare connect() attraverso il firewall se viene effettuato un qualche tipo di masquerading o NAT o qualcosa del genere. Semplicemente, pensate i vostri programmi in maniera che siate sempre voi a iniziare la connessione, e tutto andrà bene.
Se ciò non vi soddisfa, potete chiedereai vostri amministratori di sistema di aprire un buco nel firewall cosicchè la gente possa collegarsi a voi. Il firewall può inoltrare i pacchetti tramite il suo software di NAT, o attraverso un proxy o una cosa del genere.
Stateattenti, un buco in un firewall non è una cosa da prendere alla leggera. Dovete assicurarvi di non dare a della gentaccia accesso alla rete interna; Se siete dei principianti, è molto più difficile scrivere software sicuro di quanto immaginiate
Non fate venire il vostro sysadmin a lamentarsi con me. ;-)