Skip to content

Commit 77e1fca

Browse files
committed
Merge pull request TooTallNate#201 from Davidiusdadi/master
merged bugfixes & FragmentedFramesExample (TooTallNate#194)
2 parents 85390d5 + 6ea3658 commit 77e1fca

File tree

12 files changed

+262
-28
lines changed

12 files changed

+262
-28
lines changed

src/main/example/ChatServer.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import org.java_websocket.WebSocket;
99
import org.java_websocket.WebSocketImpl;
10+
import org.java_websocket.framing.Framedata;
1011
import org.java_websocket.handshake.ClientHandshake;
1112
import org.java_websocket.server.WebSocketServer;
1213

@@ -41,6 +42,11 @@ public void onMessage( WebSocket conn, String message ) {
4142
System.out.println( conn + ": " + message );
4243
}
4344

45+
@Override
46+
public void onFragment( WebSocket conn, Framedata fragment ) {
47+
System.out.println( "received fragment: " + fragment );
48+
}
49+
4450
public static void main( String[] args ) throws InterruptedException , IOException {
4551
WebSocketImpl.DEBUG = true;
4652
int port = 8887; // 843 flash policy port
@@ -56,9 +62,16 @@ public static void main( String[] args ) throws InterruptedException , IOExcepti
5662
while ( true ) {
5763
String in = sysin.readLine();
5864
s.sendToAll( in );
65+
if( in.equals( "exit" ) ) {
66+
s.stop();
67+
break;
68+
} else if( in.equals( "restart" ) ) {
69+
s.stop();
70+
s.start();
71+
break;
72+
}
5973
}
6074
}
61-
6275
@Override
6376
public void onError( WebSocket conn, Exception ex ) {
6477
ex.printStackTrace();

src/main/example/ExampleClient.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.java_websocket.client.WebSocketClient;
55
import org.java_websocket.drafts.Draft;
66
import org.java_websocket.drafts.Draft_10;
7+
import org.java_websocket.framing.Framedata;
78
import org.java_websocket.handshake.ServerHandshake;
89

910
/** This example demonstrates how to create a websocket connection to a server. Only the most important callbacks are overloaded. */
@@ -20,13 +21,17 @@ public ExampleClient( URI serverURI ) {
2021
@Override
2122
public void onOpen( ServerHandshake handshakedata ) {
2223
System.out.println( "opened connection" );
23-
// if you pan to refuse connection based on ip or httpfields overload: onWebsocketHandshakeReceivedAsClient
24+
// if you plan to refuse connection based on ip or httpfields overload: onWebsocketHandshakeReceivedAsClient
2425
}
2526

2627
@Override
2728
public void onMessage( String message ) {
2829
System.out.println( "received: " + message );
29-
// send( "you said: " + message );
30+
}
31+
32+
@Override
33+
public void onFragment( Framedata fragment ) {
34+
System.out.println( "received fragment: " + new String( fragment.getPayloadData().array() ) );
3035
}
3136

3237
@Override
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import java.io.BufferedReader;
2+
import java.io.IOException;
3+
import java.io.InputStreamReader;
4+
import java.net.URI;
5+
import java.net.URISyntaxException;
6+
import java.nio.ByteBuffer;
7+
8+
import org.java_websocket.WebSocket;
9+
import org.java_websocket.client.WebSocketClient;
10+
import org.java_websocket.drafts.Draft_17;
11+
import org.java_websocket.framing.Framedata.Opcode;
12+
13+
/**
14+
* This example shows how to send fragmented frames.<br>
15+
* For information on when to used fragmented frames see http://tools.ietf.org/html/rfc6455#section-5.4<br>
16+
* Fragmented and normal messages can not be mixed.
17+
* One is however allowed to mix them with control messages like ping/pong.
18+
*
19+
* @see WebSocket#sendFragmentedFrame(Opcode, ByteBuffer, boolean)
20+
**/
21+
public class FragmentedFramesExample {
22+
public static void main( String[] args ) throws URISyntaxException , IOException , InterruptedException {
23+
// WebSocketImpl.DEBUG = true; // will give extra output
24+
25+
WebSocketClient websocket = new ExampleClient( new URI( "ws://localhost:8887" ), new Draft_17() ); // Draft_17 is implementation of rfc6455
26+
if( !websocket.connectBlocking() ) {
27+
System.err.println( "Could not connect to the server." );
28+
return;
29+
}
30+
31+
System.out.println( "This example shows how to send fragmented(continuous) messages." );
32+
33+
BufferedReader stdin = new BufferedReader( new InputStreamReader( System.in ) );
34+
while ( websocket.isOpen() ) {
35+
System.out.println( "Please type in a loooooong line(which then will be send in 2 byte fragments):" );
36+
String longline = stdin.readLine();
37+
ByteBuffer longelinebuffer = ByteBuffer.wrap( longline.getBytes() );
38+
longelinebuffer.rewind();
39+
40+
for( int position = 2 ; ; position += 2 ) {
41+
if( position < longelinebuffer.capacity() ) {
42+
longelinebuffer.limit( position );
43+
websocket.sendFragmentedFrame( Opcode.TEXT, longelinebuffer, false );// when sending binary data one should use Opcode.BINARY
44+
assert ( longelinebuffer.remaining() == 0 );
45+
// after calling sendFragmentedFrame one may reuse the buffer given to the method immediately
46+
} else {
47+
longelinebuffer.limit( longelinebuffer.capacity() );
48+
websocket.sendFragmentedFrame( Opcode.TEXT, longelinebuffer, true );// sending the last frame
49+
break;
50+
}
51+
52+
}
53+
System.out.println( "You can not type in the next long message or press Ctr-C to exit." );
54+
}
55+
System.out.println( "FragmentedFramesExample terminated" );
56+
}
57+
}

src/main/java/org/java_websocket/SSLSocketChannel2.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ public class SSLSocketChannel2 implements ByteChannel, WrappedByteChannel {
5656
protected SSLEngineResult readEngineResult;
5757
protected SSLEngineResult writeEngineResult;
5858

59+
/**
60+
* Should be used to count the buffer allocations.
61+
* But because of #190 where HandshakeStatus.FINISHED is not properly returned by nio wrap/unwrap this variable is used to check whether {@link #createBuffers(SSLSession)} needs to be called.
62+
**/
63+
protected int bufferallocations = 0;
64+
5965
public SSLSocketChannel2( SocketChannel channel , SSLEngine sslEngine , ExecutorService exec , SelectionKey key ) throws IOException {
6066
if( channel == null || sslEngine == null || exec == null )
6167
throw new IllegalArgumentException( "parameter must not be null" );
@@ -127,21 +133,23 @@ private synchronized void processHandshake() throws IOException {
127133
}
128134
inData.compact();
129135
unwrap();
130-
if( sslEngine.getHandshakeStatus() == HandshakeStatus.FINISHED ) {
136+
if( readEngineResult.getHandshakeStatus() == HandshakeStatus.FINISHED ) {
131137
createBuffers( sslEngine.getSession() );
132138
return;
133139
}
134140
}
135141
consumeDelegatedTasks();
136-
assert ( sslEngine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING );
137142
if( tasks.isEmpty() || sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP ) {
138143
socketChannel.write( wrap( emptybuffer ) );
139-
if( sslEngine.getHandshakeStatus() == HandshakeStatus.FINISHED ) {
144+
if( writeEngineResult.getHandshakeStatus() == HandshakeStatus.FINISHED ) {
140145
createBuffers( sslEngine.getSession() );
146+
return;
141147
}
142148
}
143-
}
149+
assert ( sslEngine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING );// this function could only leave NOT_HANDSHAKING after createBuffers was called unless #190 occurs which means that nio wrap/unwrap never return HandshakeStatus.FINISHED
144150

151+
bufferallocations = 1; // look at variable declaration why this line exists and #190. Without this line buffers would not be be recreated when #190 AND a rehandshake occur.
152+
}
145153
private synchronized ByteBuffer wrap( ByteBuffer b ) throws SSLException {
146154
outCrypt.compact();
147155
writeEngineResult = sslEngine.wrap( b, outCrypt );
@@ -192,13 +200,18 @@ protected void createBuffers( SSLSession session ) {
192200
inCrypt.flip();
193201
outCrypt.rewind();
194202
outCrypt.flip();
203+
bufferallocations++;
195204
}
196205

197206
public int write( ByteBuffer src ) throws IOException {
198207
if( !isHandShakeComplete() ) {
199208
processHandshake();
200209
return 0;
201210
}
211+
// assert ( bufferallocations > 1 ); //see #190
212+
if( bufferallocations <= 1 ) {
213+
createBuffers( sslEngine.getSession() );
214+
}
202215
int num = socketChannel.write( wrap( src ) );
203216
return num;
204217

@@ -225,6 +238,10 @@ public int read( ByteBuffer dst ) throws IOException {
225238
}
226239
}
227240
}
241+
// assert ( bufferallocations > 1 ); //see #190
242+
if( bufferallocations <= 1 ) {
243+
createBuffers( sslEngine.getSession() );
244+
}
228245
/* 1. When "dst" is smaller than "inData" readRemaining will fill "dst" with data decoded in a previous read call.
229246
* 2. When "inCrypt" contains more data than "inData" has remaining space, unwrap has to be called on more time(readRemaining)
230247
*/
@@ -256,7 +273,6 @@ public int read( ByteBuffer dst ) throws IOException {
256273
}
257274
return transfered;
258275
}
259-
260276
/**
261277
* {@link #read(ByteBuffer)} may not be to leave all buffers(inData, inCrypt)
262278
**/

src/main/java/org/java_websocket/WebSocket.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import org.java_websocket.drafts.Draft;
88
import org.java_websocket.framing.Framedata;
9+
import org.java_websocket.framing.Framedata.Opcode;
910

1011
public interface WebSocket {
1112
public enum Role {
@@ -62,6 +63,21 @@ public enum READYSTATE {
6263

6364
public abstract void sendFrame( Framedata framedata );
6465

66+
/**
67+
* Allows to send continuous/fragmented frames conveniently. <br>
68+
* For more into on this frame type see http://tools.ietf.org/html/rfc6455#section-5.4<br>
69+
*
70+
* If the first frame you send is also the last then it is not a fragmented frame and will received via onMessage instead of onFragmented even though it was send by this method.
71+
*
72+
* @param op
73+
* This is only important for the first frame in the sequence. Opcode.TEXT, Opcode.BINARY are allowed.
74+
* @param buffer
75+
* The buffer which contains the payload. It may have no bytes remaining.
76+
* @param fin
77+
* true means the current frame is the last in the sequence.
78+
**/
79+
public abstract void sendFragmentedFrame( Opcode op, ByteBuffer buffer, boolean fin );
80+
6581
public abstract boolean hasBufferedData();
6682

6783
/**

src/main/java/org/java_websocket/WebSocketImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,11 @@ private void send( Collection<Framedata> frames ) {
580580
}
581581
}
582582

583+
@Override
584+
public void sendFragmentedFrame( Opcode op, ByteBuffer buffer, boolean fin ) {
585+
send( draft.continuousFrame( op, buffer, fin ) );
586+
}
587+
583588
@Override
584589
public void sendFrame( Framedata framedata ) {
585590
if( DEBUG )

0 commit comments

Comments
 (0)