/** * Closes all connected clients sockets, then closes the underlying ServerSocketChannel, * effectively killing the server socket selectorthread, freeing the port the server was bound to * and stops all internal workerthreads. * * <p>If this method is called before the server is started it will never start. * * @param timeout Specifies how many milliseconds shall pass between initiating the close * handshakes with the connected clients and closing the servers socket channel. * @throws IOException When {@link ServerSocketChannel}.close throws an IOException * @throws InterruptedException */ public void stop(int timeout) throws IOException, InterruptedException { if (!isclosed.compareAndSet(false, true)) { return; } synchronized (connections) { for (WebSocket ws : connections) { ws.close(CloseFrame.GOING_AWAY); } } synchronized (this) { if (selectorthread != null) { if (Thread.currentThread() != selectorthread) {} if (selectorthread != Thread.currentThread()) { selectorthread.interrupt(); selectorthread.join(); } } if (decoders != null) { for (WebSocketWorker w : decoders) { w.interrupt(); } } if (server != null) { server.close(); } } }
private void handleFatal(WebSocket conn, RuntimeException e) { onError(conn, e); try { selector.close(); } catch (IOException e1) { onError(null, e1); } for (WebSocketWorker w : decoders) { w.interrupt(); } }
// Runnable IMPLEMENTATION ///////////////////////////////////////////////// public void run() { synchronized (this) { if (selectorthread != null) throw new IllegalStateException(getClass().getName() + " can only be started once."); selectorthread = Thread.currentThread(); if (isclosed.get()) { return; } } selectorthread.setName("WebsocketSelector" + selectorthread.getId()); try { server = ServerSocketChannel.open(); server.configureBlocking(false); ServerSocket socket = server.socket(); socket.setReceiveBufferSize(WebSocketImpl.RCVBUF); socket.bind(address); selector = Selector.open(); server.register(selector, server.validOps()); } catch (IOException ex) { handleFatal(null, ex); return; } try { while (!selectorthread.isInterrupted()) { SelectionKey key = null; WebSocketImpl conn = null; try { selector.select(); Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> i = keys.iterator(); while (i.hasNext()) { key = i.next(); if (!key.isValid()) { // Object o = key.attachment(); continue; } if (key.isAcceptable()) { if (!onConnect(key)) { key.cancel(); continue; } SocketChannel channel = server.accept(); channel.configureBlocking(false); WebSocketImpl w = wsf.createWebSocket(this, drafts, channel.socket()); w.key = channel.register(selector, SelectionKey.OP_READ, w); w.channel = wsf.wrapChannel(channel, w.key); i.remove(); allocateBuffers(w); continue; } if (key.isReadable()) { conn = (WebSocketImpl) key.attachment(); ByteBuffer buf = takeBuffer(); try { if (SocketChannelIOHelper.read(buf, conn, conn.channel)) { if (buf.hasRemaining()) { conn.inQueue.put(buf); queue(conn); i.remove(); if (conn.channel instanceof WrappedByteChannel) { if (((WrappedByteChannel) conn.channel).isNeedRead()) { iqueue.add(conn); } } } else pushBuffer(buf); } else { pushBuffer(buf); } } catch (IOException e) { pushBuffer(buf); throw e; } } if (key.isWritable()) { conn = (WebSocketImpl) key.attachment(); if (SocketChannelIOHelper.batch(conn, conn.channel)) { if (key.isValid()) key.interestOps(SelectionKey.OP_READ); } } } while (!iqueue.isEmpty()) { conn = iqueue.remove(0); WrappedByteChannel c = ((WrappedByteChannel) conn.channel); ByteBuffer buf = takeBuffer(); try { if (SocketChannelIOHelper.readMore(buf, conn, c)) iqueue.add(conn); if (buf.hasRemaining()) { conn.inQueue.put(buf); queue(conn); } else { pushBuffer(buf); } } catch (IOException e) { pushBuffer(buf); throw e; } } } catch (CancelledKeyException e) { // an other thread may cancel the key } catch (ClosedByInterruptException e) { return; // do the same stuff as when InterruptedException is thrown } catch (IOException ex) { if (key != null) key.cancel(); handleIOException(key, conn, ex); } catch (InterruptedException e) { return; // FIXME controlled shutdown (e.g. take care of buffermanagement) } } } catch (RuntimeException e) { // should hopefully never occur handleFatal(null, e); } finally { if (decoders != null) { for (WebSocketWorker w : decoders) { w.interrupt(); } } if (server != null) { try { server.close(); } catch (IOException e) { onError(null, e); } } } }