GeinMedlem sedan sep. 20005 700 inlägg Har ett program där en klient ansluter till en server, skickar lite data och sedan stänger ner anslutningen. Hur ska servern hantera socket-avslutningar utan att krascha?
Som programmet är skrivet nu så tar servern emot en anslutning och börjar sedan lyssna på inkommande data
line = inputStream.readUTF();
Om klienten bara stänger ner anslutningen med socket.Close() så får jag exception hos servern
java.net.SocketException: Software caused connection abort: recv failed
Jag har lärt mej att Exceptions inte får styra programflödet i en applikation. Hur ska servern hantera detta? Både om klienten avslutar med vilje eller om anslutningen tappas av någon anledning så ska servern kunna återgå till det normala och börja lyssna igen.
En bit ur serverkoden:
sSocket = new ServerSocket(31000);
cSocket = sSocket.accept();
System.out.println("Client connected\n");
inputStream = new DataInputStream(cSocket.getInputStream());
outputStream = new DataOutputStream(cSocket.getOutputStream());
outputStream.writeUTF("Authentication please");
while(line.compareTo("closing") != 0)
{
System.out.println(line);
line = inputStream.readUTF();
if(line.compareTo("user123:secretpassword") == 0)
{
// User authentication OK, start stream
String cAddress = cSocket.getInetAddress().toString();
cAddress = cAddress.substring(1, cAddress.length());
ThreadStream t = new ThreadStream("vfw://0", cAddress, "31000");
//t.activity.start();
}
}
Klienten:
Socket s;
DataOutputStream os = null;
try{
s = new Socket("192.168.0.197", 31000);
os = new DataOutputStream(s.getOutputStream());
os.writeUTF("Hello there...");
os.writeUTF("user123:secretpassword");
while(true){}
}
catch(IOException e){
System.out.println(e);
}
Jag reserverar mej för eventuell fulkod då programmet är långt ifrån färdigt
jmeMedlem sedan maj 20012 237 inlägg Hej!
Just nu har du:
sSocket = new ServerSocket(31000);
cSocket = sSocket.accept();
med ovanstående sätt så stödjer servern bara en connection. När den connection dör så slutar servern ockås att lyssna på nya connections.
Gör så istället att du har cSocket = sSocket.accept() i en loop.
ServerSocket.accept() fungerar så att programmets körning stannar där tills en ny connection tas upp.
Låt servern agera enbart som en lyssnare för nya connections. Låt en annan klass sköta kommunikationen med varje connection.
sSocket = new ServerSocket(30000);
Servern:
while(true)
{
cSocket = sSocket.accept();
//skapa en ny klass som sköter varje ny connection
// om du vill hålla reda på varje öppen connection så kan du lagra den i en Collection eller liknande
vector.add(new ConnectionHandler(cSocket) );
}
ConnectionHandler:
private InputStream in;
private OutputStream out;
private Socket socket;
public ConnectionHandler(Socket pSocket) extends Thread // eller implements Runnable, hur du nu vill ha det
{
socket = pSocket;
out = pSocket.getOutputStream();
in = pSocket.getInputStream();
start();
}
public void run()
{
boolean ok = true;
String input = null;
while(ok)
{
try
{
input = readData(in); // metoden får du skapa själv, dvs läsa in data från strömmen in
// gör vad du vill med datan
// om indatan är "closing" så kan du t ex skicka en quit-message tillbaks till servern och sedan kalla på socket.close();
close();
// om du har lagrat alla ConnectionHandlers i en Vector i server klassen så bör du även informera servern om att en connection är stängd och att servern skall ta bort den från Vectorn
}
catch(Exception e)
{
ok = false;
}
}
}
public void close()
{
try
{
in.close();
out.close();
socket.close();
}
catch(Exception e)
{
}
finally
{
in = null;
out = null;
socket = null;
}
}
GeinMedlem sedan sep. 20005 700 inlägg Tack för ett utmärkt svar! Det där verkar vara en fin lösning, jag ska prova implementera den nu. Jag återkommer med fler frågor här om så dyker upp!
red/ Fast nu har du något förvirrande skrivit extends Thread efter konstruktorn, det är väl ändå fel?
red2/ Du använder också exceptions för att kontrollera programflödet, precis i motsats till det jag har fått lära mej. Hur är det egentligen med det? ;) Eller vid närmare eftertanke så är det ju bara för att se till att anslutningen stängs ner korrekt så, jag köper väl det! :)
LimeMedlem sedan sep. 2001961 inlägg Det finns två sätt att tänka när det gäller objektorienterade språk och exceptions.
1. Det obstinata "jag tänker i C"-sätter som säger att så fort man har ett exception så ska programmet terminera.
2. Det objektorienterade som använder exceptions för att tala om att något är fel.
Själv använder jag otroligt mycket Exceptions i mina program. Visst kan man göra så att man anropar en metod i en klass som returnerar en boolean eller ett annat värde som säger att "det gick bra" och sedan gör man "getResult" på samma instans men det är bättre att då hantera felen när de kommer. Och ett fel markeras genom att man kastar ett exception som den som är ansvarig för att det blev fel skall hantera.
Ett exempel är i en applikation där användaren matar in ett produktnummer. Tillbaka kommer ett objekt Produkt (med ca 100 attribut) OMM det hittas. Om det inte hittas så kastas ett ProductNotFoundException från databaslagret. Då kan man säga det till användaren.
Om det nu råkar vara så att man inte får kontakt med databasen öht så kastar vi ett "DatabaseConnectionFailedException" och då kan man tala om för användaren att "hörrö... har du verkligen kopplat dig mot servern..."
Grejen är att de 8 klasser som ligger mellan inmatningen och databaslagret och som gör en del andra saker (cashning och sånt), de struntar fullständigt i om produkten inte hittas utan vill bara ta hand om resultatet om det kommer något.
Bra exceptionhantering gör mycket så att säga att man inte får använda det för att styra sitt program tycker jag är skitsnack.
jmeMedlem sedan maj 20012 237 inlägg Ojdå, givetvis skall extends Thread skrivas efter klassnamnet och inte efter konstruktören.
I Socket klassen finns metoden isConnected() som man kan kolla ifall man fortfarande är uppkopplad mot servern.
Denna metod kan man kalla i loopen. Ifall man får false så kan man avbryta loopen.
I mitt exempel så använder jag Exception. Du kan givetvis använda en mer specifik Exception, som t ex IOException, som kanske vore bättre i detta fall. Kastas ett IOException i koden så kan man ganska lugnt anta att uppkopplingen mot databasen har skitits sig på nåt sätt. Genom att då sätta ok = false i catch-delen så kan du avbryta iterereingen.
Ett annat sätt vore att stoppa loopen inuti en try-catch sats.
Då behöver du inte ha en boolean flagga som i mitt exempel.
Men då bör alla undantag som kastas vara så pass allvarliga, att loopen inte kan fortsätta.
Sen kan du ju även skapa mer specifika try-catch satser inuti loopen, såna undantag som inte kräver att loopen avbryts.
Jag tycker inte att användningen av en boolean flagga är så fult.
Du ska inte implementera mycket kod i catch/finally delen men att sätta en flagga tycker jag är helt ok.