package javacodebook.net.socket.channelserver; import java.nio.*; import java.io.*; import java.nio.channels.*; import java.nio.charset.*; import java.net.*; import java.util.*; /** * Dieser kleine "Datums-Server" empfängt auf Port 5000 über TCP/IP gesendete * Nachrichten. Wird "date" gesendet, übermittelt der Server dem Sender das * Datum, bei "help" wird eine Kurzbeschreibung der verfügbaren Befehle über- * tragen, und bei "end" wird die Verbindung unterbrochen. Bei allen anderen * Eingaben wird eine Fehlermeldung zurückgeschickt. * Dadurch, das die verwendeten Channel "non-blocking" sind, können mehrere * Anfragen parallel beantwortet werden. */ public class ChannelServer { private static Charset charset = Charset.forName("ISO-8859-1"); private static CharsetEncoder encoder = charset.newEncoder(); private static CharsetDecoder decoder = charset.newDecoder(); private static String command=null; private static String response =null; public static void main(String[] args) throws Exception { // Dieser ByteBuffer wird später zum lesen der Clientanfragen benötigt. ByteBuffer buffer = ByteBuffer.allocateDirect(1024); // Ein Selector wird erstellt. Ein Selector kann von beliebig vielen // Channels bei unterschiedlichen Vorkommnissen informiert werden. Selector selector = Selector.open(); // Ein "non-blocking" ServerSocket wird am Port: 5000 angemeldet ServerSocketChannel ssChannel = ServerSocketChannel.open(); ssChannel.configureBlocking(false); ssChannel.socket().bind(new InetSocketAddress(5000)); // Der Selector wird am ersten Channel registriert. Über das // angegebene Operation bit wird definiert, dass der Selector nur bei // einer neu Anmeldung eines Clients informiert wird. SelectionKey acceptKey=ssChannel.register(selector, SelectionKey.OP_ACCEPT); // Der Server soll immer seinen Diest zur Verfügung stellen, daher // befindet sich der Code in einer Endlos-Schleife. while(true) { // Diese Methode blockt solange bis sich in den Channels, die // diesen selector registriert haben, etwas ereignet hat. selector.select(); // Für den Fall das sich gleich in mehreren angemeldeten Channels // etwas ereignet hat oder auf einem mehrere Anfragen reinkommen, // liefert die Methode selectedKeys() gleich einen SET von keys // zurück. Jeder key kapselt das Ereignis, und kann später nach // diesem befragt werden. Set keys = selector.selectedKeys(); // Jeder key aus dem Set wird abgearbeitet. Iterator i = keys.iterator(); while(i.hasNext()) { SelectionKey key = (SelectionKey) i.next(); // Damit der key beim nächsten select nicht wieder auftaucht, // muss er aus dem Set herausgenommen werden i.remove(); // Liefert der Key bei der Methode isAcceptable() true zurück, // wissen wir, dass es sich um ein Ereignis vom // ServerSocketChannel handelt, und ein neuer Client sich // angemeldet hat. Folgender Block wird dann abgearbeitet. if (key.isAcceptable()) { // Der SocketChannel vom Client wird erfragt. SocketChannel client = ssChannel.accept(); // Dieser Channel soll auch "non-Blocking" sein, damit // mehrere Client Anfragen bearbeitet werden können. client.configureBlocking(false); // Der Selector wird auch am SocketChannel registriert. // Über das angegebene Operation bit, OP_READ, wird // definiert, dass der Selector nur dann vom Channel // informiert wird, wenn es was zu lesen gibt. client.register(selector, SelectionKey.OP_READ); } // Liefert der Key bei der Methode isReadable() true zurück, // wissen wir, dass es sich um ein Ereignis von einem // SocketChannel handelt, und ein bereits angemeldeter Client // Daten geschickt hat, die nun zum Lesen bereit stehen . Folgender // Block wird abgearbeitet. else if (key.isReadable()) { // Eine Referenz auf den SocketChannel wird erfragt SocketChannel client = (SocketChannel) key.channel(); // Die Client-Daten werden in einen Buffer geschrieben. // Falls der Client die Verbindung zwischenzeitlich // unterbrochen hat, würde das Ende vom Stream durch einen // Rückgabewert von -1 identifiziert werden. In diesem // Fall wird der Channel geschlossen. try { int bytesread = client.read(buffer); if (bytesread == -1) { key.cancel(); client.close(); } } catch(Exception err) { err.printStackTrace(); } // Client Eingabe befindet sich derzeit im Buffer, muss // für die weitere Verarbeitung zum String umgewandelt // werden. Der Buffer wird anschließend für spätere // Verwendung wieder geleert. buffer.flip(); CharBuffer charBuffer = decoder.decode(buffer); command= charBuffer.toString(); command = command.trim(); buffer.clear(); // wird beim Befehl "date" ausgeführt if(command.equals("date")) { // Antwort wird als String zusammengesetzt zum // ByteBuffer umgewandelt und zum Client geschickt. response ="Das Datum im Java Format: " + new java.util.Date()+"\n"; client.write(encoder.encode( CharBuffer.wrap(response))); } // wird beim Befehl "help" ausgeführt else if(command.equals("help")) { response = "Befehle: \"date\" liefert Datum, " +"\"help\" fuer Hilfe," +" \"end\" fuer Verbindungsende.\n"; client.write(encoder.encode( CharBuffer.wrap(response))); } // wird beim Befehl "end" ausgeführt else if(command.equals("end")) { System.out.println("Verbindung zu einem Client wird " +"abgebrochen!"); client.close(); } // wird bei allen anderen Fällen ausgeführt. else { response = "Sorry, \""+command +"\" ist falscher Befehl!\n"; client.write(encoder.encode( CharBuffer.wrap(response))); } } } } } }