/**
   * 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();
      }
    }
  }
  /**
   * Creates a WebSocketServer that will attempt to bind/listen on the given <var>address</var>, and
   * comply with <tt>Draft</tt> version <var>draft</var>.
   *
   * @param address The address (host:port) this server should listen on.
   * @param decodercount The number of {@link WebSocketWorker}s that will be used to process the
   *     incoming network data. By default this will be <code>
   *     Runtime.getRuntime().availableProcessors()</code>
   * @param drafts The versions of the WebSocket protocol that this server instance should comply
   *     to. Clients that use an other protocol version will be rejected.
   * @param connectionscontainer Allows to specify a collection that will be used to store the
   *     websockets in. <br>
   *     If you plan to often iterate through the currently connected websockets you may want to use
   *     a collection that does not require synchronization like a {@link CopyOnWriteArraySet}. In
   *     that case make sure that you overload {@link #removeConnection(WebSocket)} and {@link
   *     #addConnection(WebSocket)}.<br>
   *     By default a {@link HashSet} will be used.
   * @see #removeConnection(WebSocket) for more control over syncronized operation
   * @see <a href="https://github.com/TooTallNate/Java-WebSocket/wiki/Drafts" > more about drafts
   */
  public WebSocketServer(
      InetSocketAddress address,
      int decodercount,
      List<Draft> drafts,
      Collection<WebSocket> connectionscontainer) {
    if (address == null || decodercount < 1 || connectionscontainer == null) {
      throw new IllegalArgumentException(
          "address and connectionscontainer must not be null and you need at least 1 decoder");
    }

    if (drafts == null) this.drafts = Collections.emptyList();
    else this.drafts = drafts;

    this.address = address;
    this.connections = connectionscontainer;

    oqueue = new LinkedBlockingQueue<WebSocketImpl>();
    iqueue = new LinkedList<WebSocketImpl>();

    decoders = new ArrayList<WebSocketWorker>(decodercount);
    buffers = new LinkedBlockingQueue<ByteBuffer>();
    for (int i = 0; i < decodercount; i++) {
      WebSocketWorker ex = new WebSocketWorker();
      decoders.add(ex);
      ex.start();
    }
  }
Example #3
0
 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();
   }
 }
Example #4
0
  public WebSocketServer(InetSocketAddress address, int decodercount, List<Draft> drafts) {
    if (drafts == null) this.drafts = Collections.emptyList();
    else this.drafts = drafts;
    setAddress(address);

    oqueue = new LinkedBlockingQueue<WebSocketImpl>();

    decoders = new ArrayList<WebSocketWorker>(decodercount);
    buffers = new LinkedBlockingQueue<ByteBuffer>();
    for (int i = 0; i < decodercount; i++) {
      WebSocketWorker ex = new WebSocketWorker();
      decoders.add(ex);
      ex.start();
    }
  }
  // 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);
        }
      }
    }
  }