|
|
|
![]() |
|
Strumenti |
![]() |
#1 |
Senior Member
Iscritto dal: May 2006
Città: Vergiate (VA)
Messaggi: 4315
|
[C#]PROBLEMA CON SEMPLICE CHAT
Sto facendo una chat client-server facile ovvero server ascolta client si connette iniziano a parlare..è una cosa single threaded quindi in teoria una volta che il client si connette il server può anche smettere di ascoltare x altri..solo che ho 2 domande primo perchè quando avvio il server che si mette in ascolto dopo un pò si blocca e viene fuori che l'applicazione nn risponde anche se ho copiato la procedura standard da altri progetti che nn si bloccano..poi una volta che il client è connesso mi chiedevo come fare x far si che il server sia sempre pronto a ricevere i messaggi che gli arrivano..pensavo a un ciclo infinito che continua a invocare la funzione che guarda se ci sono dati disponibili ma nn mi sembra una bella cosa..cmq è fatto così:
Codice:
private void StartServer(int porta) { try { this.eventi.AppendText("Avvio server\n"); ip = IPAddress.Parse("192.168.0.2"); listener = new TcpListener(ip, porta); listener.Start(); while (true) { client = listener.AcceptTcpClient(); this.eventi.AppendText("Client connesso!!\n"); chat(); break; } private void chat() { this.stream = client.GetStream(); int n=0; while (true) { if (stream.DataAvailable) { recMessage(n); n = 1; } } } ![]()
__________________
HAF 922-RYZEN 5 3600+NOCTUA U12S-ANTEC HCG 520W-MSI B550 GAMING PLUS-2X8GB HYPERX FURY RGB-SAPPHIRE RX580 NITRO+ 4GB- ADATA XPG SX8200 Pro 512GB + -CRUCIAL M4 64GB+SAMSUNG 860EVO 256GB+840EVO 128GB+MAXTOR 320GB-CM MK750 CHERRY RED+CMSTORM SENTINEL 3+RAZER VESPULA-AOC 24G2U & SAMSUNG 223BW ![]() |
![]() |
![]() |
![]() |
#2 |
Member
Iscritto dal: Jul 2008
Messaggi: 237
|
Se posti il codice completo posso darti una mano, ad occhio e croce ti dico che sicuramente:
1. manca il catch per il try in StartServer 2. Non ha senso mettere while(true) se all'interno c'è un break eseguito incondizionatamente 3. Se è tutto su un solo thread allora è logico che si blocchi perché non processi gli eventi generati dall'utente, mentre se è su più threads devi controllare di effettuare il lock di tutto quel che è condiviso e può essere sia letto sia scritto altrimenti il tuo codice non è thread safe, devi verificare che non ci siano race conditions o deadlocks. |
![]() |
![]() |
![]() |
#3 |
Senior Member
Iscritto dal: May 2006
Città: Vergiate (VA)
Messaggi: 4315
|
il catch c'è nn lo messo perchè era inutile la parte incriminata è quella so anche che il while è inutile ma avevo provato senza allora l'ho messo così avevo detto di nn guardare il codice in se x se..cmq è a singolo thread proprio perchè c'è un solo client che si connette e col quale parla il form2 è il client
Codice:
public partial class Form1 : Form { private TcpListener listener; private IPAddress ip; NetworkStream stream; String nick = null; TcpClient client; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { Form form2 = new Form2(); form2.Show(); } private void StartServer(int porta) { try { this.eventi.AppendText("Avvio server\n"); /*ip = Dns.GetHostEntry("LocalHost").AddressList[0];*/ ip = IPAddress.Parse("192.168.0.2"); listener = new TcpListener(ip, porta); listener.Start(); while (true) { client = listener.AcceptTcpClient(); this.eventi.AppendText("Client connesso!!\n"); chat(); break; } } catch (Exception e) { MessageBox.Show("errore"); } } private void chat() { this.stream = client.GetStream(); int n=0; while (true) { if (stream.DataAvailable) { recMessage(n); n = 1; } } } private void reset() { this.eventi.AppendText("Server fermato\n"); } private void stopServer() { stream.Close(); client.Close(); listener.Stop(); } private void showInfo() { MessageBox.Show("Inserire un numero compreso tra 1024 e 65536"); } private void recMessage(int n) { if (stream.CanRead) { byte[] readBuffer = new byte[1024]; StringBuilder completeMessage = new StringBuilder(); int num = 0; // Incoming message may be larger than the buffer size. do { num = stream.Read(readBuffer, 0, readBuffer.Length); if (num == 0) return; completeMessage.AppendFormat("{0}", Encoding.ASCII.GetString(readBuffer, 0, num)); } while (stream.DataAvailable); if (String.Compare(completeMessage.ToString(), "close") == 0) stopServer(); if (n == 0) { nick = completeMessage.ToString(); lista.AppendText("Il client è " + nick); } else view.AppendText("" + nick + ": "+ completeMessage); } } private void sendMessage() { byte[] writebuffer; String mex; writebuffer = Encoding.ASCII.GetBytes(testo.Text); if (writebuffer.Length == 0) throw new Exception(); stream.Write(writebuffer, 0, writebuffer.Length); view.AppendText("\nServer: " + testo.Text); testo.Clear(); } private void l(object sender, MouseEventArgs e) { } private void start_Click(object sender, EventArgs e) { try { int port; port = Int32.Parse(porta.Text); if (port < 1024 || port > 65536) throw new Exception(); this.eventi.AppendText("Porta accettata\n"); StartServer(port); } catch (Exception ex) { MessageBox.Show("Numero di porta errato!!Inserire un numero intero compreso tra 1024 e 65536"); reset(); } } private void clear_Click(object sender, EventArgs e) { testo.Clear(); } private void finish_Click(object sender, EventArgs e) { stopServer(); } }
__________________
HAF 922-RYZEN 5 3600+NOCTUA U12S-ANTEC HCG 520W-MSI B550 GAMING PLUS-2X8GB HYPERX FURY RGB-SAPPHIRE RX580 NITRO+ 4GB- ADATA XPG SX8200 Pro 512GB + -CRUCIAL M4 64GB+SAMSUNG 860EVO 256GB+840EVO 128GB+MAXTOR 320GB-CM MK750 CHERRY RED+CMSTORM SENTINEL 3+RAZER VESPULA-AOC 24G2U & SAMSUNG 223BW ![]() |
![]() |
![]() |
![]() |
#4 |
Member
Iscritto dal: Jul 2008
Messaggi: 237
|
Considerando che mantieni la connessione aperta per tutta la durata della chat e che si tratta di TCP, l'uso che fai dello stream è terribile.
Innanzitutto devi definire se è consentito mandare due messaggi di seguito; in secondo luogo definisci una lunghezza massima del messaggio. Se ciascuno manda un solo messaggio e aspetta la risposta basta fare stream.Read cercando di leggere la lunghezza massima del messaggio e controllare che non ritorni 0. Quando ritorna 0 la connessione è stata chiusa. A quel punto liberi le risorse ed esci. Quando ritorna un valore, è il numero di bytes letti, e te li gestisci come vuoi. Tutto sta comunque in un do/while dove la condizione è che l'ultima lettura non sia 0. Se vuoi lasciare la possibilità di mandare due messaggi di seguito allora la cosa più comoda e semplice è avere due threads (puoi condividere la connessione facilmente). Inoltre ricorda che stream.DataAvailable non ti garantisce affatto che tutti i dati inviati dal client siano già stati ricevuti, quindi per come stanno le cose su una rete reale potresti facilmente troncare la ricezione prematuramente. |
![]() |
![]() |
![]() |
#5 |
Senior Member
Iscritto dal: May 2006
Città: Vergiate (VA)
Messaggi: 4315
|
uno può scrivere quanto vuole..ma senza multithread..quindi nn so come fare perchè sia sempre pronto a ricevere e contemporaneamente a mandare
__________________
HAF 922-RYZEN 5 3600+NOCTUA U12S-ANTEC HCG 520W-MSI B550 GAMING PLUS-2X8GB HYPERX FURY RGB-SAPPHIRE RX580 NITRO+ 4GB- ADATA XPG SX8200 Pro 512GB + -CRUCIAL M4 64GB+SAMSUNG 860EVO 256GB+840EVO 128GB+MAXTOR 320GB-CM MK750 CHERRY RED+CMSTORM SENTINEL 3+RAZER VESPULA-AOC 24G2U & SAMSUNG 223BW ![]() |
![]() |
![]() |
![]() |
#6 |
Member
Iscritto dal: Jul 2008
Messaggi: 237
|
Senza multithreading lo scenario che hai in mente non esiste, non in maniera decente.
|
![]() |
![]() |
![]() |
#7 |
Senior Member
Iscritto dal: May 2006
Città: Vergiate (VA)
Messaggi: 4315
|
ok allora mettiamo che ognuno manda un messaggio alla volta..cmq deve stare sempre in ascolto perchè nn può sapere quando l'altro scriverà..e deve anche capire quando il messaggio è finito..
__________________
HAF 922-RYZEN 5 3600+NOCTUA U12S-ANTEC HCG 520W-MSI B550 GAMING PLUS-2X8GB HYPERX FURY RGB-SAPPHIRE RX580 NITRO+ 4GB- ADATA XPG SX8200 Pro 512GB + -CRUCIAL M4 64GB+SAMSUNG 860EVO 256GB+840EVO 128GB+MAXTOR 320GB-CM MK750 CHERRY RED+CMSTORM SENTINEL 3+RAZER VESPULA-AOC 24G2U & SAMSUNG 223BW ![]() |
![]() |
![]() |
![]() |
#8 |
Member
Iscritto dal: Jul 2008
Messaggi: 237
|
Un messaggio alla volta, visto che si tratta di testo e non di dati, lo puoi gestire stabilendo una lunghezza massima del messaggio (arbitraria) e poi usando codice di questo genere:
Codice:
var buf = new byte[LUNGHEZZA_MASSIMA_MESSAGGIO]; var letti = stream.Read(buf, 0, LUNGHEZZA_MASSIMA_MESSAGGIO); if(letti != 0) //letti contiene la lunghezza del messaggio { //uso UTF8 arbitrariamente, puoi usare ASCII o Unicode o quel che preferisci string messaggio = Encoding.UTF8.GetString(buf.Take(letti).ToArray()); //usiamo LINQ to Objects per evitare gli \0 finali nella string. si può fare anche diversamente ma così è più conciso. //fai quel che vuoi con messaggio } else { //gestisci il caso in cui la connessione è stata chiusa } |
![]() |
![]() |
![]() |
#9 |
Senior Member
Iscritto dal: May 2006
Città: Vergiate (VA)
Messaggi: 4315
|
ok ma questo deve essere in un ciclo infinito a ripetizione cioè continua a cercare di leggere finchè nn arrivano..ma se dopo che li ha ricevuti (e cmq continua a leggere il nulla)uno schiaccia il tasto x inviare li invia cmq no
__________________
HAF 922-RYZEN 5 3600+NOCTUA U12S-ANTEC HCG 520W-MSI B550 GAMING PLUS-2X8GB HYPERX FURY RGB-SAPPHIRE RX580 NITRO+ 4GB- ADATA XPG SX8200 Pro 512GB + -CRUCIAL M4 64GB+SAMSUNG 860EVO 256GB+840EVO 128GB+MAXTOR 320GB-CM MK750 CHERRY RED+CMSTORM SENTINEL 3+RAZER VESPULA-AOC 24G2U & SAMSUNG 223BW ![]() |
![]() |
![]() |
![]() |
#10 |
Member
Iscritto dal: Jul 2008
Messaggi: 237
|
Il tuo ultimo post ha poco senso, cerca di esplicitare di più la sequenza temporale delle azioni e i soggetti. Ad ogni modo il codice di cui sopra è solo la parte di ricezione di un messaggio. Durante la sua esecuzione in un contesto a thread unico non è possibile interagire ulteriormente con il programma, ed in seguito all'esecuzione puoi integrare la parte che dovrebbe gestire l'invio e racchiudere tutto in un ciclo while con condizione che verifichi che la connessione è aperta. In questo modo hai ricezione-invio-ricezione-invio...
|
![]() |
![]() |
![]() |
#11 |
Senior Member
Iscritto dal: May 2006
Città: Vergiate (VA)
Messaggi: 4315
|
si ma l'invio dovrebbe avvenire ogni qualvolta uno schiaccia il pulsante invia nn quando lo decide il codice..è la ricezione che deve stare sempre all'erta
__________________
HAF 922-RYZEN 5 3600+NOCTUA U12S-ANTEC HCG 520W-MSI B550 GAMING PLUS-2X8GB HYPERX FURY RGB-SAPPHIRE RX580 NITRO+ 4GB- ADATA XPG SX8200 Pro 512GB + -CRUCIAL M4 64GB+SAMSUNG 860EVO 256GB+840EVO 128GB+MAXTOR 320GB-CM MK750 CHERRY RED+CMSTORM SENTINEL 3+RAZER VESPULA-AOC 24G2U & SAMSUNG 223BW ![]() |
![]() |
![]() |
![]() |
#12 |
Member
Iscritto dal: Jul 2008
Messaggi: 237
|
Infatti, nessuno ti vieta di fare così. E la ricezione in un contesto a thread singolo non può "stare sempre all'erta" in maniera affidabile. Ad ogni modo ti consiglio di studiare qualcosa a livello di protocolli e di guardare qualche implementazione reale perché sembri avere le idee abbastanza confuse sulla materia e questa non mi pare la sede adatta per chiarirle.
|
![]() |
![]() |
![]() |
#13 |
Senior Member
Iscritto dal: May 2006
Città: Vergiate (VA)
Messaggi: 4315
|
ho provato con i thread che ne dici?l'unica cosa è che se avvio il form 1 nn mi visualizza il client nn so perchè mentre prima se lo facevo senza dargli un thread apposito appariva
Codice:
namespace CHAT_FORM { public partial class Form1 : Form { private TcpListener listener; private IPAddress ip; NetworkStream stream; String nick = null; TcpClient client; Thread recThread; Thread sendThread; Thread clientThread; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { Form form2 = new Form2(); clientThread = new Thread(new ThreadStart(form2.Show)); clientThread.Start(); } private void StartServer(int porta) { try { this.eventi.AppendText("Avvio server\n"); ip = IPAddress.Parse("127.0.0.1"); listener = new TcpListener(ip, porta); listener.Start(); while (true) { client = listener.AcceptTcpClient(); this.eventi.AppendText("Client connesso!!\n"); recThread = new Thread(new ThreadStart(chat)); recThread.Start(client); break; } } catch (Exception e) { MessageBox.Show("errore"); } } private void chat() { this.stream = client.GetStream(); int n=0; while (true) { recMessage(n,stream); n = 1; } } private void reset() { this.eventi.AppendText("Server fermato\n"); } private void stopServer() { stream.Close(); client.Close(); listener.Stop(); } private void showInfo() { MessageBox.Show("Inserire un numero compreso tra 1024 e 65536"); } private void recMessage(int n,NetworkStream stream) { if (stream.CanRead) { byte[] readBuffer = new byte[1024]; StringBuilder completeMessage = new StringBuilder(); int num = 0; // Incoming message may be larger than the buffer size. do { num = stream.Read(readBuffer, 0, readBuffer.Length); completeMessage.AppendFormat("{0}", Encoding.ASCII.GetString(readBuffer, 0, num)); } while (num>0); if (String.Compare(completeMessage.ToString(), "close") == 0) stopServer(); if (n == 0) { nick = completeMessage.ToString(); lista.AppendText("Il client è " + nick); } else view.AppendText("" + nick + ": "+ completeMessage); } } private void sendMessage() { byte[] writebuffer; String mex; writebuffer = Encoding.ASCII.GetBytes(testo.Text); if (writebuffer.Length == 0) throw new Exception(); stream.Write(writebuffer, 0, writebuffer.Length); view.AppendText("\nServer: " + testo.Text); testo.Clear(); } private void l(object sender, MouseEventArgs e) { } private void start_Click(object sender, EventArgs e) { try { int port; port = Int32.Parse(porta.Text); if (port < 1024 || port > 65536) throw new Exception(); this.eventi.AppendText("Porta accettata\n"); StartServer(port); } catch (Exception ex) { MessageBox.Show("Numero di porta errato!!Inserire un numero intero compreso tra 1024 e 65536"); reset(); } } private void clear_Click(object sender, EventArgs e) { testo.Clear(); } private void finish_Click(object sender, EventArgs e) { stopServer(); } private void send_Click(object sender, EventArgs e) { sendThread = new Thread(new ThreadStart(sendMessage)); sendThread.Start(); do { if (!sendThread.IsAlive) sendThread.Abort(); } while (sendThread.IsAlive); } } }
__________________
HAF 922-RYZEN 5 3600+NOCTUA U12S-ANTEC HCG 520W-MSI B550 GAMING PLUS-2X8GB HYPERX FURY RGB-SAPPHIRE RX580 NITRO+ 4GB- ADATA XPG SX8200 Pro 512GB + -CRUCIAL M4 64GB+SAMSUNG 860EVO 256GB+840EVO 128GB+MAXTOR 320GB-CM MK750 CHERRY RED+CMSTORM SENTINEL 3+RAZER VESPULA-AOC 24G2U & SAMSUNG 223BW ![]() |
![]() |
![]() |
![]() |
#14 |
Senior Member
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
|
Per un problema del genere ti consiglierei di usare dei datagrammi UDP
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto. E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test. |
![]() |
![]() |
![]() |
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 23:48.