33import java .io .IOException ;
44import java .nio .ByteBuffer ;
55import java .nio .channels .NotYetConnectedException ;
6+ import java .nio .channels .SelectionKey ;
7+ import java .nio .channels .Selector ;
68import java .nio .channels .SocketChannel ;
79import java .nio .charset .Charset ;
810import java .security .NoSuchAlgorithmException ;
11+ import java .util .concurrent .BlockingQueue ;
912
1013/**
1114 * Represents one end (client or server) of a single WebSocket connection.
@@ -76,6 +79,15 @@ public final class WebSocket {
7679 * The bytes that make up the current text frame being read.
7780 */
7881 private ByteBuffer currentFrame ;
82+ /**
83+ * Queue of buffers that need to be sent to the client.
84+ */
85+ private BlockingQueue <ByteBuffer > bufferQueue ;
86+ /**
87+ * Lock object to ensure that data is sent from the bufferQueue in
88+ * the proper order
89+ */
90+ private Object bufferQueueMutex = new Object ();
7991
8092
8193 // CONSTRUCTOR /////////////////////////////////////////////////////////////
@@ -84,11 +96,16 @@ public final class WebSocket {
8496 * @param socketChannel The <tt>SocketChannel</tt> instance to read and
8597 * write to. The channel should already be registered
8698 * with a Selector before construction of this object.
99+ * @param bufferQueue The Queue that we should use to buffer data that
100+ * hasn't been sent to the client yet.
87101 * @param listener The {@link WebSocketListener} to notify of events when
88102 * they occur.
89103 */
90- WebSocket (SocketChannel socketChannel , WebSocketListener listener ) {
104+ WebSocket (SocketChannel socketChannel , BlockingQueue <ByteBuffer > bufferQueue ,
105+ WebSocketListener listener )
106+ {
91107 this .socketChannel = socketChannel ;
108+ this .bufferQueue = bufferQueue ;
92109 this .handshakeComplete = false ;
93110 this .remoteHandshake = this .currentFrame = null ;
94111 this .buffer = ByteBuffer .allocate (1 );
@@ -133,7 +150,11 @@ public void close() throws IOException {
133150 this .wsl .onClose (this );
134151 }
135152
136- public void send (String text ) throws IOException {
153+ /**
154+ * @return True if all of the text was sent to the client by this thread.
155+ * False if some of the text had to be buffered to be sent later.
156+ */
157+ public boolean send (String text ) throws IOException {
137158 if (!this .handshakeComplete ) throw new NotYetConnectedException ();
138159 if (text == null ) throw new NullPointerException ("Cannot send 'null' data to a WebSocket." );
139160
@@ -143,12 +164,50 @@ public void send(String text) throws IOException {
143164 b .put (START_OF_FRAME );
144165 b .put (textBytes );
145166 b .put (END_OF_FRAME );
167+ b .rewind ();
146168
147- // Write the ByteBuffer to the socket
148- b .rewind ();
149- this .socketChannel .write (b );
169+ // See if we have any backlog that needs to be sent first
170+ if (handleWrite ()) {
171+ // Write the ByteBuffer to the socket
172+ this .socketChannel .write (b );
173+ }
174+
175+ // If we didn't get it all sent, add it to the buffer of buffers
176+ if (b .remaining () > 0 ) {
177+ if (!this .bufferQueue .offer (b )) {
178+ throw new IOException ("Buffers are full, message could not be sent to" +
179+ this .socketChannel .socket ().getRemoteSocketAddress ());
180+ }
181+ return false ;
182+ }
183+
184+ return true ;
150185 }
151186
187+ boolean hasBufferedData () {
188+ return !this .bufferQueue .isEmpty ();
189+ }
190+
191+ /**
192+ * @return True if all data has been sent to the client, false if there
193+ * is still some buffered.
194+ */
195+ boolean handleWrite () throws IOException {
196+ synchronized (this .bufferQueueMutex ) {
197+ ByteBuffer buffer = this .bufferQueue .peek ();
198+ while (buffer != null ) {
199+ this .socketChannel .write (buffer );
200+ if (buffer .remaining () > 0 ) {
201+ return false ; // Didn't finish this buffer. There's more to send.
202+ } else {
203+ this .bufferQueue .poll (); // Buffer finished. Remove it.
204+ buffer = this .bufferQueue .peek ();
205+ }
206+ }
207+ return true ;
208+ }
209+ }
210+
152211 public SocketChannel socketChannel () {
153212 return this .socketChannel ;
154213 }
@@ -255,4 +314,5 @@ private void completeHandshake(byte[] handShakeBody) throws IOException, NoSuchA
255314 close ();
256315 }
257316 }
317+
258318}
0 commit comments