Newsgroup: it.comp.os.linux.development
Titolo: chat server

ciao ragazzi, ho cominciato da un paio di giorno a studiare le socket e
incontro le mie prime difficoltà,.
Voglio implementare un chat server, ho utilizzato la funzione fork() per
rendere il server NON bloccante, ma nn riesco a fare in modo che il
messaggio di un client venga poi girato anke a tutti gli altri client
connessi. (sennò che chat è?:S)
ho provato creando un vettore di socket descriptor tanti quanti i client, ed
ogni volta che si riceve un messaggio lo si gira a tutti gli altri client cn
qualcosa del genere;
for (int i = 0; i < host_connessi; i++)
    write(client_descriptor[i]; stringa, strlen(stringa));
ma non funziona, la stringa viene scritta solo sul terminale dell'host da
cui è stata mandata.
Come faccio?
Grazie in anticipo a tutti

 Voglio implementare un chat server, ho utilizzato la
dimenticavo, i client usano semplicemente telnet



Non ci sono molte informazioni per capire dove sia il tuo problema.
Da quello che ho capito tu hai un server che esegue costantemente una
accept(), e quando arriva una connessione lui forka, e manda il figlio a
inoltrare i messaggi che invia la connessione che gli è stata assegnata.
Se quello che ho capito è giusto, come la aggiorni la lista dei
file_descriptor dell'iesimo figlio alla ennesima connessione con n>i ?

 Non ci sono molte informazioni per capire dove sia il tuo problema.
hai ragione, ora t allego il codice
// questo è il file chat.c
#include"chat.h"
extern void hnd_clt(socket_t*, int*, socket_t*);
int main(int argc, char *argv[])
{
 socket_t svr, clt[mxCl]; int cnt = 0; // svr = descriptor del server
socket, clt = vettore di descriptor per i client
 struct sockaddr_in svradd, cltadd;
 int err = 0, backlog = mxCl;
 socklen_t len; n_read_t chr = 0;
  static char buff[mxMess];
 char wlc[] = "Chat Server 0.0\n";
 err = bldsck(&svr, &svradd, prt); // bldsck (build socket (quanto mi
piacciono i nomi difficili:-P)): dichiarata in chat.h
 fprintf(stdout, "%s\n", wlc);
 if(err == -1)
  return EXIT_FAILURE;
 err = listen(svr, backlog);
 len = sizeof(struct sockaddr_in);
 while(1) {
  if(cnt >= mxCl)
   break;
  clt[cnt] = accept(svr, (struct sockaddr*)&cltadd, &len);
  hnd_clt(clt, &cnt, &svr);  // Ank'essa dichiarata in chat.h
  cnt++;
 }
 close(svr);
}
e questo è il file chat.h, + importante
#ifndef _CHAT_H
#define _CHAT_H
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#ifndef mxMess
#define  mxMess    250
#endif
#ifndef mxCl
#define  mxCl   5
#endif
#ifndef prt
#define  prt  4444
#endif
typedef int socket_t;
typedef unsigned int n_read_t;
static char *buffer = NULL;
int bldsck(socket_t *s, struct sockaddr_in *s1, int port) {
 int ret, opt = 1;
 static int err;
 *s = socket(AF_INET, SOCK_STREAM, 0);
 if( (err = setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
!= -1)
 {
  s1->sin_family = AF_INET;
  s1->sin_port = htons(port);
  s1->sin_addr.s_addr = INADDR_ANY;
  ret = bind(*s,(struct sockaddr*) s1, sizeof(*s1));
  return ret;
 }
 return -1;
}
void hnd_clt(socket_t *clt, int *cnt, socket_t *svr)
{
 n_read_t chr = 0;
 pid_t pid;
  char buff[mxMess];
 int i;
 if( (pid = fork()) != 0)
 {
  close(clt[*cnt]) && *--cnt;
  return;
 }
 close(*svr);
 while( (chr = read(clt[*cnt], buff, mxMess)) > 0)
 {
  // A QUESTO PUNTO NN SO CHE MI*KIA FARE!!!!!!!
 }
 close(clt[*cnt]);
 // Dovrei decrementare il contatore relativo all'array di descriptor, ma nn
ho mai usato la "fork()" perciò ci sto solo perdendo la testa
}
#endif

   // A QUESTO PUNTO NN SO CHE MI*KIA FARE!!!!!!!
[cut]
:)
dovresti cercare di trovare una risposta alla mia domanda del precedente
post, tuttavia mi sento di dirti che fork() non č la soluzione ottimale
in un chat-server.
Forse utilizzando una select() tra tutti i descriptor attivi ti faciliti
la vita e usi un solo processo.
Ps. dovresti evitare di scrivere del codice in un file.h.. che per
l'appunto č un file _header_ dove dovresti dichiarare 'soltanto'
prototipi, macro, struct.. etc.etc.
Usa piuttosto due file .c se vuoi distribuire le tue funzioni, che
compilerai con un apposito makefile oppure cosě
$ gcc -c file1.c
$ gcc -c file2.c
$ ln -o file file1.o file2.o
Mi raccomando, guardati la select(), che offre proprio il vantaggio di non
rendere bloccanti le letture da file_descriptor (accept e read).
Ciao
Matic

connessi. (sennò che chat è?:S)
ma non sarebbe più facile con select() ?
Se usi fork() penso che dovresti smazzarti con memoria condivisa e
semafori.. 

 Mi raccomando, guardati la select(), [snip]
temevo che me l'avresti detto, solo a guardarla quella funzione mi vengono i
brividi :'-( T ringrazio
Ciao Ciao

 connessi. (sennò che chat è?:S)
Ciao, io sto facendo lo stesso, se vuoi ci mettiamo in contatto e ci
aiutiamo vicendevolmente!
Attualmente sto anche traducendo quello fatto in Java
Entrambe le versione usano i thread e non la fork, li trovo + convenienti.
Al momento credo che l'unica cosa NECESSARIA da mettere su sia un contollo
sugli accessi ad alcune variabili, in pratica due mutex qua e la.
Fammi sapere se ti interessa "collaborare"!
ps io lo faccio x puro divertimento/allenamento, non so te...
Corrado
-- 
FrEe ThE kNoWlEdGe

 Come faccio?
Secondo me puoi cercare i sorgenti di ircd e cercare qualche spunto. Certo
e' un po' estrema come soluzione pero' potrebbe darti qualche buona idea.
Cmq credo che l'approccio a fork()s sia errato, visto che per comunicare fra
i vari processi figlio dovresti mettere su un accrocco incredibile.
Meglio giocare con select() come ti hanno gia' suggerito.
Keita'
--
ATI drivers on Linux, firmate la petizione!
http://www.petitiononline.com/xf123456/petition-sign.html
?



provo a spiegarti il succo, anche perché mi ricordo che anche io a suo
tempo chiesi parecchie informazioni qui :P
Dopo aver aperto una socket, ti si potrebbe porre il problema di leggere i
dati senza bloccare il tuo programma, cioè rendere la read() non
bloccante.
Per fare questo ti vengono incontro alcuni metodi, ad esempio la chiamata
fcntl( fd, F_SETFL, O_NONBLOCK );
dove fd è il descrittore della socket; oppure tramite select() puoi dire
al tuo programma aspetta tot secondi per vedere se ci sono dati da
leggere: se a questo punto la select() ti ritorna che ci sono dati puoi
effettuare la read() sicuro che non sarà bloccante, altrimenti non la
effettui.
passando al codice:
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
	int retval;
	int fd;
	fd_set rfds;
	struct timeval tv;
 
	...
	...
	FD_ZERO(&rfds);
	FD_SET(fd, &rfds);
	tv.tv_usec = 0;
	tv.tv_sec = 1;
 
	retval = select(fd+1, &rfds, NULL, NULL, &tv);
 
	if( retval > 0 )
	{
		/*ci sono dati in arrivo e la read non sarà bloccante */
	}
 
	...
}
la struct tv sarebbe il timeout per la select
tv.tv_sec e tv.tv_usec sono i 'secondi' e i 'milionesimi di secondo' che
la select deve aspettare prima di ritornare il valore 0 se non ci sono
dati da leggere nei descriptor memorizzati nel set rfds.
questo fd_set lo puoi controllare con queste macro
FD_ZERO( &rfds )  ti azzera il set
FD_SET ( file_descriptor, &rfds ) ti inserisce nel set un descrittore
FD_CLR ( file_descriptor, &rfds ) elimina il descrittore dal set
FD_ISSET( file_descriptor, &rfds ) è vero se, dopo la select,
file_descriptor è pronto per essere letto
nel caso tuo, che devi controllare piu file_descriptor, ti conviene fare
una cosa del genere
FD_ZERO( &rfds );
for( i = 0; i < numero_fd; i++ )
	FD_SET( lista_fd[i], &rfds );
select( FD_SETSIZE, &rfds, NULL, NULL, &tv );
for( i = 0; i < numero_fd; i++ )
	if( FD_ISSET( lista_fd[i], &rfds ) )
	{
		leggi i dati da lista_fd[i]
		e li riscrivi su tutti gli altri
		lista_fd[j] con j != i
	}
Un ultima cosa.. ho analizzato solamente il caso in cui la select è usata
per controllare i dati in ingresso, ma la puoi utilizzare anche per vedere
se una socket è pronta per inviare dati, passando il set di descrittori
che vuoi controllare come terzo argomento.
Puoi anche passarlo come quarto argomento e non mi ricordo che fa :P
Vabbè.. ho scritto un sacco di cose la metà delle quali saranno anche
sbagliate, spero di averti chiarito un po' le idee
PS. ovviamente controlla le manpages... man 2 select..
Ciao
Matic

 provo a spiegarti il succo [snip]
beh, nn so come ringraziare te e tutti gli altri, ora mi distruggo un po' le
meningi, vediamo che ne esce fuori:-D
Ciao Ciao