[Java] Multithreaded SocketServer

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Dit is een van mijn eerste projectjes in Java, en ik probeer een multithreaded socketserver te maken.
Ik loop enkel tegen wat probleempjes,

1: Wanneer een client de verbinding verbreekt, stop ik de while-lus, en haal ik de verbinding uit de connectionTable, echter blijft mijn thread nu nog levendig? En hoe zet ik die op stop?

2: Wanneer een client de verbinding verbreekt krijg ik een 'null', hij zegt "I Got: null" en de lijn eronder "Client dropped", normaal gezien wordt de while lus toch verbroken voordat hij aan I got geraakt.

3: Als ik iets stuur en druk op enter zegt hij "i got: ..." Dit is dus ok, maar wanneer ik de tweede maal wat stuur zegt hij niet, de derde maal zegt hij weer "i got:..." dus op de oneven verzonden berichten reageerd hij... Hoe komt dit?

Alvast bedankt voor de hulp!

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import java.net.*;
import java.io.*;
import java.util.*;

public class SocketServer 
{
    //Server Variables
    static Vector connectionTable;
    static int nextConnectionID = 1;

    public SocketServer(int port)
    {
        ServerSocket socketServer = null;
        Socket socket = null;
        connectionTable = new Vector();

        try
        {
            socketServer = new ServerSocket(port);
            while ((socket = socketServer.accept()) != null)
            {
                ClientThread now;
                Thread current = new Thread(now = new ClientThread(socket));
                current.setDaemon(true);
                connectionTable.addElement(now);
                current.start();
            }
        }
        catch (Exception e)
        {
            System.err.println(e);
            System.exit(1);
        }
    }
    
}

class ClientThread extends Thread
{
    //Thread Variables
    private Socket linkTo;      //The Socket
    private int connectionID;
    private String line;

    public ClientThread(Socket from)
    {
        connectionID = SocketServer.nextConnectionID++;
        linkTo = from;
        System.out.println(linkTo);
        System.out.println(connectionID);
        ClientIn();
    }
    
    public void ClientIn()
    {
        while(true)
        {
            try
            {
                // Get input from the client
                DataInputStream in = new DataInputStream (linkTo.getInputStream());
                if(in.readLine() == null){
                    System.out.println("Client dropped!");
                    linkTo.close();
                    SocketServer.connectionTable.removeElement(this);
                    break;
                    //Client stoppen
                }
                else
                {
                    System.out.println("I got:" + in.readLine());
                    
                }
            } catch (IOException ioe) {
                System.out.println("IOException on socket listen: " + ioe);
                ioe.printStackTrace();
            }
        }
    }
}

Acties:
  • 0 Henk 'm!

  • momania
  • Registratie: Mei 2000
  • Laatst online: 17-09 07:50

momania

iPhone 30! Bam!

Ten eerste is het geen goed idee om Thread te extenden of om zelf threads te starten. Beter is het om Runnable te implementeren en een threadpool te gebruiken. In je huidige code verlies je ook de referenties naar je threads en kan je ze dus nooit stoppen. ( de while(true) {} is ook nogal nasty btw ;) )

Ten tweede raad ik je aan om dit stap voor stap te doen. Kijk eerst hoe threads werken en krijg dat onder de knie. Kijk dan hoe sockets (en vooral input- en outputstreams) werken en probeer het daarna pas te combineren. :)

Neem je whisky mee, is het te weinig... *zucht*


Acties:
  • 0 Henk 'm!

Verwijderd

1: Wanneer een client de verbinding verbreekt, stop ik de while-lus, en haal ik de verbinding uit de connectionTable, echter blijft mijn thread nu nog levendig? En hoe zet ik die op stop?
De garbage collector ruimt alles op dat niet door een actieve thread benaderd kan worden. Als je thread is beindigd (de run methode is afgelopen), en je hebt geen verwijzigen meer naar de thread, dan gaat de rest vanzelf.
2: Wanneer een client de verbinding verbreekt krijg ik een 'null', hij zegt "I Got: null" en de lijn eronder "Client dropped", normaal gezien wordt de while lus toch verbroken voordat hij aan I got geraakt.

3: Als ik iets stuur en druk op enter zegt hij "i got: ..." Dit is dus ok, maar wanneer ik de tweede maal wat stuur zegt hij niet, de derde maal zegt hij weer "i got:..." dus op de oneven verzonden berichten reageerd hij... Hoe komt dit?
Beide problemen hebben dezelfde oorzaak. Elke keer als je in.readLine() aanroept krijg je de volgende regel uit de stream. Aangezien je hem 2x aanroept krijg je twee verschillende regels. Doe dus eerst 'String regel = in.readLine()', en controleer vervolgens of 'regel' null is, en print 'regel' als dat niet het geval is.

Overigens kan je je '... = new DataInputStream(...)' naar buiten je while loop verplaatsen.

Acties:
  • 0 Henk 'm!

  • Remus
  • Registratie: Juli 2000
  • Laatst online: 15-08-2021
Het grootste probleem is dat je constructor de method ClientIn() aanroept. Deze methode bevat de meerderheid van het werk van je thread. Dus bij de aanroep van de constructor wordt het werk uitgevoerd op de huidige thread, niet in een nieuwe thread.

Iets vergelijkbaars doe je met je SocketServer: in de constructor wordt een wait-loop gedaan: dat hoort niet je hoort dergelijke dingen buiten je constructor te houden. Constructors zijn er voor het initialiseren van de state van het object, niet voor de uitvoering van de taken van het object.

Plaats je catch van IOExceptions buiten de loop, break na ontvangst of gooi hem verder na afdrukken. Nu loopt je loop door.

Als je Thread override (wat niet aangeraden wordt), of een Runnable implementeert dient de activiteit in de run() method geïmplementeerd te worden.

Waarom maak je je clientThreads daemon threads? Daemon threads zijn normaal gesproken voor bepaalde 'service' threads. Normaal gesproken eindigt de JVM pas als alle threads beëindigd zijn, daarbij worden daemon threads genegeerd.

Verder is het volgens de java code convention om methodes met een kleine letter te laten beginnen. ClientIn() dus met een kleine letter beginnen.

Even je punten langs gaan:
1) Hoe constateer je dat de thread nog leeft? Aangezien je run van Thread niet override houdt deze er direct mee op.

2) Dit komt doordat je catch van IOExceptions in de loop staat en de exceptions niet doorgooit of een break doet na ontvangst.

3) Moeilijk te zeggen. Het lijkt mij iig sterk.

Acties:
  • 0 Henk 'm!

  • nxt
  • Registratie: November 2001
  • Laatst online: 24-08 15:34

nxt

Remus schreef op zondag 13 december 2009 @ 10:14:
3) Moeilijk te zeggen. Het lijkt mij iig sterk.
Dat komt omdat hij 2x in.readLine() aanroept, en maar 1 van de twee wordt naar de console gestuurd, vandaar dat de helft van de input verloren gaat ;).

Acties:
  • 0 Henk 'm!

  • kunnen
  • Registratie: Februari 2004
  • Niet online
Overigens lijkt de regel
Java:
1
        connectionID = SocketServer.nextConnectionID++; 

niet thread-safe.

Acties:
  • 0 Henk 'm!

  • Remus
  • Registratie: Juli 2000
  • Laatst online: 15-08-2021
nxt schreef op maandag 14 december 2009 @ 00:16:
[...]

Dat komt omdat hij 2x in.readLine() aanroept, en maar 1 van de twee wordt naar de console gestuurd, vandaar dat de helft van de input verloren gaat ;).
Ah duh! Tsja, ik had de code blijkbaar niet heel erg aandachtig bekeken :)

Acties:
  • 0 Henk 'm!

  • Remus
  • Registratie: Juli 2000
  • Laatst online: 15-08-2021
ThomasB schreef op maandag 14 december 2009 @ 00:28:
Overigens lijkt de regel
Java:
1
        connectionID = SocketServer.nextConnectionID++; 

niet thread-safe.
Dat zou nu geen probleem moeten zijn, aangezien die regel in de constructor staat en deze in de huidige opzet alleen vanuit één thread wordt aangeroepen (namelijk de accept thread). Zodra de constructor van diverse threads aangeroepen gaat worden wordt het idd wel een probleem.

Acties:
  • 0 Henk 'm!

  • XiniX88
  • Registratie: December 2006
  • Laatst online: 19:30
http://java.sun.com/devel...les/Networking/Webserver/

Mooi voorbeeld (is dan wel een webserver, maar tracht hetzelfde te doen als jij hier wil doen bewerkstelligen)

http://java.sun.com/devel.../Webserver/WebServer.java

Daar de source. Het enige wat hier extra in zit is een klein stukje HTTP protocol. Mooie hiervan is dat het thread safe is, de threads op de juiste manier runt, en ook de verschillende threads netjes bijhoud.

[ Voor 15% gewijzigd door XiniX88 op 14-12-2009 12:42 ]

Pagina: 1