Avatar billede ashansen Nybegynder
20. april 2007 - 10:36 Der er 29 kommentarer og
1 løsning

socket listener

Hej folkens.

Jeg sidder og skriver på en klient i et sockets miljø..

Jeg har problemer med at få lavet en slags listener som står og lytter på om der kommer data fra min server, men der sendes data fra klienten til serveren...
For mig ser det ud som om, at lytte processen, optager streamen i den periode klienten lytter...

Måden jeg har bygget klienten op på er ved hjælp af en klasse med metoderne
- EstablishConnection
- SendData
- RecieveData
- CloseConnection

RecieveData ser ud som følger
        public string RecieveData()
        {
            return ClientReader.ReadLine();
        }

Nogen der kan give mig et spark i den regtige retning her?

Mvh

Allan
Avatar billede ashansen Nybegynder
20. april 2007 - 10:40 #1
Måske det kunne være nyttigt at poste hele min "socket" klasse

using System;
using System.Net;
using System.Net.Sockets;
using System.IO;


namespace SocketClientWin
{
    class Client
    {
        static TcpClient NetClient;
        static StreamReader ClientReader;
        static StreamWriter ClientWriter;


        public void EstablishConnection(string[] args)
        {
            String Server = args[0];
            int ServerPort = Int32.Parse(args[1]);

            NetClient = new TcpClient(Server, ServerPort);

            ClientReader = new StreamReader(NetClient.GetStream());
            ClientWriter = new StreamWriter(NetClient.GetStream());
        }
       
        public void SendData(string data)
        {
            ClientWriter.WriteLine(data);
            ClientWriter.Flush();
        }


        public string RecieveData()
        {
            return ClientReader.ReadLine();
        }


        public void CloseConnection()
        {
            ClientReader.Close();
            ClientWriter.Close();
            NetClient.Close();
        }
    }     
}
Avatar billede ashansen Nybegynder
20. april 2007 - 10:48 #2
Jeg havde i tankerne, at gribe på en recieveevent fra streamen, men jeg kan forstå at det som sådan ikke findes indenfor sockets.

Dog kan man lave det asynkront, men det er jo kun en fordel i forbindelse med at klienter connecter op imod server... eller hvad??
Avatar billede ashansen Nybegynder
20. april 2007 - 11:34 #3
Jeg har en windows form, hvorfra følgende kode eksekveres via en knap.
Det får hele applikationen til at fryse...

Hvis der er ikke er data i streamen, og denne så ikke er null (jeg har også prøvet med ""), hvad dælen er den så?
Eller er det en helt forkert måde at gribe det an på?


            Client WinClient = new Client();

            while (WinClient.RecieveData() != null)
            {
                View.Text = "SERVER: " + WinClient.RecieveData();
            }

            WinClient.SendData(txtboxSend.Text);
            View.Text += "CLIENT: " + txtboxSend.Text + "\r\n";
Avatar billede mr-kill Nybegynder
20. april 2007 - 12:28 #4
Hvis du laver så clienten kører i en tråd for sig selv, så fryser formen ikke. Kig evt. på denne kode:

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Windows.Forms;

namespace WindowsApplication1
{
    public class Client
    {
        private string ip;
        private int port;
        private StreamWriter sw;

        public Client(string _ip, int _port)
        {
            ip = _ip;
            port = _port;
        }

        public void Run()
        {
            TcpClient client = new TcpClient(ip, port);
            StreamReader sr = new StreamReader(client.GetStream());
            sw = new StreamWriter(client.GetStream());

            string line = "";
            while (true)
            {
                try
                {
                    line = sr.ReadLine();
                }
                catch
                {
                    MessageBox.Show("Forbindelse afbrudt!");
                    Environment.Exit(0);
                }

                MessageBox.Show(line);
            }
        }
        public void Send(string msg)
        {
            lock (sw) // lås så der ikke er andre der bruger den
            {
                sw.WriteLine(msg);
                sw.Flush();
            }
        }
    }
}

Den bruges så på denne måde:

private void Form1_Load(object sender, EventArgs e)
        {
            Client client = new Client("123.123.123.123", 123);
            Thread t = new Thread(new ThreadStart(client.Run));
            t.Start();

            client.Send("Hej!");
        }
Avatar billede ashansen Nybegynder
20. april 2007 - 13:06 #5
jeg kan umiddelbart godt modtage data fra min server nu...
men jeg kan ikke sende...

lock(sw) må ikke være null

Hvis jeg udkommenterer denne linie, får jeg fejl på
sw.WriteLine(msg);  Objektreferencen er ikke indstillet til en forekomst af et objekt.
Avatar billede mr-kill Nybegynder
20. april 2007 - 13:11 #6
Der skal nok en:

if(sw == null)
{
    MessageBox.Show("Der er ikke oprettet forbindelse til serveren");
    return;
}

i starten af public void Send(string msg)

Du kan kun sende til serveren når du også lytter fra den med min kode
Avatar billede mr-kill Nybegynder
20. april 2007 - 13:16 #7
I din form skal du så bruge den på denne måde i stedet (fik ikke lige testet det før)

private Client client;
        private Thread clientThread;
        private void Form1_Load(object sender, EventArgs e)
        {
            client = new Client("123.123.123.123", 123);
            clientThread = new Thread(new ThreadStart(client.Run));
            clientThread.Start();

            client.Send("Hej!");
        }
        }
Avatar billede mr-kill Nybegynder
20. april 2007 - 13:24 #8
Det skal også lige siges at grunden til du får fejl er fordi clienten ikke har noget at oprette forbindes inden der sendes, når det bliver gjort som jeg viste. Dette kan du løse enten ved at client tråden bruger en delegate til at fortælle din main-tråd hvornår den er færdig med at forbinde eller du kan vendte lidt med at sende noget til serveren og så bruge den "if" der tjekker om sw er null inden der bliver sendt. Lav en button der sender til serveren ved tryk.. så vil du se at det virker:

private void button1_Click(object sender, EventArgs e)
        {
            client.Send("Hej!");
        }
Avatar billede ashansen Nybegynder
20. april 2007 - 13:39 #9
ja nu virket det, nogenlunde :)

Serveren er sat til at hvergang der modtaget data fra klient, så skal der også sendes til den.
Nu er der bare det ved det, at det kun er hver anden gang der kommer noget igennem... det gælder begge veje..
Avatar billede mr-kill Nybegynder
20. april 2007 - 13:42 #10
hvis du poster noget mere kode, vil jeg gerne kigge på det..
Avatar billede ashansen Nybegynder
20. april 2007 - 13:47 #11
hep :)

her min socket klasse i serveren

using System;
using System.Net;
using System.Net.Sockets;
using System.IO;

namespace SocketServer
{
    class Server
    {
        TcpListener ServerListener;
        TcpClient Client;
        StreamReader ClientReader;
        StreamWriter ClientWriter;
       
        public void StartServer(string[] args)
        {
            int LocalServerPort = Int32.Parse(args[0]);
            Console.WriteLine("Server is startet\nWaiting for client to connect...");

            ServerListener = new TcpListener(IPAddress.Any, LocalServerPort);
            ServerListener.Start();
            Client = ServerListener.AcceptTcpClient();
            Console.WriteLine("Client connected succesfully!");

         
            ClientReader = new StreamReader(Client.GetStream());
            ClientWriter = new StreamWriter(Client.GetStream());

        }

        public void Send(string ServerData)
        {
            ClientWriter.WriteLine(ServerData);
            ClientWriter.Flush();
        }

        public string Recieve()
        {
            return ClientReader.ReadLine();
        }

        public void EndProgram()
        {
            ClientReader.Close();
            ClientWriter.Close();
            Client.Close();
            ServerListener.Stop();
        }
    }
}
Avatar billede ashansen Nybegynder
20. april 2007 - 13:48 #12
og main hvor serveren køres fra

using System;
using System.Collections.Generic;
using System.Text;
   

namespace SocketServer
{
    class Start
    {
        static void Main()
        {

            string[] ServerCredentials = {"10001"};

            Server NetworkServer = new Server();
            NetworkServer.StartServer(ServerCredentials);
           
            while (true)
            {
                Console.WriteLine(NetworkServer.Recieve() + " og der");
            }

            NetworkServer.EndProgram();
            Console.WriteLine("Socketconnection closed!");

            Console.ReadLine();
           
        }
    }
}
Avatar billede mr-kill Nybegynder
20. april 2007 - 14:02 #13
Hmmm når jeg tester det med den client klasse jeg postede, så virker det fint.. har også prøvet at lade servern svare hver gang der bliver modtaget noget og alle beskeder kommer igennem begge veje...

Kan du måske sige lidt mere om hvad der ikke kommer igennem?
Avatar billede lasserasch Juniormester
21. april 2007 - 06:11 #14
Nu blander jeg mig bare lige lidt i denne post her, men jeg har også lige siddet et par dage og leget med socket forbindelser.

Har fået min kode til at virke ret godt. Den bruger en lidt anden tilgang men det virker 100% og også uden brug af eksterne komponenter osv. Den kunne du jo evt. kigge igennem. Koden er rimelig godt kommenteret og med fungerende eksempler.

Du kan downloade den her : www.skare.dk/tcpkommunikation.zip

Der er en server og klient i eksemplet og serveren kan snakke med flere klienter på en gang = den bruger flere tråde...

Bare et forslag!

/Lasse
Avatar billede ashansen Nybegynder
21. april 2007 - 10:27 #15
Hej igen

Jeg kigger videre på det i eftermiddag når sønnike sover til middag..

lasserasch, tak for eksemplerne.
Jeg har lige hurtigt skimtet clienten igennem.
Jeg kan se, at der er det den native del at sockets du bruger, hvor jeg bruger higher level abstraction layer.
Fordelen ved dette skulle være at kodemængden reduceres og så dette .net socketslag for en del at det slaviske kodearbejde...

Men tak for din interesse... jeg kigger videre på det, også dit materiale

Mvh

Allan
Avatar billede lasserasch Juniormester
21. april 2007 - 13:21 #16
Jeg er ny omkring socket's så det ved jeg ikke rigtig så meget om, men kodemængden da ikke specielt størrere end i overstående kodestumper. Måske lidt, men ikke meget...


Nå, men det virker i hvert fald for mig. Jeg brugte nemlig også lang tid på at sidde og lege med noget ala det du er igang med nu. Kunne bare ikke få det til at virke ordenligt. Men fandt et program på nettet som jeg splittede ad, og det jeg så har lagt op på nettet, er så resultatet af det lille projekt.

Held og lykke...

Mvh.
Lasse
Avatar billede ashansen Nybegynder
19. maj 2007 - 15:40 #17
Hej igen

Jeg  beklager mit lange fravær

Jeg har fået det til at virke nu.
Mr. Kill, du skal have point!

Dog har jeg et spørgsmpl på falderebet.
I min form har jeg et textboks element.
Hvordan får jeg alt det der læses i streamen (fra tråden) skrevet ud heri?

Mvh

Allan
Avatar billede ashansen Nybegynder
19. maj 2007 - 18:07 #18
og kan man på en måde sætte en trigger op i formen?
Avatar billede mr-kill Nybegynder
19. maj 2007 - 20:09 #19
Hvis du vil sææte noget i din form fra en anden tråd så skal du kigge på delegates..

http://www.akadia.com/services/dotnet_delegates_and_events.html
Avatar billede ashansen Nybegynder
19. maj 2007 - 20:31 #20
takker herfra
Avatar billede ashansen Nybegynder
20. maj 2007 - 21:41 #21
Hej igen

Nu har jeg kigget på delegates og events.
JEg har det næsten oppe at køre nu, men mangler bare lige det sidste.

Her kommer uddrag fra min form

        Client WinClient = new Client("10.70.0.10", 10001);
       

        public Form1()
        {
            InitializeComponent();

            WinClient.StreamEvent +=new Client.StreamDelegate(test);
            Thread ClientThread = new Thread(new ThreadStart(WinClient.StartListener));
            ClientThread.Start();
        }

        public void test()
        {
            View.Text = WinClient.ExportDataFromListener();
        }


og her fra socket


        public delegate void StreamDelegate();
        public event StreamDelegate StreamEvent;


        public Client(string _ip, int _port)
        {
            ip = _ip;
            port = _port;
        }

       
        public void StartListener()
        {
            TcpClient client = new TcpClient(ip, port);

            sr = new StreamReader(client.GetStream());
            sw = new StreamWriter(client.GetStream());

            string line = "";
            while (true)
            {
                try
                {
                    line = sr.ReadLine();
                }
                catch
                {
                    MessageBox.Show("Forbindelse til server afbrudt!");
                    Environment.Exit(0);
                }

                MessageBox.Show(line);
                ExportData = line;
                StreamEvent();
            }
        }


        public string ExportDataFromListener()
        {
            return ExportData;
        }


Jeg opretter en delegate og binder den op på en eventhandler.
Det virker fint, men når der i min form notificeret om en event, vil jeg gerne at den returnede streng fra metoden ExportDataFromListener udskrives i view.text i min form.
Det kan den ikke da hvis er fra en anden tråd - jeg får følgende fejl :

Handling på tværs af tråde er ugyldig: Objektet 'View' blev åbnet fra en anden tråd end den tråd, det blev oprettet i.


Kan i hjælpe?

Mvh

Allan
Avatar billede lasserasch Juniormester
20. maj 2007 - 22:00 #22
I test() skal du tjekke op på InvokeRequired.

Igen er der et eksempel på hvordan det gøres, i den eksempel kode jeg linkede til tidligere :  http://www.skare.dk/tcpkommunikation.zip

/Lasse
Avatar billede ashansen Nybegynder
21. maj 2007 - 21:59 #23
jo, men jeg kan ikke umiddelbart gennemskue det og overføre det til min egen kode.
Kan du give et eksempel?

Mvh

Allan
Avatar billede lasserasch Juniormester
21. maj 2007 - 23:15 #24
Der er et eksempel på det i tcpkommunikation.zip. Og det er også rimelig godt kommenteret.

Men her er basis :

1. Når du får denne "fejl", er det fordi du forsøger at opdatere en kontrol som ligger på din form og er synlig for brugeren. Dette må ikke gøres på tværs af tråde. (Man kan godt slå advarslen fra, men det er absolut ingen løsning. Der er jo en grund til at den er der...)

2. Du skal bruge en delegate til det.

Den definerer du i starten af din kode.

I dette eksempel trækkes en streng med igennem forløbet også. Det er jo ikke altid nødvendigt...

public partial class form1 : Form
    {
        delegate void Delegate1(String TXT);

...



3.
Du kalder pt. et eller anden void. Og det er her det går galt. Det skal du blive ved med.

Du skal bare tilføje lidt kode :

Her et eksempel på en void som bruger den delegate jeg lige har defineret :
Min void forsøger at sætte teksten i en label som ligger på formen. Det får den ikke
lov til, hvis der kaldes fra en anden tråd.

private void dinvoid(string TXT)
        {
        if (label1.InvokeRequired)
            {
                Delegate1 d = new Delegate1(dinvoid);
                this.Invoke(d, new object[] { TXT });
            }
            else
            {
                label1.Text = TXT;
            }

      }

Det er forholdsvis simpelt når man lige fanger pointen i det.
Når du opretter en ny reference til "Delegate1", så skal man angive navnet på den void man står i pt, da det jo stadig er den void man vil arbejde med.

Og TXT er bare en String variabel som vi skal bruge. Hvis du ikke har brug for den sletter man jo bare lidt af koden så den ser sådan ud :

public partial class form1 : Form
    {
        delegate void Delegate1();

...



private void dinvoid()
        {
        if (label1.InvokeRequired)
            {
                Delegate1 d = new Delegate1(dinvoid);
                this.Invoke(d);
            }
            else
            {
                label1.Text = "JUHUUEEE.... DET VIRKER!";
            }

      }


/Lasse
Avatar billede lasserasch Juniormester
21. maj 2007 - 23:19 #25
Håber det kan hjælpe dig! Jeg er smuttet i seng nu! Lasse Out...
Avatar billede ashansen Nybegynder
22. maj 2007 - 21:25 #26
Så fik jeg den
Jeg prøvede med både delegastes og events samtidig.
Nu kan jeg snuppe data fra én tråd over til en anden.

Dog driller event handleren mig.
Jeg har jo bekendt en tråd hvori jeg har min listener, som konstant står oglytter på data, jeg vil så gerne at denne smider en event når der er data..

har du/i et godt spark i denne retning?
Avatar billede lasserasch Juniormester
22. maj 2007 - 22:29 #27
Har du ikke en "OnDataArrival" eller noget i den retning event, som du kan bruge til det?

Jeg bruger ikke selv events når jeg lytter, men min socket løsning er jo også lidt anderledes end din.

/Lasse
Avatar billede mr-kill Nybegynder
22. maj 2007 - 22:41 #28
Ta et kig på denne kode jeg har skruet sammen, måske kan du bruge den til noget...

using System;
using System.Windows.Forms;
using System.Threading;

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

        private Thread thread;

        private void Form1_Load(object sender, EventArgs e)
        {
            Modtager modtager = new Modtager();
            modtager.Evt += new msgDeletage(modtager_Evt);

            thread = new Thread(new ThreadStart(modtager.Start));
            thread.Start();
        }

        private void modtager_Evt(string msg)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new msgDeletage(modtager_Evt), new object[] { msg });
                return;
            }

            textBox1.Text += msg + Environment.NewLine;
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            thread.Abort();
        }
    }

    public class Modtager
    {
        public event msgDeletage Evt;

        public void Start()
        {
            while (true)
            {
                System.Threading.Thread.Sleep(1000);
                Evt("Data modtaget!");
            }
        }
    }
    public delegate void msgDeletage(string msg);
}
Avatar billede ashansen Nybegynder
06. juni 2007 - 12:59 #29
Hej igen

Så virker det endelig for mig :)
dog undre det mig, at jeg "kun" kan udskrive til f.eks. en messagebox og ikke en label...

via følgende
      public void ManageControls(string message)
        {
            //label1.Text = message;
            MessageBox.Show(message);
        }

dette er i min form.
Den trigger på et event fra en anden klasse jeg har.
Gennem min delegate overfører jeg så data, som jeg udskriver i en messageboks, men det virker bare ikke i min label...

Det forekommer mig simpelt at arbejder med de her kontroller, men den her kan jeg dælme ikke gennemskue....
Avatar billede lasserasch Juniormester
06. juni 2007 - 14:50 #30
Igen kan jeg kun råde dig til at studere mit førnævnte eksempel grundigt. Den laver netop en udskrivning til controls på en form.

Og ja det er underligt, for det skal virke. Du er sikker på at du ikke rende ind i en exception? MessageBox kræver jo ikke nogen invoke da den ikke ligger på formen som en control. Det gør din label... Så jeg tror ikke du får invoked korrekt. Tjek din output og se om der står noget om en exception der...

/Lasse
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