Avatar billede quiw Nybegynder
20. september 2010 - 15:44 Der er 15 kommentarer og
1 løsning

Server + Concurrency.

Jeg er i gang med et projekt, hvor jeg laver en server applikation. Ligesom da jeg troede server applikationen var færdig, testede jeg den med nogle "spam-connections", og fandt naturligvis ud af at jeg overhovedet ikke har taget højde for Concurrency. Efter at skælde mig ud for noget så dumt, så forsøgte jeg at implementere noget synchronize, men uden held. Jeg kan simpelthen ikke gennemskue hvordan det skal se ud i sidste ende.

Eksempel på "Skelettet" i min server appliktion:

--------------------- Main:
package main;

import java.net.*;
import java.util.ArrayList;
import java.io.*;

public class Main{
    private static ServerSocket server;
    private static Socket socket;
    private static ArrayList<Client> clients;
   
    public static void main(String [ ] args){
        clients = new ArrayList<Client>();
        startServer();
    }
    public static void startServer(){
        try{
            server = new ServerSocket(4444);
        }catch(Exception e){
            System.out.println("Could not create socket.");
            System.exit(1);
        }
       
        while(true){
            try{
                addClient();
            }catch(IOException e){
                System.out.println("Could not accept socket.");
                System.exit(1);
            }
        }
    }
    public static ArrayList<Client> getClients(){
        return clients;
    }
    public static void removeClient(Client c){
        clients.remove(c);
    }
    public static void addClient() throws IOException{
        socket = server.accept();
        Client client = new Client(socket);
        clients.add(client);
        client.start();
    }
}

--------------------- Client:
package main;

import java.net.Socket;

public class Client extends Thread{
    private Socket socket;
   
    public Client(Socket s){
        this.socket = s;
    }
    public void run(){
        System.out.println("User connected.");
       
        //Send besked til alle online clients
        for(Client c : Main.getClients()){
            c.send("Hello.");
        }
       
        System.out.println("User disconnected.");
        Main.removeClient(this);
    }
    public void send(String msg){
        // send besked til socket.
    }
}


Hvis flere clienter connecter på samme tid, så får jeg naturligvis:

Exception in thread "Thread-17" java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
    at java.util.AbstractList$Itr.next(Unknown Source)
    at main.Client.run(Client.java:13)

Nogle der har et forslag til hvordan jeg kan implementere en løsning? På forhånd tak.
Avatar billede arne_v Ekspert
20. september 2010 - 15:54 #1
Alle steder hvor du bruger clients put:

synchronized(client) {

}

omkring koden.
Avatar billede arne_v Ekspert
20. september 2010 - 15:54 #2
Jeg synes ioevrigt ikke at det er paent med saa meget static !!
Avatar billede quiw Nybegynder
20. september 2010 - 15:59 #3
Det har du fuldstændigt ret i. Men ved ikke lige hvordan jeg ellers udfører funktioner fra Main inde fra Clients, er ikke så øvet i java. :)
Avatar billede arne_v Ekspert
20. september 2010 - 16:05 #4
Du kunne sende en ref tilk instans af Main med over i Client constructor .
Avatar billede quiw Nybegynder
20. september 2010 - 16:13 #5
Altså, lave en Client(Socket s, Main m); og starte Client i main: Client(socket, this);? Det gjorde jeg i den tidligere version, men af en underlig grund, så gjorde det mere skade end gavn, derfor jeg kom på at bruge static i stedet. Men vil lige undersøge igen, vender tilbage.
Avatar billede quiw Nybegynder
20. september 2010 - 16:31 #6
Jeg har forsøgt at pakke Client ind i synchronized, hver gang jeg benytter ArrayList clients, men jeg får stadig en stor del concurrency exceptions:

Exception in thread "Thread-200" java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
    at java.util.AbstractList$Itr.next(Unknown Source)
    at main.Client.run(Client.java:18)



Koden ser nu således ud:

----------------------- Main:
package main;

import java.net.*;
import java.util.ArrayList;
import java.io.*;

public class Main{
    private ServerSocket server;
    private Socket socket;
    private ArrayList<Client> clients;
   
    public Main(){
        clients = new ArrayList<Client>();
    }
   
    public void startServer(){
        try{
            server = new ServerSocket(4444);
        }catch(Exception e){
            System.out.println("Could not create socket.");
            System.exit(1);
        }
       
        while(true){
            try{
                Client client = new Client(socket, this);
                socket = server.accept();
               
                synchronized(client) {
                    clients.add(client);
                    client.start();
                }
            }catch(IOException e){
                System.out.println("Could not accept socket.");
                System.exit(1);
            }
        }
    }
    public ArrayList<Client> getClients(){
        synchronized(clients) {
            return clients;
        }
    }
    public void removeClient(Client c){
        synchronized(c) {
            clients.remove(c);
        }
    }
}

----------------- Clients:
package main;

import java.net.Socket;

public class Client extends Thread{
    private Socket socket;
    private Main main;
   
    public Client(Socket s, Main m){
        this.socket = s;
        this.main = m;
    }
    public void run(){
        System.out.println("User connected.");
       
        //Send besked til alle online clients
       
        for(Client c : main.getClients()){
            synchronized(c) {
                c.send("Hello.");
            }
        }
       
        System.out.println("User disconnected.");
        synchronized(this) {
            main.removeClient(this);
        }
    }
    public void send(String msg){
        // send besked til socket.
    }
}
Avatar billede arne_v Ekspert
20. september 2010 - 16:46 #7
Min fejl.

Heg skrev:

synchronized(client) {

}

det skulle vaere:

synchronized(clients) {

}
Avatar billede quiw Nybegynder
20. september 2010 - 17:58 #8
Jeg forstår dog ikke hvorfor at jeg bør:
synchronized(main.getClients()) {
    for(Client c : main.getClients()){
        c.send("Hello.");
    }
}

Når jeg inde i main allerede har beskyttet getClients():
public ArrayList<Client> getClients(){
    synchronized(clients) {
            return clients;
    }
}
Avatar billede arne_v Ekspert
20. september 2010 - 18:13 #9
for(Client c : main.getClients()){
        c.send("Hello.");
    }

fungerer som:

    ArrayList<Client> lst = main.getClients();
    for(Client c : lst) {
        c.send("Hello.");
    }

d.v.s. at synchronized indeni getClients metoden beskytter ikke mod at listen aendres mens for loekken koerer
Avatar billede quiw Nybegynder
20. september 2010 - 23:48 #10
Mange tak! Det virker efter hensigten. Smid gerne et svar.
- Som et lille ekstraspørgsmål, jeg database klasse, bør jeg lave 1 instans der håndtere alle databaseforespørgslerne, eller bør jeg lave en instans per klient/tråd der kører?
Avatar billede arne_v Ekspert
21. september 2010 - 01:01 #11
En instans af server klasse for hver port der lyttes på (ofte kun 1).

En instans af client klasse for hver connection.
Avatar billede arne_v Ekspert
21. september 2010 - 01:02 #12
svar
Avatar billede quiw Nybegynder
21. september 2010 - 01:14 #13
Og kan det passe at synchronized gør sådan, at jeg kun kan have 50 aktive brugere online på samme tid, så "låser" serveren?
Avatar billede arne_v Ekspert
21. september 2010 - 01:25 #14
Nej - ikke hvis det er lavet rigtigt.

Du vil dog opleve at:
- mange tråde (lad os sige >200) kører langsommere fordi der er administrations overhead på tråde
Avatar billede quiw Nybegynder
21. september 2010 - 18:00 #15
Så har jeg tydeligvis ikke lavet det rigtigt, men jeg kigger på det. Tak for hjælpen!
Avatar billede arne_v Ekspert
20. november 2010 - 01:24 #16
så mangler du bare at acceptere svaret
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