Problem z Klient-Serwer. Wątki. dostęp do textBox1 z innej klasy.

0

Hej, mam dosyć duży jak dla mnie problem. Chcę stworzyć komunikację klient-serwer-klient, do tego użyłem formatki + dodatkowy wątek do uruchomienia procedury połączenia klientów + nowa klasa, która tworzy wątek dla każdego podłączonego klienta. Połączenie działa, aczkolwiek chciałbym, aby w formatce, w formie głównej w textBox1 wyświetlało mi dane połączonego klienta. I tutaj jest problem, bo te dane są w innej klasie w innym wątku.

Poradziłem sobie z wyświetlaniem informacji w textBoxie z innego wątku w tej samej klasie

Invoke(new Action(() => { textBox1.Text = " >> " + "Client No:" + Convert.ToString(counter) + " started!+\n"; }));

aczkolwiek nie działa to samo w innej klasie.
Teraz trochę mojego kodu:
kod z buttonu:

 private void button1_Click(object sender, EventArgs e)
        {
            Thread thr = new Thread(startServer);
            thr.Start();
        }
 public void startServer()
        {
                 IPAddress ipAd = IPAddress.Parse("127.0.0.1");

                TcpListener myList = new TcpListener(ipAd, 8001);
                TcpClient clientSocket = default(TcpClient);
                int counter = 0;

                myList.Start();
                counter = 0;
                while (true)
                {
                    counter += 1;
                    clientSocket = myList.AcceptTcpClient();
                    Invoke(new Action(() => { textBox1.Text = " >> " + "Client No:" + Convert.ToString(counter) + " started!+\n"; }));
                    Thread.Sleep(400);
                    handleClinet client = new handleClinet();
                    client.startClient(clientSocket, Convert.ToString(counter));
                }

                clientSocket.Close();
                myList.Stop();
                Console.WriteLine(" >> " + "exit");
                Console.ReadLine();     
        }
        public class handleClinet
        {

            TcpClient clientSocket;
            string clNo;

            public void startClient(TcpClient inClientSocket, string clineNo)
            {
                this.clientSocket = inClientSocket;
                this.clNo = clineNo;
                Thread ctThread = new Thread(doChat);
                ctThread.Start();
                
            }
            private void doChat()
            {
               
                int requestCount = 0;
                byte[] bytesFrom = new byte[10025];
                string dataFromClient = null;
                Byte[] sendBytes = null;
                string serverResponse = null;
                string rCount = null;
                requestCount = 0;
                while ((true))
                {
                    try
                    {
                        requestCount = requestCount + 1;
                        NetworkStream networkStream = clientSocket.GetStream();
                        networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize);
                        dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
                        dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
                      //  Console.WriteLine(" >> " + "From client-" + clNo + dataFromClient); // Nie chcę wyświetlania w konsoli, tylko w TextBox1
                        Invoke(new Action(() => { textBox1.Text = " >> " + "From client-" + clNo + dataFromClient; })); // TUTAJ TAKIE COŚ NIE DZIAŁA :/
                        rCount = Convert.ToString(requestCount);
                        serverResponse = "Server to clinet(" + clNo + ") " + rCount;
                        sendBytes = Encoding.ASCII.GetBytes(serverResponse);
                        networkStream.Write(sendBytes, 0, sendBytes.Length);
                        networkStream.Flush();
                   //     Console.WriteLine(" >> " + serverResponse);


                    }
                    catch (Exception ex)
                    {
                      //  Console.WriteLine(" >> " + ex.ToString());
                      //  instance.Invoke(new Action(() => { textBox1.Text = " >> " + ex.ToString();}));

                    }
                }
            }
        }

Szukałem na forach, ale nigdzie nie mogłem znaleźć rozwiązania, które by rozwiązały mój problem. Ewentualnie było tak napisane i zagmatwane, że nie byłem w stanie tego rozwikłać.
Dopiero co się uczę C# i .NET, więc proszę o wyrozumiałość.

1

Musisz mieć dostęp do obiektu który posiada textbox1, który mógłbyś uczynić publicznym, ale nie jest to dobrą praktyką. Lepiej mieć metodę która zmieni tekst za Ciebie.

A więc tak powinno to wyglądać:

public partial class Server : Form // Załóżmy, że tak nazywa się klasa serwera z textboxem (główna formatka)
{
	public void startServer()
	{
		...
		handleClinet client = new handleClinet(this); // Przekazujemy obiekt to klasy client.
		...
	}
	public void ChangeText(string text)
	{
		Invoke(new Action(() => { textBox1.Text = text; })); // Zmieniamy tekst
	}
}


public class handleClinet
{
	private readonly Server server; // readonly aby pole nie zostało przypadkowo zmienione
	public handleClinet(Server server) // Konstruktor
	{
		this.server = server; // Inicjalizujemy server
	}
	private void doChat()
	{
		...
		server.ChangeText("Hello World"); // Zmieniamy tekst w textbox'ie.
		...
	}
}
0

Dzięki wielkie :) teraz pojawił się kolejny problem :/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.Sockets;

namespace Sieci_22._05._2017
{

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread thr = new Thread(startServer);
            thr.Start();
        }

        public void textBox1_TextChanged(object sender, EventArgs e)
        {
            
        }
        public void ChangeText(string text)
        {
            Invoke(new Action(() => { textBox1.Text = text; })); // Zmieniamy tekst
        }
        public void startServer()
        { 
                 IPAddress ipAd = IPAddress.Parse("127.0.0.1");
             
                TcpListener myList = new TcpListener(ipAd, 8001);
                TcpClient clientSocket = default(TcpClient);
                int counter = 0;

                myList.Start();
                counter = 0;
                while (true)
                {
                    counter += 1;
                    clientSocket = myList.AcceptTcpClient();
                    Invoke(new Action(() => { textBox1.Text = " >> " + "Client No:" + Convert.ToString(counter) + " started!+\n"; }));
                    Thread.Sleep(400);
                    handleClinet client = new handleClinet(this);
                    client.startClient(clientSocket, Convert.ToString(counter));
                }
                clientSocket.Close();
                myList.Stop();
                Console.WriteLine(" >> " + "exit");
                Console.ReadLine();
        }
        public class handleClinet
        {
            private readonly Form1 server; // readonly aby pole nie zostało przypadkowo zmienione
            public handleClinet(Form1 server) // Konstruktor
            {
                this.server = server; // Inicjalizujemy server
            }

            TcpClient clientSocket;
            string clNo;

            public void startClient(TcpClient inClientSocket, string clineNo)
            {
                this.clientSocket = inClientSocket;
                this.clNo = clineNo;
                Thread ctThread = new Thread(doChat);
                ctThread.Start();
                
            }
            private void doChat()
            {
               
                int requestCount = 0;
                byte[] bytesFrom = new byte[10025];
                string dataFromClient = null;
                Byte[] sendBytes = null;
                string serverResponse = null;
                string rCount = null;
                requestCount = 0;
                while ((true))
                {
                    try
                    {
                        requestCount = requestCount + 1;
                        NetworkStream networkStream = clientSocket.GetStream();
                        networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize);
                        dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
                        dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
                        server.ChangeText(" >> " + "From client-" + clNo + dataFromClient);
                        Thread.Sleep(5000);
                        rCount = Convert.ToString(requestCount);
                        serverResponse = "Server to clinet(" + clNo + ") " + rCount;
                        sendBytes = Encoding.ASCII.GetBytes(serverResponse);
                        networkStream.Write(sendBytes, 0, sendBytes.Length);
                        networkStream.Flush();
                        server.ChangeText(" >> " + serverResponse);
                        Thread.Sleep(5000);
                    }
                    catch (Exception ex)
                    {
                        server.ChangeText(" >> " + ex.ToString());
                        Thread.Sleep(5000);
                    }
                }
            }
        }
    }
}

To jest mój cały kod serwera. A niżej kod klienta:

                      using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.IO;

namespace sieci_22._05._2017_klient
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                TcpClient tcpclnt = new TcpClient();
                Console.WriteLine("Connecting.....");

                tcpclnt.Connect("127.0.0.1",8001);
                // use the ipaddress as in the server program

                Console.WriteLine("Connected");
                Console.Write("Enter the string to be transmitted : ");

                String str = textBox1.Text;
                Stream stm = tcpclnt.GetStream();

                ASCIIEncoding asen = new ASCIIEncoding();
                byte[] ba = asen.GetBytes(str);
                Console.WriteLine("Transmitting.....");

                stm.Write(ba, 0, ba.Length);

                byte[] bb = new byte[100];
                int k = stm.Read(bb, 0, 100);

                for (int i = 0; i < k; i++)
                    Console.Write(Convert.ToChar(bb[i]));

             //   tcpclnt.Close();
            }

            catch (Exception q)
            {
                Console.WriteLine("Error..... " + q.StackTrace);
            }
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {

        }
    }
}

Ogólnie chcę, aby do serwera mogło się połączyć kilka klientów(przy każdym podłączeniu klienta, serwer wysyła do wszystkich podłączonych informację kto jest podłączony do niego. Aczkolwiek nie chcę, aby strumień się zamykał zaraz po otrzymaniu jakiejkolwiek wiadomości. Tak, aby później np. klient nr1 wysyła wiadomość do klienta nr3 przechodzącą przez serwer.

Troszkę pewnie zagmatwałem, ale czy ktoś jest mi w stanie doradzić ? Bez tego ani rusz dalej :/

1

Tu większość kodu jest do wywalenia, bo jest niepotrzebnie skomplikowana.

Klient

Zrobiłbym to tak:
Dodał dwa przyciski (czyli 3 w całej formatce). Te przyciski to: Connect, Send i Disconnect.
Idea jest prosta: Connect łączy się z serwerem. Wpisujemy coś w TextBox'a i klikamy Send, jak już chcemy się odłączyć klikamy Disconnect.

A teraz kod:

public partial class Form1 : Form
{
    TcpClient client;
    NetworkStream stream;
    StreamWriter sw;
    StreamReader sr;

    public Form1()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, EventArgs e)
    {
        ConnectToServer();        
    }

    private void button2_Click(object sender, EventArgs e)
    {
        SendData();
    }

    private void button3_Click(object sender, EventArgs e)
    {
        Disconnect();
    }
    private void ConnectToServer()
    {
        client = new TcpClient();
        client.Connect("127.0.0.1", 8001);
        stream = new NetworkStream(client.Client);
        sw = new StreamWriter(stream);
        sw.AutoFlush = true;
        sr = new StreamReader(stream);
    }
    private void SendData()
    {
        sw.WriteLine(textBox1.Text);
        textBox1.Text = sr.ReadLine();
    }
    private void Disconnect()
    {
        client.Close();
    }
}

Prawda, że przyjemniejsze dla oka?
I dodatkowo nie musimy robić żadnej pętli czekającej na zmianę TextBoxa.

Serwer

Tu to samo co wyżej, tylko, że musimy przekazać Socket w metodzie startClient.
Czyli:

public class handleClinet
{
    private readonly Form1 server; // readonly aby pole nie zostało przypadkowo zmienione
    public handleClinet(Form1 server) // Konstruktor
    {
        this.server = server; // Inicjalizujemy server
    }

    Socket socket;
    string clNo;

    public void startClient(Socket inClientSocket, string clineNo)
    {
        socket = inClientSocket;
        clNo = clineNo;
        Thread ctThread = new Thread(doChat);
        ctThread.Start();
    }
    private void doChat()
    {
        while(socket.Connected)
        {
            NetworkStream ns = new NetworkStream(socket);
            StreamReader sr = new StreamReader(ns);
            StreamWriter sw = new StreamWriter(ns);
            sw.AutoFlush = true;
            try
            {
                server.ChangeText(sr.ReadLine());
                sw.WriteLine(""); // Tu odpowiedź od serwera
            }
            catch(IOException)
            {
		// Tu możesz się pokusić o obsługę wyjątków
            }
        }
    }
}

Teraz zadania dla Ciebie:

  1. Po zakończeniu serwera jeden wątek cały czas chodzi i trzeba to naprawić ;)
  2. Brutalne wyjście (bez klikania Disconnect, tylko wyjście poprzez zamknięcie formatki) powinno wywoływać metodę Disconnect
0

Nie rób tak. Mieszasz logikę z GUI. A stąd już tylko mały krok do problemów.
Jeśli chcesz, żeby wątek serwera wyświetlił Ci info na formie, to powinieneś zrobić to inaczej.

Serwer to ma być Serwer - osobna klasa z logiką, a nie z formą, czy nie daj Boże - osobna forma :|

To można zrobić na kilka sposobów. Albo za pomocą delegatów, albo za pomocą interfejsów. Pokazuję Ci przykład na interfejsach. Zadeklaruj sobie np. coś takiego:

public interface IServerListener
{
  void ClientHasConnected(Client c);
}

To jest przykład. Ta metoda zostanie wywołana w momencie podłączenia klienta do serwera. Klasa Client to jakaś hipotetyczna klasa z informacjami o kliencie.

I teraz możesz taki interfejs obsłużyć z poziomu formy:

public partial class MojaSuperForma: Form, IServerListener
{
  public void IServerListener.ClientHasConnected(Client c);
  {

  }
}

I co teraz? Teraz musisz zadbać o to, żeby na formie wykonać odpowiednie działanie. Tutaj to będzie np. wyświetlenie jego nazwy na labelu. Ale zrób sobie do tego osobną metodę:

void ShowClientNameOnLabel(string name)
{
    nameLabel.Text = name;
}

Ale jak już zauważyłeś, metoda ClientHasConnected może być wywołana z innego wątku. I w jej ciele musisz o to zadbać:

  public void IServerListener.ClientHasConnected(Client c);
  {
      if(InvokeRequired)
          BeginInvoke(new Action(() => ShowClientNameOnLabel(c.Name)));
      else
          ShowClientNameOnLabel(c.Name);
  }

Tutaj przydałaby się jeszcze synchronizacja - używanie blokad: lock, więc zróbmy coś takiego:

void ShowClientNameOnLabel(string name)
{
    lock(locker)
    {
        nameLabel.Text = name;
    }
}

locker to zwykły obiekt, który jest tworzony tak:

Object locker = new Object();

Dzięki lock masz pewność, że dwa wątki nigdy nie będą zapisywały w tym samym czasie do jednego labela.

Teraz, gdy tworzysz serwer, musisz przekazać mu obiekt, który implementuje określony interfejs. Klasa serwer powinna mieć coś takiego:

public class Server
{
    IServerListener listener;

    public Server(IServerListener listener)
    {
        this.listener = listener;
    }
}

A w konstruktorze przekazujesz obiekt swojej formy. Ten kod można jeszcze ulepszać. Ale mniej więcej tak to powinno wyglądać.

Możesz też zamiast interfejsów użyć delegatów. Zależy od Twoich potrzeb.

0
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.Sockets;


namespace Sieci_22._05._2017
{

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread thr = new Thread(startServer);
            thr.Start();
        }

        public void textBox1_TextChanged(object sender, EventArgs e)
        {
            
        }
        public void ChangeText(string text)
        {
            Invoke(new Action(() => { textBox1.Text = text; })); // Zmieniamy tekst
        }
        public void startServer()
        { 
                 IPAddress ipAd = IPAddress.Parse("127.0.0.1");
             
                TcpListener myList = new TcpListener(ipAd, 8001);
                TcpClient clientSocket = default(TcpClient);
                int counter = 0;

                myList.Start();
                counter = 0;
                while (true)
                {
                    counter += 1;
                    clientSocket = myList.AcceptTcpClient();
                    Invoke(new Action(() => { textBox1.Text = " >> " + "Client No:" + Convert.ToString(counter) + " started!+\n"; }));
                    Thread.Sleep(400);
                    handleClinet client = new handleClinet(this);
                    client.startClient(clientSocket, Convert.ToString(counter));
                }
                clientSocket.Close();
                myList.Stop();
                Console.WriteLine(" >> " + "exit");
                Console.ReadLine();
        }
        public class handleClinet
        {
            private readonly Form1 server; // readonly aby pole nie zostało przypadkowo zmienione
            public handleClinet(Form1 server) // Konstruktor
            {
                this.server = server; // Inicjalizujemy server
            }
            Socket socket;
            string clNo;

            public void startClient(Socket inClientSocket, string clineNo)
            {
                socket = inClientSocket;
                clNo = clineNo;
                Thread ctThread = new Thread(doChat);
                ctThread.Start();
            }
            private void doChat()
            {
                while (socket.Connected)
                {
                    NetworkStream ns = new NetworkStream(socket);
                    StreamReader sr = new StreamReader(ns);
                    StreamWriter sw = new StreamWriter(ns);
                    sw.AutoFlush = true;
                    try
                    {
                        server.ChangeText(sr.ReadLine());
                        sw.WriteLine(""); // Tu odpowiedź od serwera
                    }
                    catch (IOException )
                    {
                        // Tu możesz się pokusić o obsługę wyjątków
                    }
                }
            }
        }
    }
}

Podkreśla mi:
client.startClient(clientSocket, Convert.ToString(counter)); --> dokładniej clientSocket , "cannot convert from System.Net.Sockets.TcpClients to System.Net.Sockets

oraz
StreamReader sr = new StreamReader(ns);
StreamWriter sw = new StreamWriter(ns);
te 2 linijki

oraz:
catch (IOException ) a dokładniej IOException

Co mam zrobić ? bo już myślenie off całkowite

1

1:

client.startClient(clientSocket, Convert.ToString(counter));
// Powinno być:
client.startClient(clientSocket.Client, Convert.ToString(counter))

2: Dodaj:

using System.IO;
0

ok, jesteś wielki :D działa :D
teraz pytanie, bo w textBoxie mi znika wszystko zaraz po wysłaniu kolejnej wiadomości i tylko ostatnia wiadomość jest widoczna.
Jest jakaś opcja jak TextBox, tylko inna, żebym mógł w niej przechowywać dane klientów ?
I jeżeli dobrze rozumiem, to tu:
sw.WriteLine(""); // Tu odpowiedź od serwera
jak w "" wpiszę wiadomość to odeśle tą wiadomość do klienta, który wysłał wcześniej wiadomość ? czy mogę to zrobić jakoś, żeby odpowiedź serwer wysyłał już do innego podłączonego klienta ?

1

Jest opcja - RichTextBox i dodawanie do niego tekstu.

sw.WriteLine(""); wyślesz wiadomość do klienta który się z Tobą połączył i do którego należy wątek.

Musiałbyś trzymać listę klientów i parse'ować nadchodzące dane tak, abyś wiedział kto pisze do kogo i wtedy użył odpowiedniego klienta.
Wtedy poprzez ID używać odpowiedniego klienta do przekazania wiadomości od drugiego.

Tu implementacja zależy od Ciebie. Możesz dla przykładu zrobić to tak, że pierwszą wiadomością jaką klient wysyła do serwera jest jego unikalne ID.
Następnie, trzymasz listę wszystkich obecnie połączonych klientów, załóżmy, że jest to:

List<handleClinet> clients = new List<handleClinet>();
clients.Add(client); // Nowy klient to obiekt client, dodajemy go do listy podłączonych użytkowników.

Dodatkowo, klasa klienta ma pole ID:

public string ID;

Po sparsowaniu danych tak że parsedID to ID użytkownika do którego chcemy coś wysłać, możemy odszukać klienta i wysłać mu wiadomość parsedMessage.

var client = clients.Find(us => us.ID == parsedID);
if(client != null)
{
	client.SendMessage(parsedMessage);
}

Czyli:

  1. Serwer odbiera od klienta X wiadomość z jego ID i ustawia to w polu ID i dodaje klienta do listy.
  2. Serwer odbiera od klienta Y wiadomość z ID klienta Z i wiadomością dla niego. Załóżmy, że w formacie [ID]:[TEXT]
  3. Serwer "wycina" samo ID z wiadomości klienta X i szuka klienta Z w swojej liście.
  4. Serwer wywołuje metodę wysyłającą czystą wiadomość, tj. [TEXT], klienta Z jeżeli takowy istnieje.

Być może są lepsze rozwiązanie o których nie wiem, bo nie bawiłem się (jeszcze) z takimi rzeczami :D

0

a mogę zamiast robienia w kliencie unikalnych ID, to skorzystać w serwerze z tego clNo ? bo w sumie to numeruje mi podłączone klienty, więc mógłbym chyba jakoś po tym wysyłać do odpowiedniego clNo wiadomość ?
Edit.
co muszę dodać, żeby móc zrobić tak jak Ty:
client.sendMessage() ?? ponieważ jak wpisuje client. to wyświetla mi:
client.Equals
client.GetHashCode
client.GetType
client.StartClient
client.ToString

a więc nie mam opcji sendMessage()

1
  1. Tylko skąd użytkownik ma znać numer tego drugiego i mieć pewność, że to on?
  2. Nie masz, ale nic nie stoi na przeszkodzie aby dodać ;) Działanie jest takie same jak po stronie klienta (mówię o aplikacji klienta), przyjrzyj się jak tam to działa (jak zrobione jest łączenie z serwerem, wysyłanie mu wiadomości i odłączanie od serwera).
0
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.IO;

namespace Sieci_22._05._2017
{

    public partial class Form1 : Form
    {
        List<handleClinet> clients = new List<handleClinet>();
        public Form1()
        {
            InitializeComponent();
            
        }
       
        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread thr = new Thread(startServer);
            thr.Start();
        }

        public void textBox1_TextChanged(object sender, EventArgs e)
        {
            
        }
        public void ChangeText(string text)
        {
            Invoke(new Action(() => { richTextBox2.Text +=  text+"\n" ; })); // Zmieniamy tekst
            

        }
        public void addClient(string id)
        {
            Invoke(new Action(() => { richTextBox1.Text +=id+ "\n"; })); // Zmieniamy id
        }

        public void startServer()
        {
        //    List<handleClinet> clients = new List<handleClinet>();
            IPAddress ipAd = IPAddress.Parse("127.0.0.1");

            TcpListener myList = new TcpListener(ipAd, 8001);
                TcpClient clientSocket = default(TcpClient);
                int counter = 0;

                myList.Start();
                counter = 0;
                int parsedID = 1;
            var client2 = clients.Find(us => us.ID == parsedID);
            string parsedMessage;
                if (client2 != null)
            {
                client2.SendMessage(parsedMessage);
            }
            while (true)
                {
                    counter += 1;
                    clientSocket = myList.AcceptTcpClient();
                    Invoke(new Action(() => { textBox1.Text = " >> " + "Client No:" + Convert.ToString(counter) + " started!+\n"; }));
                    Thread.Sleep(400);
                    handleClinet client = new handleClinet(this);
                    clients.Add(client);
                    client.startClient(clientSocket.Client, Convert.ToString(counter));
                }

                clientSocket.Close();
                myList.Stop();
                Console.WriteLine(" >> " + "exit");
                Console.ReadLine();
        }
        public class handleClinet
        {
            public int ID;
            private readonly Form1 server; // readonly aby pole nie zostało przypadkowo zmienione
            public handleClinet(Form1 server) // Konstruktor
            {
                this.server = server; // Inicjalizujemy server
            }
            Socket socket;
            string clNo;

            public void startClient(Socket inClientSocket, string clineNo)
            {
                socket = inClientSocket;
                clNo = clineNo;
                Thread ctThread = new Thread(doChat);
                ctThread.Start();
                server.addClient(clNo);
               
            }
            public void SendMessage(parsedMessage)
            {
                NetworkStream ns = new NetworkStream(socket);
                StreamReader sr = new StreamReader(ns);
                StreamWriter sw = new StreamWriter(ns);
                sw.WriteLine("odpowiedz do klienta pierwszego.");
            }

            private void doChat()
            {
                while (socket.Connected)
                {
                    NetworkStream ns = new NetworkStream(socket);
                    StreamReader sr = new StreamReader(ns);
                    StreamWriter sw = new StreamWriter(ns);
                    sw.AutoFlush = true;
                    try
                    {
                        server.ChangeText(">>klient " + clNo +" >> " + sr.ReadLine());
                        sw.WriteLine("dostalem wiadomosc"); // Tu odpowiedź od serwera
                        

                    }
                    catch (IOException )
                    {

                    }
                }
            }
        }

        private void richTextBox1_TextChanged(object sender, EventArgs e)
        {

        }

        private void richTextBox2_TextChanged(object sender, EventArgs e)
        {

        }
    }
}

Próbowałem coś takiego, żeby załapać jak to ma działać (statycznie ustawiłem parsed ID na 1, żeby wysyłało odpowiedź do 1wszego klienta niezależnie od którego innego dostanie wiadomość. Ale coś dalej źle zrobiłem bo nie działa :/
Pomoże ktoś ?

0

@atmal ?

1
public void SendMessage(parsedMessage) // Brak typu parametru
{
	...
}
string parsedMessage;
if (client2 != null)
{
	client2.SendMessage(parsedMessage); // parsedMessage jest puste, nie możesz go przekazać.
}

W czym piszesz, że nie informuje o takich błędach?

Kod Ci nie działa, bo nie ma żadnego klienta z ID=1, bo nigdzie nie przypisujesz wartości do zmiennej w obiekcie HandleClient.
Poza tym próbujesz wysłać wiadomość do klienta zaraz po tym jak serwer wystartował gdzie nie ma jeszcze ani jednego klienta na serwerze, dopiero w while(true) jest:

clientSocket = myList.AcceptTcpClient();

który akceptuje połączenie.

0
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.IO;

namespace Sieci_22._05._2017
{

    public partial class Form1 : Form
    {
        List<handleClinet> clients = new List<handleClinet>();
        public Form1()
        {
            InitializeComponent();
            
        }
       
        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread thr = new Thread(startServer);
            thr.Start();
        }

        public void textBox1_TextChanged(object sender, EventArgs e)
        {
            
        }
        public void ChangeText(string text)
        {
            Invoke(new Action(() => { richTextBox2.Text +=  text+"\n" ; })); // Zmieniamy tekst
            

        }
        public void addClient(string id)
        {
            Invoke(new Action(() => { richTextBox1.Text +=id+ "\n"; })); // Zmieniamy id
        }

        public void startServer()
        {
        //    List<handleClinet> clients = new List<handleClinet>();
            IPAddress ipAd = IPAddress.Parse("127.0.0.1");

            TcpListener myList = new TcpListener(ipAd, 8001);
                TcpClient clientSocket = default(TcpClient);
                int counter = 0;

                myList.Start();
                counter = 0;
                int parsedID = 1;
           
            string parsedMessage="elo";
               
            while (true)
                {
                    counter += 1;
                    clientSocket = myList.AcceptTcpClient();
                    Invoke(new Action(() => { textBox1.Text = " >> " + "Client No:" + Convert.ToString(counter) + " started!+\n"; }));
                    Thread.Sleep(400);
                    handleClinet client = new handleClinet(this);
                    clients.Add(client);
                    client.startClient(clientSocket.Client, Convert.ToString(counter));
             //   var client2 = clients.Find(us => us.ID == parsedID);
             //   if (client2 != null)
             //   {
             //       client2.SendMessage(parsedMessage);
             //   }
                foreach (var client2 in clients)
                {
                    client2.SendMessage(parsedMessage);
                }
            }

                clientSocket.Close();
                myList.Stop();
                Console.WriteLine(" >> " + "exit");
                Console.ReadLine();
        }
        public class handleClinet
        {
            public int ID;
            private readonly Form1 server; // readonly aby pole nie zostało przypadkowo zmienione
            public handleClinet(Form1 server) // Konstruktor
            {
                this.server = server; // Inicjalizujemy server
            }
            Socket socket;
            string clNo;

            public void startClient(Socket inClientSocket, string clineNo)
            {
                socket = inClientSocket;
                clNo = clineNo;
                ID = Convert.ToInt32(clineNo);
                Thread ctThread = new Thread(doChat);
                ctThread.Start();
                server.addClient(clNo);
            }
            public void SendMessage(string parsedMessage)
            {
                NetworkStream ns = new NetworkStream(socket);
                StreamReader sr = new StreamReader(ns);
                StreamWriter sw = new StreamWriter(ns);
                sw.WriteLine("odpowiedz do klienta pierwszego.");
            }

            private void doChat()
            {
                while (socket.Connected)
                {
                    NetworkStream ns = new NetworkStream(socket);
                    StreamReader sr = new StreamReader(ns);
                    StreamWriter sw = new StreamWriter(ns);
                    sw.AutoFlush = true;
                    try
                    {
                        server.ChangeText(">>klient " + clNo +" >> " + sr.ReadLine());
                      //  sw.WriteLine("dostalem wiadomosc"); // Tu odpowiedź od serwera
                        

                    }
                    catch (IOException )
                    {

                    }
                }
            }
        }

        private void richTextBox1_TextChanged(object sender, EventArgs e)
        {

        }

        private void richTextBox2_TextChanged(object sender, EventArgs e)
        {

        }
    }
}

Chcę póki co zrobić, żeby do każdego klienta wysyłało wiadomość parsedMessage, więc zrobiłem:

 foreach (var client2 in clients)
                {
                    client2.SendMessage(parsedMessage);
                }

ale nie wysyła. Problem jest w foreachu, czy może w funkcji SendMessage ?

1

Problem leży po stornie klienta który odbiera wiadomość dopiero po wysłaniu swojej.

Tutaj masz przykład klienta asynchronicznego, spróbuj coś z tego ulepić :D

1 użytkowników online, w tym zalogowanych: 0, gości: 1