DatagramChannel mit Selector richtig schließen


#1

Guten Tag,

ich habe ein kleiner TFTP-Server geschrieben und dazu ein DatagramChannel mit Selektor geöffnet der die Anfrage auf UDP Port 69 abhört. Nun möchte ich den Server nur zu bestimmten Zeitpunkten im Programm geöffnet haben und jedes mal wenn dieser Programmteil, der den Server benötigt, geöffnet wird, soll ein neues Objekt der Klasse TFTP-Server erzeugt werden.
Die Klasse TFTP-Server erbt von Thread und wird nach Programmteilende geschlossen, nun hatte ich das Problem das dies erst nicht funktioniert hat und bei der Erzeugung eines neuen Objektes wirfte das Objekt ne Exception das der Port 69 schon belegt sei.
also wollte ich den Channel schließen dabei bekomme ich jetzt allerdings diese Exception:
[EVIL]Exception in thread “Thread-7”
java.nio.channels.ClosedSelectorException
at sun.nio.ch.SelectorImpl.selectedKeys(SelectorImpl.java:75)
at tftpserver.TFTPServer.run(TFTPServer.java:78)
[/EVIL]
hier meine TFTPServer-Klasse: (methode killServer() wirft die exception)


	public static final byte OPCODE_READ = 0x01;
	public static final byte OPCODE_WRITE = 0x02;
	public static final byte OPCODE_DATA = 0x03;
	public static final byte OPCODE_ACK = 0x04;
	public static final byte OPCODE_ERROR = 0x05;
	private File filedir = null;
	private boolean kill = false;
	private static final String ERR_ONLYREAD = "only read-requests are allowed";
	private static final String ERR_UNKNOWN = "unknown error";
	private int trancount = 0;
	private final List<TFTPObserver> listeners = new ArrayList<TFTPObserver>();
	private DatagramChannel kanal;
	private Selector selektor;

	public TFTPServer(File filedirectory) {
		this.filedir = filedirectory;
	}

	public TFTPServer(File filedirectory, TFTPObserver listener) {
		this.filedir = filedirectory;
		this.listeners.add(listener);
	}

	private void informNewConnection(int id) {
		for (TFTPObserver observer : listeners) {
			observer.newConnection(id);
		}
	}

	private void informTransferFailed(int id, String filename) {
		for (TFTPObserver observer : listeners) {
			observer.transferFaild(id, filename);
		}
	}

	private void informTransferSuccessful(int id, String filename) {
		for (TFTPObserver observer : listeners) {
			observer.transferSuccessful(id, filename);
		}
	}

	@Override
	public void run() {
		ByteBuffer tftpbuf = ByteBuffer.allocate(600);
		InetSocketAddress isa = null;
		try {
			selektor = Selector.open();
			kanal = DatagramChannel.open();
			kanal.socket().bind(new InetSocketAddress(69));
			kanal.configureBlocking(false);
			kanal.register(selektor, SelectionKey.OP_READ);
			while (!kill) {
				System.out.println("test tftp");
				selektor.select();
				Set<?> auswahl = selektor.selectedKeys();
				Iterator<?> it = auswahl.iterator();
				SelectionKey key = null;
				if (it.hasNext()) {
					key = (SelectionKey) it.next();
					if (key.isReadable()) {
						SocketAddress soa = kanal.receive(tftpbuf);
						if (soa instanceof InetSocketAddress) {
							isa = (InetSocketAddress) soa;
						}
					}
				}
				tftpbuf.position(0);
				decodehead(tftpbuf.array(), isa);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			JOptionPane
					.showConfirmDialog(
							null,
							"UDP-Port 69 is already used - tftp-server is not running!",
							"TrapReceiver Error", JOptionPane.DEFAULT_OPTION,
							JOptionPane.ERROR_MESSAGE, null);
			LOGGER.severe(e.getMessage());
		}
	}

	private void decodehead(byte[] array, InetSocketAddress isa) {
		// TODO Auto-generated method stub
		int opcode = decodeOpcode(array);
		String filename = decodeFilename(array);
		String mode = decodeMode(array);
		if ((opcode == 1) && mode.equalsIgnoreCase("octet")) {
			int id = getID(isa);
			new TFTPConnection(isa, filename, mode, filedir, this, id).start();
			this.informNewConnection(id);
			trancount++;
		} else if ((opcode == 2) && mode.equalsIgnoreCase("octet")) {
			sendError(4, isa, ERR_ONLYREAD.getBytes());
		} else if (!mode.equalsIgnoreCase("octet")) {
			sendError(4, isa, "only octet-mode is available".getBytes());
		} else {
			sendError(0, isa, ERR_UNKNOWN.getBytes());
		}
	}

	private int getID(InetSocketAddress isa) {
		// TODO Auto-generated method stub
		byte[] adr = isa.getAddress().getAddress();
		return (adr[0] & 0xFF) + ((adr[1] & 0xFF) * 2) + ((adr[2] & 0xFF) * 3)
				+ ((adr[3] & 0xFF) * 4) + isa.getPort();
	}

	private String decodeMode(byte[] array) {
		// TODO Auto-generated method stub
		List<Byte> mode = new ArrayList<Byte>();
		boolean ok = false;
		for (int i = 0; i < array.length; i++) {
			if (ok) {
				if ((array[i + 2] != 0x00)) {
					mode.add(array[i + 2]);
				} else {
					break;
				}
			}
			if (array[i + 2] == 0x00) {
				ok = true;
			}
		}
		StringBuilder build = new StringBuilder();
		for (int i = 0; i < mode.size(); i++) {
			build.append((char) mode.get(i).byteValue());
		}
		return build.toString();
	}

	private String decodeFilename(byte[] array) {
		// TODO Auto-generated method stub
		List<Byte> filename = new ArrayList<Byte>();
		for (int i = 0; i < array.length; i++) {
			if (array[i + 2] != 0x00) {
				filename.add(array[i + 2]);
			} else {
				break;
			}
		}
		StringBuilder build = new StringBuilder();
		for (int i = 0; i < filename.size(); i++) {
			build.append((char) filename.get(i).byteValue());
		}
		return build.toString();
	}

	private int decodeOpcode(byte[] array) {
		// TODO Auto-generated method stub
		return array[1];
	}

	private void sendError(int errcode, InetSocketAddress isa, byte[] mess) {
		// TODO Auto-generated method stub
		byte[] paket = new byte[5 + mess.length];
		paket[0] = 0x00;
		paket[1] = OPCODE_ERROR;
		paket[2] = 0x00;
		paket[3] = (byte) errcode;
		for (int i = 0; i < mess.length; i++) {
			paket[i + 4] = mess**;
		}
		paket[paket.length - 1] = 0x00;
		try (DatagramSocket sock = new DatagramSocket()) {
			sock.connect(isa.getAddress(), isa.getPort());
			DatagramPacket udppaket = new DatagramPacket(paket, paket.length,
					isa.getAddress(), isa.getPort());
			sock.send(udppaket);
		} catch (IOException e) {
			LOGGER.severe(e.getMessage());
		}
	}

public void killServer() {
		try {
			kanal.close();
			selektor.close();
			System.out.println("schließe das hier");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			LOGGER.severe(e.getMessage());
		}
		kill = true;
	}

	public File getFiledir() {
		return filedir;
	}

	public void setFiledir(File filedir) {
		this.filedir = filedir;
	}

	@Override
	public void transferFaild(int id, String filename) {
		// TODO Auto-generated method stub
		trancount--;
		this.informTransferFailed(id, filename);
	}

	@Override
	public void transferSuccessful(int id, String filename) {
		// TODO Auto-generated method stub
		trancount--;
		this.informTransferSuccessful(id, filename);
	}

	public int getTrancount() {
		return trancount;
	}

	public void setTrancount(int trancount) {
		this.trancount = trancount;
	}

	@Override
	public void newConnection(int id) {
		// TODO Auto-generated method stub

	}

}```

wie muss ich denn ein DatagramChannel mit Selektor richtig schließen?

edit: 

habe die methode nun umgeschrieben ich schließe nun das socket und den kanal

```	public void killServer() {
		try {
			sock.close();
			kanal.close();
			// selektor.close();
			System.out.println("schließe das hier");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			LOGGER.severe(e.getMessage());
		}
		kill = true;
	}```

damit wird keine exception mehr geworfen, aber mein problem bleibt. bei einer neuen instanz wird wieder gemeldet das der udp 69 belegt ist

*** Edit ***

ok habe es gelöst bekommen...

verwende jetzt die interrupted-methode des threads und selektor.close() musste ich auch noch hinzufügen.

wen es interessiert die zwei methoden (run und killserver() sehen nun so aus):

```    public void killServer() {
        try {
            it.remove();
            auswahl.removeAll(auswahl);
            sock.disconnect();
            kanal.disconnect();
            sock.close();
            kanal.close();
            selektor.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            LOGGER.severe(e.getMessage());
        }
        this.interrupt();
    }```

```    public void run() {
        ByteBuffer tftpbuf = ByteBuffer.allocate(600);
        InetSocketAddress isa = null;
        try {
            selektor = Selector.open();
            kanal = DatagramChannel.open();
            sock = kanal.socket();
            sock.bind(new InetSocketAddress(69));
            kanal.configureBlocking(false);
            kanal.register(selektor, SelectionKey.OP_READ);
            while (!this.isInterrupted()) {
                selektor.select();
                if (selektor.isOpen()) {
                    auswahl = selektor.selectedKeys();
                    it = auswahl.iterator();
                    SelectionKey key = null;
                    if (it.hasNext()) {
                        key = (SelectionKey) it.next();
                        if (key.isReadable()) {
                            SocketAddress soa = kanal.receive(tftpbuf);
                            if (soa instanceof InetSocketAddress) {
                                isa = (InetSocketAddress) soa;
                            }
                            tftpbuf.position(0);
                            decodehead(tftpbuf.array(), isa);
                        }
                    }
                } else {
                    break;
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            JOptionPane
                    .showConfirmDialog(
                            null,
                            "UDP-Port 69 is already used - tftp-server is not running!",
                            "TrapReceiver Error", JOptionPane.DEFAULT_OPTION,
                            JOptionPane.ERROR_MESSAGE, null);
            LOGGER.severe(e.getMessage());
        } finally {
            sock.close();
            try {
                kanal.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                LOGGER.severe(e.getMessage());
            }
        }
    }```