Avatar billede jespersahner Nybegynder
05. februar 2007 - 23:05 Der er 18 kommentarer og
1 løsning

Client-Server problem

Jeg ønsker at etablere en client-server relation mellem to maskiner A og B, hvor A skal fungere som client og B som server.

Idet A har fast/kendt IP-adresse, mens B har dynamisk/ukendt IP-adresse (B kører på et lokalt netværk), etableres forbindelsen ved, at B først kalder A op, hvorefter A kalder B op, idet B's adresse nu er kendt.

Jeg bruger flg. klasser:
1. På A findes flg. klasse, som venter på opkald fra B og derefter etablerer forbindelse til B:

import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ClientA {
   
    public static void main(String[] args) {
        try {
            // Server, venter på opkald:
            ServerSocket serversocket=new ServerSocket(8080);
            Socket socket=serversocket.accept();
            PrintWriter ud=new PrintWriter(socket.getOutputStream());
            ud.println("Hul igennem!");
            ud.flush();
            socket.close();
            // Klient etableres efter modtaget opkald:
            Socket client=new Socket(socket.getInetAddress(),8080);               
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }   
}

2. På B startes en server op:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerB {
   
    public static void main(String[] args) {
        try {
            ServerSocket serversocket=new ServerSocket(8080);
            while (true) {
                Socket socket=serversocket.accept();
                BufferedReader ind=new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String s=ind.readLine();
                System.out.println(s);
                socket.close();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }   
}

3. Fra B kaldes A nu op med:

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

public class ClientB {
   
    public static void main(String[] args) {
        try {
            // Forbind til A:
            Socket socket=new Socket("80.70.60.50",8080);
            InputStream is=socket.getInputStream();
            BufferedReader ind=new BufferedReader(new InputStreamReader(is));
            String s=ind.readLine();
            while (s!=null) {
                System.out.println(s);
                s=ind.readLine();
            }
            socket.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    } 
}

Det går fint med, at B kalder A op, og "Hul igennem!" modtages også på B. Men Socket client=new Socket(socket.getInetAddress(),8080) fejler i ClientA-klassen, dvs. A er ikke i stand til at forbinde til B. Det skal her siges, at B kører på et lokalt netværk, hvilket måske kan give problemer, men "Hul igennem!" modtages omvendt fint på B.

Hvad er årsagen til, at A ikke kan forbinde til B, og hvordan løser jeg dette problem?
Avatar billede arne_v Ekspert
06. februar 2007 - 01:01 #1
hvilken fejl får du ?

en oplagt mulighed var en firewall som tillader connections indefra og ud men ikke
udefra og ind
Avatar billede jespersahner Nybegynder
06. februar 2007 - 01:10 #2
->arne_v: Jeg får fejlen:
java.net.ConnectException: Connection timed out: connect

Jeg undrer mig over, at jeg godt kan modtage "Hul igennem!" som retursvar, når B kalder A op, mens A ikke umiddelbart (selvstændigt) kan forbinde til B - men det har måske noget med firewall'en at gøre, som du skriver.

Men hvis det er tilfældet, kan jeg så på en eller anden måde holde forbindelsen åben, når B kalder A op i stedet for at etablere en ny forbindelse?
Avatar billede arne_v Ekspert
06. februar 2007 - 01:14 #3
det er helt normalt at firewalls restricter connection ikke det at skrive
Avatar billede arne_v Ekspert
06. februar 2007 - 01:15 #4
hvorfor lader du ikke bare din server connecte til client ?

du kan godt lade B være server på applikations niveau men client på TCP niveau
Avatar billede jespersahner Nybegynder
06. februar 2007 - 01:18 #5
->arne_v: Du skriver: "hvorfor lader du ikke bare din server connecte til client ?"
Hvad mener du her? Som jeg forstår det, er det netop det jeg forsøger ved at lade B kalde A op først.
Avatar billede arne_v Ekspert
06. februar 2007 - 01:21 #6
du lader ClientB connecte - jeg ville lade ServerB connecte !

sockets er fuld bi directional
Avatar billede jespersahner Nybegynder
06. februar 2007 - 01:30 #7
->arne_v: Jeg er lidt forvirret her. Hvis ServerB skal connecte, skal det så ske via ServerSocket-objektet? - og hvordan i givet fald?
Avatar billede arne_v Ekspert
06. februar 2007 - 01:37 #8
nej - så skal den bruge Socket og ClientA skal bruge ServerSocket

men efter at connection er etableret bør du kunne præcis det samme koden gør nu
Avatar billede jespersahner Nybegynder
06. februar 2007 - 01:44 #9
->arne_v: Men er client-server forholdet så ikke byttet om? Tanken er jo, at B skal fungere som server og lytte på indkommende beskeder fra A. Derfor skal B vel have ServerSocket til rådighed (?) Hvordan kan B ellers vide, når der er besked fra A?
Avatar billede arne_v Ekspert
06. februar 2007 - 01:46 #10
B ved at der er besked fra A når der kommer en besked på den socket der er connectet til A
Avatar billede arne_v Ekspert
06. februar 2007 - 01:47 #11
du kan tænke på client/server i 2 niveauer:

app level - client sender request til server og får respons tilbage

TCP level - client connecter til server

pointen er at B godt kan være client på TCP level og server på app level samtidigt
Avatar billede jespersahner Nybegynder
06. februar 2007 - 01:55 #12
->arne_v: Jeg er desværre stadig forvirret - men det er også langt over sengetid :-) Hvordan vil du konkret tilrette ClientA og ServerB?
Avatar billede arne_v Ekspert
06. februar 2007 - 02:00 #13
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ClientA {
 
    public static void main(String[] args) {
        try {
            ServerSocket serversocket=new ServerSocket(8080);
            Socket socket=serversocket.accept();
            // send første request til "server"
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    } 
}

og

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerB {
 
    public static void main(String[] args) {
        try {
            Socket socket=new Socket("80.70.60.50",8080);
            // læs første request fra "client"
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    } 
}
Avatar billede jespersahner Nybegynder
06. februar 2007 - 02:05 #14
->arne_v: Ok, men hvordan finder B nu ud af, om der er nye requests, når første request er læst fra A?
Avatar billede arne_v Ekspert
06. februar 2007 - 02:27 #15
den læser bare i en loop

normale reads er blocking så den venter pænt indtil der kommer en besked mere
Avatar billede jespersahner Nybegynder
06. februar 2007 - 16:20 #16
->arne_v: Jeg roder lidt videre med dit forslag og vender tilbage. Du skrev tidligere "pointen er at B godt kan være client på TCP level og server på app level samtidigt". Hvis jeg forstår dig ret, er sagen, at når B først har connect'et til A (på TCP level), er forbindelsen etableret, og både A og B kan så fungere som client hhv. server, jf. din egen kommentar "sockets er fuld bi directional".

Jeg vil dog (måske lidt dumt) spørge videre: Vil etablering af en sådan åben forbindelse belaste den fysiske forbindelse anderledes end en løsning, hvor clienten foretager reconnect hver gang? - altså koster det noget at have forbindelsen stående åben så at sige?
Avatar billede arne_v Ekspert
06. februar 2007 - 16:32 #17
det er langt hurtigere at have en permanent connection end at connecte hver gang

der er dog begraensninger paa hvor mange sockets du kan have aabne paa
et enkelt system
Avatar billede jespersahner Nybegynder
20. februar 2007 - 19:38 #18
->arne_v: Så fik jeg endelig tid til at rode lidt videre med det, og du har jo helt ret i, at det er lige så fint, at lade A fungere som server og B som klient. Når først forbindelsen er etableret, fungerer den begge veje, som du skriver. Da ydermere read er blocking, er det ikke nødvendigt, at B foretager løbende request til A, idet B vil tage imod indlæsning, når den kommer fra A. I den sammenhæng vil B "emulere" den server-funktionalitet jeg går efter: Jeg vil gerne have en løsning, hvor B ikke løbende skal requeste mod A, men hvor A "push'er" information til B.

Smid gerne straks et svar, og tak for hjælpen.
_______________
Note til mig selv:

package util;

import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerA {
   
    public static void main(String[] args) {
        try {
            // Server:
            ServerSocket serversocket=new ServerSocket(8080);
            Socket socket=serversocket.accept();
            PrintWriter ud=new PrintWriter(socket.getOutputStream());
            ud.println("Hul igennem!");
            for (int i=0;i<10;i++) {
                ud.println("Hej "+i);
                ud.flush();
                Util.sleep(5000);
            }           
            socket.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
   
}

package util;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

public class ClientB {
   
    public static void main(String[] args) {
        try {
            Socket socket=new Socket("xx.xx.xx.xx",8080);
            InputStream is=socket.getInputStream();
            BufferedReader ind=new BufferedReader(new InputStreamReader(is));
            String s=ind.readLine();
            while (s!=null) {
                Util.println(s);
                s=ind.readLine();
            }
            socket.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
   
}
Avatar billede arne_v Ekspert
21. februar 2007 - 01:28 #19
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
Kurser inden for grundlæggende programmering

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