Avatar billede doomstone-dk- Nybegynder
11. maj 2008 - 17:36 Der er 12 kommentarer og
1 løsning

TCP server. problem med at modtage/sende.

Hejsa, jeg sidder og leget lidt med noget netværk haløj.
Ikke noget specifikt, men vil gerne lære det.

Det jeg vil lave er en server, som kan have flere clienter på en gang.

Men jeg har haft 2 problemer 
1. Flere clienter, har dog løst det med noget multi threat, er det dog den bedste mode?

2. Kan kun sende data til cliente hvis han vil sender noget til serveren først.

Er der en måde jeg kan gøre det lidt OOP på? så f.eks. den kalder en funktion Incomming(something client, string message) når der kommer en besked, og så ellers have en while(1) ved siden af som sende den data til den som skal sendes?

Her er det jeg har pt. Det er sammen sat ud fra hvad jeg kunne finde af guides.
Hvis i kende nogle andre gode guides omkring dette må i meget gerne linke til dem

Kode:
    class Server
    {
        private TcpListener tcpListener;
        private Thread listenThread;

        public Server(int port)
        {
            Console.WriteLine("Starting server on port: " + port + "...");
            this.tcpListener =
              new TcpListener(IPAddress.Any, port);
            this.listenThread =
              new Thread(new ThreadStart(ListenForClients));

            this.listenThread.Start();
        }

        private void ListenForClients()
        {
            this.tcpListener.Start();

            while (true)
            {
                //blocks until a client has connected to the server
                TcpClient client = this.tcpListener.AcceptTcpClient();
                Console.WriteLine("Got Client, Starting client thread.");

                //create a thread to handle communication
                //with connected client
                Thread clientThread =
                  new Thread(new
                  ParameterizedThreadStart(HandleClientComm));
                clientThread.Start(client);
            }
        }

        private void HandleClientComm(object client)
        {
            TcpClient tcpClient = (TcpClient)client;
            NetworkStream clientStream = tcpClient.GetStream();

            clientStream.ReadTimeout = 50;

            byte[] message = new byte[4096];
            int bytesRead;
            byte[] responce;

            while (true)
            {
                bytesRead = 0;

                try
                {
                    //blocks until a client sends a message
                    bytesRead = clientStream.Read(message, 0, 4096);
                }
                catch
                {
                    //a socket error has occured
                    break;
                }

                if (bytesRead == 0)
                {
                    //the client has disconnected from the server
                    break;
                }

               

                //message has successfully been received
                ASCIIEncoding encoder = new ASCIIEncoding();
                System.Diagnostics.Debug.WriteLine(
                  encoder.GetString(message, 0, bytesRead));
                responce = encoder.GetBytes("Hello");
                clientStream.Write(responce, 0, responce.Length);
            }

            tcpClient.Close();
        }
    }
Avatar billede arne_v Ekspert
11. maj 2008 - 18:59 #1
En tråd per client er langt den nemmeste måde at programmere det på.

Det skalerer ikke så godt hvis du skal understøtte rigtigt mange klienter.

Men udmærket design at starte med.
Avatar billede arne_v Ekspert
11. maj 2008 - 19:00 #2
Du vælger selv hvilken ende der skal læse først og hvilken der skal skrive først.
Avatar billede arne_v Ekspert
11. maj 2008 - 19:01 #3
Hvis det er tekst du læser og skriver, så wrap Stream in StreamReader og StreamWriter - det er
meget nemmere.
Avatar billede doomstone-dk- Nybegynder
11. maj 2008 - 19:12 #4
Som sagt så er det bare noget jeg leget med, så det bliver ikke mande den skal understøtte, maks 2-5 clienten :D

Du vælger selv hvilken ende der skal læse først og hvilken der skal skrive først.
Hvad mener du med det?

Men det er jo meget simpel, hvis der er en komando der kan fortælle mig om der er noget at læse. Har dog ikke lige kunne finde sådan en.
Og den måde det bliver gjort på nu gør jo sådan at thread'en stopper til der er noget at læse, og så der efter sender noget.
Avatar billede arne_v Ekspert
11. maj 2008 - 20:19 #5
Kan du ikke bruge DataAvailable property.

Men helt generelt gælder det at det er en god ting med en klar protokol om hvem der
skriver først.
Avatar billede doomstone-dk- Nybegynder
11. maj 2008 - 20:30 #6
Ja men f.eks. hvis den køre en chat og f.eks. nogle andre data'er.
Så skal den jo sende Chat data når de kommer ind, og så skal den modatage data når cliente sender en besked.

Og den eneste måde jeg kan se lige pt er at den sende en besked og venter på at modtage en eller omvendt.

Eller også skal clienten konstant requeste chat beskeder, hvilket ville vær spild at bånd brede :D
Avatar billede arne_v Ekspert
11. maj 2008 - 20:34 #7
Det du gør der er at du lader alle client trådene læse med blocking read og så lader
du den tråd der har læst noget sende til alle klienterne.
Avatar billede arne_v Ekspert
11. maj 2008 - 20:35 #8
Jeg har liggende noget kode på lageret.
Avatar billede arne_v Ekspert
11. maj 2008 - 20:35 #9
using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

class ChatMain
{
    public static void Main(string[] args)
    {
        Server srv = new Server();
        srv.Run();
    }
}

class Server
{
    private ArrayList allclients;
    public void Run()
    {
        allclients = new ArrayList();
        TcpListener server = new TcpListener(IPAddress.Any, 50000);
        server.Start();
        while(true)
        {
            TcpClient client = server.AcceptTcpClient();
            ClientHandler ch = new ClientHandler(client, this);
            allclients.Add(ch);
            (new Thread(new ThreadStart(ch.Run))).Start();
        }
    }
    public void SendAll(string msg)
    {
        foreach(ClientHandler ch in allclients)
        {
            ch.Send(msg);
        }
    }
    public void SendOne(string msg, string address)
    {
        foreach(ClientHandler ch in allclients)
        {
            if(ch.Remote.IndexOf(address) == 0)
            {
                ch.Send(msg);
            }
        }
    }
    public void Remove(ClientHandler ch)
    {
        allclients.Remove(ch);
    }
}

class ClientHandler
{
    private string remote;
    private StreamReader rdr;
    private StreamWriter wrt;
    private Server srv;
    public ClientHandler(TcpClient cli, Server srv)
    {
        remote = cli.Client.RemoteEndPoint.ToString();
        rdr = new StreamReader(cli.GetStream());
        wrt = new StreamWriter(cli.GetStream());
        this.srv = srv;
    }
    public void Run()
    {
        while(true)
        {
            string line = rdr.ReadLine();
            string[] parts = line.Split(" ".ToCharArray());
            string cmd = parts[0];
            if(cmd == "SEND")
            {
                string address = parts[1];
                if(address == "*")
                {
                    srv.SendAll(line.Substring(cmd.Length + address.Length + 2));
                }
                else
                {
                    srv.SendOne(line.Substring(cmd.Length + address.Length + 2), address);
                }
            }
            else if(cmd == "EXIT")
            {
                srv.Remove(this);
                return;
            }
        }
    }
    public void Send(string msg)
    {
        lock(wrt)
        {
            wrt.WriteLine(msg);
            wrt.Flush();
        }
    }
    public string Remote
    {
        get
        {
            return remote;
        }
    }
}
Avatar billede arne_v Ekspert
11. maj 2008 - 20:35 #10
using System;
using System.IO;
using System.Net.Sockets;
using System.Threading;

class ChatClient
{
    public static void Main(string[] args)
    {
        TcpClient client = new TcpClient("localhost", 50000);
        StreamWriter wrt = new StreamWriter(client.GetStream());
        (new Thread(new ThreadStart((new Reader(client)).Run))).Start();
        string line;
        while((line = Console.ReadLine()) != null)
        {
            wrt.WriteLine("SEND " + line);
            wrt.Flush();
        }
        wrt.WriteLine("EXIT");
        wrt.Flush();
        wrt.Close();
        client.Close();
    }
}

class Reader
{
    private StreamReader rdr;
    public Reader(TcpClient cli)
    {
        rdr = new StreamReader(cli.GetStream());
    }
    public void Run()
    {
        try
        {
            string line;
            while((line = rdr.ReadLine()) != null)
            {
                Console.WriteLine(line);
            }
        }
        catch(Exception)
        {
            // nothing
        }
    }
}
Avatar billede doomstone-dk- Nybegynder
11. maj 2008 - 21:16 #11
Tusind tak, har leget lidt med det nu, og tror jeg har lidt styr på hvordan du gør.
Hvis du skrive noget som et svar får du nogle point :D så vil jeg bruge resten af natten på at leget med det :D
Avatar billede arne_v Ekspert
11. maj 2008 - 23:30 #12
svar
Avatar billede rasmuzzen Nybegynder
12. maj 2008 - 12:43 #13
Syntes du skulle kikke lidt på part II i denne artikel. For lige om lidt skal dine tråde måske accesse komponenter på din form, og det er ikke lige til.

http://www.codeguru.com/csharp/csharp/cs_network/sockets/article.php/c7695/

Part I virker ikke optimalt, part 2 er et godt eksempel.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester