Avatar billede simsen Mester
30. oktober 2011 - 19:27 Der er 19 kommentarer og
1 løsning

Diverse klient/server spørgsmål

Hej,

Jeg har valgt at gå videre med en klient/server løsning til en opgave. Nu har jeg så tonsvis af spørgsmål (som måske ikke alle har noget med klient/server - men det kan I så fortælle mig forhåbentligt).....

Jeg har kigget på to forskellige tutorials til client/server løsning og de er forskellige i den forbindelse at den ene bruger StreamReader og læser/skriver linjerne som tekst linjer. Den anden bruger en anden tilgang hvor den sender via Socket's og sender det som bytes...Hvad er fordele og ulemper ved de to?

Jeg spørger helt sikkert dumt, men skal jeg vitterlig starte serveren hver gang, jeg vil spørge om noget på den. Kører den ikke altid i baggrunden og venter på forespørgsler fra klienten?

Så til det 3. spørgsmål, som jeg ikke har kunnet finde noget på..... Begge løsninger går ud på at lytte til én ting og så sende én ting retur.

Jeg vil jo lave en forfærdelig masse metoder på min server, hvor klienten så skal modtage en masse forskelligt tilbage (strings, tables osv). Men jeg kan ikke finde ud af hvordan det gøres?

Her er koden til den af løsningerne jeg arbejder på lige pt:

Server delen:

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

        private void btnStartServer_Click(object sender, EventArgs e)
        {
            Thread t = new Thread(new ThreadStart(StartServer));
            t.IsBackground = true;
            t.Start();
        }

        void StartServer()
        {
            int port = 8000;
            IPAddress localAddr = IPAddress.Parse("127.0.0.1");
            TcpListener server = new TcpListener(localAddr, port);
            server.Start();

            while (true)
            {
                Socket clientsocket = server.AcceptSocket();
                if (clientsocket.Connected)
                {
                    NetworkStream networkStream = new NetworkStream(clientsocket);
                    System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);
                    string theString = DateTime.Now.ToString();
                    streamWriter.WriteLine(theString);
                    streamWriter.Flush();
                    networkStream.Close();
                    streamWriter.Close();
                }
                clientsocket.Close();
            }

        }

        void GetStringFromServer(bool isStringCorrect)
        {
            int port = 8000;
            IPAddress localAddr = IPAddress.Parse("127.0.0.1");
            TcpListener server = new TcpListener(localAddr, port);
            server.Start();

            while (true)
            {
                Socket clientsocket = server.AcceptSocket();
                if (clientsocket.Connected)
                {
                    NetworkStream networkStream = new NetworkStream(clientsocket);
                    System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);

                    string theString = string.Empty;
                    if (isStringCorrect)
                    {
                        theString = "Yes this string is correct";
                    }
                    else
                    {
                        theString = "No this string isn't correct";
                    }

                    streamWriter.WriteLine(theString);
                    streamWriter.Flush();
                    networkStream.Close();
                    streamWriter.Close();
                }
                clientsocket.Close();
            }

        }

        void GetDataTableFromServer()
        {
            DataTable getData = new DataTable();
            getData.Columns.Add("", typeof(int));
            getData.Columns.Add("", typeof(string));

            getData.Rows.Add(1, "");
            getData.Rows.Add(2, "");

            int port = 8000;
            IPAddress localAddr = IPAddress.Parse("127.0.0.1");
            TcpListener server = new TcpListener(localAddr, port);
            server.Start();

            while (true)
            {
                Socket clientsocket = server.AcceptSocket();
                if (clientsocket.Connected)
                {
                    NetworkStream networkStream = new NetworkStream(clientsocket);
                    System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);

                    streamWriter.Write(getData);
                    streamWriter.Flush();
                    networkStream.Close();
                    streamWriter.Close();
                }
                clientsocket.Close();
            }
        }
    }
}

Klient delen:

namespace Client
{
    public partial class Client : Form
    {
        public Client()
        {
            InitializeComponent();
            txtTimeOut.Text = "400";
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            try
            {
                int timout = Convert.ToInt32(txtTimeOut.Text);
                int port = 8000;
                IPAddress localAddr = IPAddress.Parse("127.0.0.1");
                IPEndPoint remoteEndPoint = new IPEndPoint(localAddr, port);


                TcpClient NetworkClient = TimeOutSocket.Connect(remoteEndPoint, timout);

                NetworkStream networkstream = NetworkClient.GetStream();
                StreamReader streamReader = new StreamReader(networkstream);
                string line = streamReader.ReadLine();
                if (!string.IsNullOrEmpty(line))
                  txtServerTime.Text = line;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void btnGetString_Click(object sender, EventArgs e)
        {
            //Her jeg ikke aner hvad jeg skal skrive - for jeg vil jo have metoden, der henter strengen
        }

        private void btnGetTable_Click(object sender, EventArgs e)
        {
            //Her jeg ikke aner hvad jeg skal skrive - for jeg vil jo have metoden, der henter strengen

            //Altså det jeg har brug for at lære, er hvordan jeg henter specifikke metoder fra serveren
        }
    }
}
Avatar billede heinzdmx Nybegynder
30. oktober 2011 - 19:35 #1
Tag og læs om WCF (Windows Communication Fundation)
http://msdn.microsoft.com/en-us/netframework/aa663324

Her er beginners guide, som nok skal hjælpe dig lidt på vej.

Her er en forskel i forhold til hvad du har kigget på, nemlig at du ikke laver en direkte forbindelse, men kører det hele igennem WCF, som dermed sørger for at gøre det hele optimalt.

Prøv at se her:
http://msdn.microsoft.com/da-dk/library/ms751519.aspx

Så det du gør er at du kalder metoder på serveren, der returnere de værdier du gerne vil have tilbage.

F.eks. kunne dine metoder se sådan ud:

// Define a service contract.
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")]
public interface ICalculator
{
    [OperationContract]
    double Add(double n1, double n2);
    [OperationContract]
    double Subtract(double n1, double n2);
    [OperationContract]
    double Multiply(double n1, double n2);
    [OperationContract]
    double Divide(double n1, double n2);
}


Det er et simpelt interface som server (WCF service) implementere
Avatar billede arne_v Ekspert
30. oktober 2011 - 19:36 #2
Foerst skal du vedtage om du vil sende tekst eller binaere data.

Din kode ser nogenlunde ud.

Jeg ville nok accepte en TcpClient fremfor en Socket, men det er n detalje.

http://www.eksperten.dk/guide/515 har en masse kode til baad tekst og binaer. Men den kode ligner saamaend din ret meget.
Avatar billede arne_v Ekspert
30. oktober 2011 - 19:38 #3
Med hensyn til metode kald, saa ville du altsaa kunne spare en del vd at skifte til remoting eller web services.

Brug evt. 5 minutter paa at laese:

http://www.eksperten.dk/guide/86

http://www.eksperten.dk/guide/516
Avatar billede arne_v Ekspert
30. oktober 2011 - 19:52 #4
WCF er saa en maade at lave web services paa i .NET (dyn nye maade).

Et saerdeles fleksibelt vaerktoj. Men ogsaa med en konfigurations fil som nogen gange giver folk graa haar i hovedet.
Avatar billede simsen Mester
30. oktober 2011 - 20:12 #5
Jeg vender frygtelig tilbage til jer i løbet af ugen. Jeg kan mærke, jeg ikke skal se på mere kodeeksempler i dag, hvis jeg vil på arbejde i morgen :-)
Avatar billede simsen Mester
07. november 2011 - 11:16 #6
Hej igen, så er jeg frygtelig tilbage ;-)

Først har jeg forsøgt mig med remoting vha. af Arne's link - netop fordi jeg tror, det er den jeg skal bruge. Programmet jeg skal lave skal have en serverdel, og flere klienter, der kører op imod serveren. Det hele på et LAN: http://www.eksperten.dk/guide/86 Men har problemer med denne tutorial:

Eksempel 1 + 2 + 3:

Starter Server
Starter Client - går så stærkt, at jeg ikke kan se, hvad der står. Laver en "Run to" til test1() metoden i klienten og får en fejl i Serveren:

SocketException was unhandled: Normalt må en socket-adresse(protokol/netværksadresse/port) kun bruges én gang.

Stoppede serveren og startede så kun klienten - med følgende fejl i test1() linje 2:

SocketException was unhandled: Der kunne ikke oprettes forbindelse, fordi destinationscomputeren aktivt nægtede det 127.0.0.1:50000

Nå - tænkte jeg ville gå videre med web-services (selvom det hele skal foregå via LAN og ikke internettet)... Jeg må flovt bekende, jeg ikke fatter en meter af dit link: http://www.eksperten.dk/guide/515. Jeg sætter min lid til, det er fordi jeg ikke er Java haj. Så jeg har forsøgt at google mig frem til det (og har også fået et eksempel til at spille)....MEN dertil er der så nogle ting jeg bare ikke forestår, som jeg håber I kan forklare mig;

Jeg opretter et projekt til selve webservice delen. Jeg giver den en eller anden namespace (simsen.dk) med nogle metoder i. Alt er godt indtil nu. Jeg installerer den så på en server et eller andet sted.

Nu skal jeg så lave klient delen (og her det hele krakelerer for mig). Jeg skal så nu tilføje en reference til webservice. Det gør jeg så (og kan jo også finde den, da den jo ligger på min maskine).....Jeg bruger så referencen og en metode fra referencen og som skrevet - det spiller........

Men nu til det jeg ikke forstår; Fint nok det spiller på MIN pc, hvor jeg har begge ting liggende. Men hvad når jeg nu smider det ud på to forskellige computere (med 2 forskellige ip adresser i et LAN)??? Webservicen skal jo ligge på en server og når jeg så smider klientdelen (altså win formen) ud på en klient, hvordan hulen kan den så vide, at det er på den og den server, den skal finde webservicen?

Så et andet spørgsmål....Jeg ved jo ikke hvad namespacet er for den pågældende webservice (jeg kan jo så selvfølgelig bare finde på en) - men kan man ikke lægge ip adresser ind i f.eks. en txt fil og så hente ip adressen i stedet for?
Avatar billede simsen Mester
07. november 2011 - 13:38 #7
Hej Arne,

Glem lige ovennævnte (har fået det til at virke - var ikke klar over at klienten skulle køres direkte fra dosprompt og ikke fra VS)....

Men jeg har stadig nogle forståelsesspørgsmål:

1) Du viser forskellige metoder til at kalde remote server - er den ene bedre end den anden? Eller kan jeg bare tage en af dem, og bruge denne?

2) Jeg forstår ikke forskellen på de forskellige servere/klienter. Sao skulle være server aktiveret objekter - hvor der så er to forskellige måder SingleCall og Singleton og så er der Client Activated object. Men hvorfor har du ved SAO eksemplerne så tilføjet calc referencen i klient delen - altså hvis den alligevel tager serverens calc reference?

Der er et eller andet grundlæggende jeg misser. I min verden burde det være sådan at metoderne (og dermed calc referencen) ligger på serveren, hvor klienten kalder metoderne på serveren med de parametre der nu engang måtte være. Hvis calc referencen lægger også i klienten, skal jeg jo OGSÅ ind og opdatere klient programmet (og kan dermed ikke nøjes med at opdatere serverprogrammet), hver gang jeg har fundet en eller anden dum regnefejl, som jeg skal have ændret. Det vil sige, der skal reinstalleres på X antal maskiner i stedet for kun på én server?
Avatar billede simsen Mester
07. november 2011 - 13:39 #8
Nu skrev jeg godt nok du skulle glemme mit forrige indlæg.....fik bare ikke tilføjet kun omkring remote ikke om webservices :-)
Avatar billede arne_v Ekspert
07. november 2011 - 15:45 #9
re forskellige remoting modeller)

forskellen er hvor mange instanser af server side objektet du vil have - et, et per kald eller et per client
Avatar billede arne_v Ekspert
07. november 2011 - 15:46 #10
re ref)

Du skal ned under "Avanceret eksempel" for at faa et realistisk eksempel hvor client kun behoever et interface ikke implementations klassen
Avatar billede arne_v Ekspert
07. november 2011 - 15:47 #11
Kode kan godt koeres fra VS men saa skal du nok have en

Console.ReadKey();

i bunden for at du kan naa at laese hvad der skrives.
Avatar billede arne_v Ekspert
07. november 2011 - 15:48 #12
Namespace for web service kan du se i WSDL eller i den genererede kode (der genereres faktisk noget kode naar du tilfoejer en ref til en web service)
Avatar billede arne_v Ekspert
07. november 2011 - 15:49 #13
Hvis du genererer stub udfra en WSDL URL og den senere aendrer sig kan du ikke bruge den constructor uden argument, men jeg mener at der ogsaa er en constructor som tager en URL som argument (og den kan naturligvis hentes fra config).

Kig paa den genererede kode.
Avatar billede simsen Mester
08. november 2011 - 09:46 #14
Hej Arne,

Jeg fortsætter lige med Remoting. Jeg har lavet dit avancerede eksempel og får følgende fejl i klienten:

"Filen eller assemblyen 'XImpl' eller en af dens afhængigheder kunne ikke indlæses. Den angivne fil blev ikke fundet."

Jeg kan så regne ud, at jeg mangler et eller andet (og tror det er under server-delen)....

Jeg har forsøgt at lave en reference i Serverdelen til X.Common.dll og X.Impl.dll (har forsøgt med kun den ene og dem begge).....Det hjalp ikke - så forsøgte jeg at kopiere dem direkte ind i bin/debug mappen (for at være sikker på de også var der)...

Der hvor jeg tror det går galt er selve config filen i serverdelen. Jeg kunne slet og ret ikke finde en config til winforms, så jeg snuppede webforms i stedet, og slettede alt i filen og copy/pastede så det ind du har skrevet i din vejledning. Herefter kopierede jeg filen ind i bin/debug mappen.

Håber du kan se, hvor jeg fejler?

Så til nogle forståelses ting i dit udvidede eksempel;

Det glæder mit lille julehjerte, at jeg har fået mig en interface klasse (X.Common/IX.cs filen). Den del forstår jeg :-)

Men så kommer vi til X.Impl klassen.....
Her laver du to forskellige klasser med samme metode. Det forvirrer mig fuldstændig.... I X.Client kalder du med

IX a = (IX)Activator.GetObject(typeof(IX), "tcp://localhost:50000/A");

Hvor jeg regner med /A er det der afgør hvilken af metoderne den henter....Men hvordan i alverden finder den ud af at /A = XRealA?

Er det meget unaturligt (=dumt), at min tanke er at lave et interface for hvert object. Altså at jeg laver et interface, der hedder ICustomer - i den laver jeg så nogle metoder GetCustomer(int customerId) SetCustomer(int customerId, string customerName). Og så i den du kalder X.Implement laver en klasse for hver enkelt objekt - igen en Customer klasse, der så implementerer metoderne GetCustomer(int customerId) SetCustomer(int customerId, string customerName)?
Avatar billede arne_v Ekspert
09. november 2011 - 03:30 #15
Den angivene command line build procedure skulle angive hvad der skal hav ref til hvad:

csc /optimize+ /t:library /out:X.dll X.cs
csc /optimize+ /t:library /r:X.dll /out:XImpl.dll XImpl.cs
csc /optimize+ /t:exe /out:XServer.exe XServer.cs
csc /optimize+ /t:exe /r:X.dll /out:XClient.exe XClient.cs

XServer.exe XImpl.dll og X.dll skal saa vaere i samme dir

XClient.exe og X.dll skal saa vaere i samme dir

Og saa skal du lige have VS til at goere det samme !  :-)
Avatar billede arne_v Ekspert
09. november 2011 - 03:31 #16
Jeg ville illustrere hvor nemt det er at skifte implementationen ud.

Det er i konfig filn at URL og klasse linkes:

<wellknown mode="Singleton" type="X.Impl.XRealA, XImpl" objectUri="A"/>
Avatar billede arne_v Ekspert
09. november 2011 - 03:33 #17
Jeg ville nok lave en Customer.dll med:

en ren data klasse Customer
et interface ICustomerManager

og en CustomerImpl.dll med:

en klasse CustomerManager

(erstat "Manager" med "Service" hvis det skal lyde mere moderne)
Avatar billede simsen Mester
11. november 2011 - 08:26 #18
Hej Arne,

Jeg fandt ud af problemet;

I config filen står det således:

<wellknown mode="Singleton" type="X.Impl.XRealA, XImpl" objectUri="A"/>

Der skulle (for mit vedkommende) stå:

<wellknown mode="Singleton" type="X.Impl.XRealA, X.Impl" objectUri="A"/>

Et sidste spørgsmål.....

Jeg har altid lavet en ren dataklasse (og dermed projekt) for sig selv, som jeg så har lavet reference til. Jeg forstår ikke helt hvorfor du vil have dataklassen og interfacen i samme dll?

Og smidt så et svar. Du har bestemt fortjent dine points. Og rigtig mange tak for hjælpen. (Jeg vender nok frygtelig tilbage, når jeg skal have det implementeret til mit eget) :-)
Avatar billede arne_v Ekspert
11. november 2011 - 14:29 #19
De data klasser der er in og out i interfacet og de exceptions som er implicit en del af interfacet er ret taet koblet til interfacet, saa tit giver det mening at bundte dem sammen.
Avatar billede arne_v Ekspert
11. november 2011 - 14:29 #20
og et svar
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