|
21 | 21 | import java.util.Set; |
22 | 22 | import java.util.concurrent.BlockingQueue; |
23 | 23 | import java.util.concurrent.LinkedBlockingQueue; |
| 24 | +import java.util.concurrent.atomic.AtomicBoolean; |
24 | 25 | import java.util.concurrent.atomic.AtomicInteger; |
25 | 26 |
|
26 | 27 | import org.java_websocket.SocketChannelIOHelper; |
@@ -69,6 +70,8 @@ public abstract class WebSocketServer extends WebSocketAdapter implements Runnab |
69 | 70 |
|
70 | 71 | private Thread selectorthread; |
71 | 72 |
|
| 73 | + private volatile AtomicBoolean isclosed = new AtomicBoolean( false ); |
| 74 | + |
72 | 75 | private List<WebSocketWorker> decoders; |
73 | 76 |
|
74 | 77 | private BlockingQueue<WebSocketImpl> oqueue; |
@@ -167,26 +170,53 @@ public void start() { |
167 | 170 |
|
168 | 171 | /** |
169 | 172 | * Closes all connected clients sockets, then closes the underlying |
170 | | - * ServerSocketChannel, effectively killing the server socket selectorthread and |
171 | | - * freeing the port the server was bound to. |
| 173 | + * ServerSocketChannel, effectively killing the server socket selectorthread, |
| 174 | + * freeing the port the server was bound to and stops all internal workerthreads. |
| 175 | + * |
| 176 | + * If this method is called before the server is started it will never start. |
| 177 | + * |
| 178 | + * Therefore |
| 179 | + * |
| 180 | + * Additionally |
| 181 | + * |
| 182 | + * @param timeout |
| 183 | + * Specifies how many milliseconds shall pass between initiating the close handshakes with the connected clients and closing the servers socket channel. |
172 | 184 | * |
173 | 185 | * @throws IOException |
174 | 186 | * When {@link ServerSocketChannel}.close throws an IOException |
175 | 187 | * @throws InterruptedException |
176 | 188 | */ |
177 | | - public void stop() throws IOException , InterruptedException { |
| 189 | + public void stop( int timeout ) throws IOException , InterruptedException { |
| 190 | + if( !isclosed.compareAndSet( false, true ) ) { |
| 191 | + return; |
| 192 | + } |
| 193 | + |
178 | 194 | synchronized ( connections ) { |
179 | 195 | for( WebSocket ws : connections ) { |
180 | 196 | ws.close( CloseFrame.NORMAL ); |
181 | 197 | } |
182 | 198 | } |
183 | | - selectorthread.interrupt(); |
184 | | - selectorthread.join(); |
185 | | - for( WebSocketWorker w : decoders ) { |
186 | | - w.interrupt(); |
| 199 | + synchronized ( this ) { |
| 200 | + if( selectorthread != null ) { |
| 201 | + if( Thread.currentThread() != selectorthread ) { |
| 202 | + |
| 203 | + } |
| 204 | + selectorthread.interrupt(); |
| 205 | + selectorthread.join(); |
| 206 | + } |
| 207 | + if( decoders != null ) { |
| 208 | + for( WebSocketWorker w : decoders ) { |
| 209 | + w.interrupt(); |
| 210 | + } |
| 211 | + } |
| 212 | + if( server != null ) { |
| 213 | + server.close(); |
| 214 | + } |
187 | 215 | } |
188 | | - this.server.close(); |
| 216 | + } |
189 | 217 |
|
| 218 | + public void stop() throws IOException , InterruptedException { |
| 219 | + stop( 0 ); |
190 | 220 | } |
191 | 221 |
|
192 | 222 | /** |
@@ -237,6 +267,9 @@ public void run() { |
237 | 267 | if( selectorthread != null ) |
238 | 268 | throw new IllegalStateException( getClass().getName() + " can only be started once." ); |
239 | 269 | selectorthread = Thread.currentThread(); |
| 270 | + if( isclosed.get() ) { |
| 271 | + return; |
| 272 | + } |
240 | 273 | } |
241 | 274 | selectorthread.setName( "WebsocketSelector" + selectorthread.getId() ); |
242 | 275 | try { |
|
0 commit comments