Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Jetty/Feature/WebSockets"

< Jetty‎ | Feature
m
 
(10 intermediate revisions by 4 users not shown)
Line 1: Line 1:
 +
{{Jetty Feature
 +
| introduction =
  
== Getting Started With WebSockets==
+
{{Jetty TODO}}
  
The [http://en.wikipedia.org/wiki/WebSockets WebSockets] protocol and API is an emerging standard that seeks to provide high-quality, bidirectional communication between a browser (or other web client) and a server.  The goal is to eventually replace Comet techniques like long polling.   Jetty has supported the various WebSocket drafts in the 7.x and 8.x releases.   
+
The [http://en.wikipedia.org/wiki/WebSockets WebSockets] protocol and API is an emerging standard that seeks to provide high-quality, bidirectional communication between a browser (or other web client) and a server. The goal is to eventually replace Comet techniques like long polling. Jetty has supported the various WebSocket drafts in the 7.x and 8.x releases.   
  
 
==You don’t want to do this!==
 
==You don’t want to do this!==
  
This doc shows you how to use WebSockets from the lowest levels, but we would not advise that any application programmer should follow these examples to build an application.   WebSockets is not a silver bullet, and on it’s own will never be simple to use for nontrivial applications (see [http://webtide.intalio.com/2011/04/is-websocket-chat-simpler/ Is WebSocket Chat Simpler?]), so we recommend that application programmers look toward frameworks like [http://cometd.org Cometd], that private a higher level of abstraction, hide the technicalities, and allow you to use either Comet long polling or WebSockets transparently.
+
This document shows you how to use WebSockets from the levels closest to the machine; it targets framework developers who want to use WebSockets, and application developers who can't stand not knowing what is under the hood. However, application programmers should not follow these examples to build an application. WebSockets is not a silver bullet, and on its own will never be simple to use for nontrivial applications (see [http://webtide.intalio.com/2011/04/is-websocket-chat-simpler/ Is WebSocket Chat Simpler?]). We recommend that you look toward frameworks like [http://cometd.org Cometd], that private a higher level of abstraction, hide the technicalities, and allow you to use either Comet long polling or WebSockets transparently.
  
This piece targets framework developers who want to use WebSockets in their own frameworks,  and application developers who can’t stand not knowing what is under the hood.
+
| body =
  
 
==Test Client and Server==
 
==Test Client and Server==
  
The simplest way to get started is to download a Jetty aggregate jar that comes complete with a test WebSocket client and server.  You can do this with a browser or with the following command line wgets:
+
The simplest way to get started is to download 3 jars: The Jetty aggregate Jar contains all of jetty; the jetty-websocket test jar that contains a client and server; and the servlet jar. You can do this using a browser or with the following command line wgets:
  
 
<source lang="bash">
 
<source lang="bash">
  
wget -O jetty-all.jar --user-agen=demo \
+
wget -O jetty-all.jar --user-agent=demo \
    http://repo2.maven.org/maven2/org/eclipse/jetty/aggregate/jally-all/7.4.0.v20110414/jetty-all-7.4.0.v20110414.jar
+
  http://repo2.maven.org/maven2/org/eclipse/jetty/aggregate/jetty-all/7.6.2.v20120308/jetty-all-7.6.2.v20120308.jar
 +
wget -O jetty-websocket-tests.jar --user-agent=demo \
 +
  http://repo2.maven.org/maven2/org/eclipse/jetty/jetty-websocket/7.6.2.v20120308/jetty-websocket-7.6.2.v20120308-tests.jar
 
wget --user-agent=demo \
 
wget --user-agent=demo \
 
     http://repo2.maven.org/maven2/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar
 
     http://repo2.maven.org/maven2/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar
Line 23: Line 27:
 
</source>
 
</source>
  
To run a simple test server (use –help to see more options):
+
To run a simple test server (use <tt>–help</tt> to see more options):
  
 
<source lang="java">
 
<source lang="java">
  
  java -cp jetty-all.jar:servlet-api-2.5.jar \
+
  java -cp jetty-websocket-tests.jar:jetty-all.jar:servlet-api-2.5.jar \
 
   org.eclipse.jetty.websocket.TestServer \
 
   org.eclipse.jetty.websocket.TestServer \
 
   --port 8080 \
 
   --port 8080 \
Line 35: Line 39:
 
</source>
 
</source>
  
You can test the server with the test client (use –help to see more options):
+
{{note|
 +
'''Windows Users Take Note:''' 
 +
 
 +
The above example command line is for Unix/Linux/OSX, change the above example's use of "<tt>:</tt>" to "<tt>;</tt>" for the command to work in Windows.
 +
}}
 +
 
 +
You can test the server with the test client (use <tt>–help</tt> to see more options):
  
 
<source lang="java">
 
<source lang="java">
  
  java -cp jetty-all.jar:servlet-api-2.5.jar \
+
  java -cp jetty-websocket-tests.jar:jetty-all.jar:servlet-api-2.5.jar \
 
   org.eclipse.jetty.websocket.TestClient \
 
   org.eclipse.jetty.websocket.TestClient \
 
   --port 8080 \
 
   --port 8080 \
Line 46: Line 56:
 
</source>
 
</source>
  
The output from the test client is similar to ping. You can use the options you discover by using –help to try out different types of tests, including fragmentation and aggregation of WebSocket frames
+
{{note|
 +
'''Windows Users Take Note:''' 
 +
 
 +
The above example command line is for Unix/Linux/OSX, change the above example's use of "<tt>:</tt>" to "<tt>;</tt>" for the command to work in Windows.
 +
}}
 +
 
 +
The output from the test client is similar to ping. You can use the options you discover by using <tt>–help</tt> to try out different types of tests, including fragmentation and aggregation of WebSocket frames.
  
 
==Using a Browser==
 
==Using a Browser==
  
Using a Java client is not much use wunless you want to write a desktop application that uses WebSocket (a viable use).  But most users of WebSocket want to use the browser as a client.  So point your browser at the TestServer at http://localhost:8080.
+
Using a Java client is not compelling unless you want to write a desktop application that uses WebSocket (a viable idea).  But most WebSocket users want to use the browser as a client.  So point your browser at the TestServer at http://localhost:8080.
  
The WebSocket TestServer also runs an HTTP file server at the directory given by –docroot, so in this case you should see in the browser a listing of the directory in which you ran the test server.
+
The WebSocket TestServer also runs an HTTP file server at the directory given by <tt>–docroot</tt>, so in this case you should see in the browser a listing of the directory in which you ran the test server.
  
 
To turn the browser into a WebSocket client, you need to serve some HTML and Javascript that executes in the browser and talks back to the server using Websockets.   
 
To turn the browser into a WebSocket client, you need to serve some HTML and Javascript that executes in the browser and talks back to the server using Websockets.   
Create the file index.html in the same directory you ran the server from  
+
# Create the file <tt>index.html</tt> in the same directory you ran the server from.
Put into it the following contents which you can download from [http://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/tags/jetty-7.4.0.v20110414/jetty-websocket/src/test/webapp/index.html here]. This index file contains the HTML, CSS and Javascript for a basic chat room.
+
# Put into it the following contents which you can download from [http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/plain/jetty-websocket/src/test/webapp/index.html jetty-websocket/src/test/webapp/index.html]. This index file contains the HTML, CSS and Javascript for a basic chat room.
  
 
You should now be able to point your browser(s) at the test server, see a chat room, and join it.  If your browser does not support WebSockets, you’ll receive a warning.
 
You should now be able to point your browser(s) at the test server, see a chat room, and join it.  If your browser does not support WebSockets, you’ll receive a warning.
Line 62: Line 78:
 
==Understanding How the Client Works==
 
==Understanding How the Client Works==
  
The initial HTML view has a prompt for a user name.  When a you enter a name, the system calls the join method, which creates the WebSocket to the server.  The URI for the WebSocket derives from the document's location. Call back functions are registered for open, message and close events.  The org.ietf.websocket.test-echo-broadcast subprotocol is specified as this echoes all received messages to all other broadcast connections, providing the semantic a chat room requires:
+
The initial HTML view has a prompt for a user name.  When a you enter a name, the system calls the <tt>join</tt> method, which creates the WebSocket to the server.  The URI for the WebSocket derives from the document's location. Call back functions are registered for open, message and close events.  The <tt>org.ietf.websocket.test-echo-broadcast</tt> subprotocol is specified as this echoes all received messages to all other broadcast connections, providing the semantic a chat room requires:
 
+
<source lang="Java">
+
 
+
<font face="monospace">join<font color="#339933"><nowiki>:</nowiki></font> '''<font color="#003366">function</font>'''<font color="#009900">(</font><font color="#000066">name</font><font color="#009900">)</font> <font color="#009900">{</font>
+
  '''<font color="#000066">this</font>'''._username<font color="#339933"><nowiki>=</nowiki></font><font color="#000066">name</font><font color="#339933"><nowiki>;</nowiki></font>
+
  '''<font color="#003366">var</font>''' location <font color="#339933"><nowiki>=</nowiki></font> document.<font color="#660066">location</font>.<font color="#660066">toString</font><font color="#009900">(</font><font color="#009900">)</font>.<font color="#660066">replace</font><font color="#009900">(</font><font color="#3366CC">'http://'</font><font color="#339933">,</font><font color="#3366CC">'ws://'</font><font color="#009900">)</font>.<font color="#660066">replace</font><font color="#009900">(</font><font color="#3366CC">'https://'</font><font color="#339933">,</font><font color="#3366CC">'wss://'</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
  '''<font color="#000066">this</font>'''._ws<font color="#339933"><nowiki>=</nowiki></font>'''<font color="#003366">new</font>''' WebSocket<font color="#009900">(</font>location<font color="#339933">,</font><font color="#3366CC">"org.ietf.websocket.test-echo-broadcast"</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
  '''<font color="#000066">this</font>'''._ws.<font color="#660066">onopen</font><font color="#339933"><nowiki>=</nowiki></font>'''<font color="#000066">this</font>'''._onopen<font color="#339933"><nowiki>;</nowiki></font>
+
  '''<font color="#000066">this</font>'''._ws.<font color="#660066">onmessage</font><font color="#339933"><nowiki>=</nowiki></font>'''<font color="#000066">this</font>'''._onmessage<font color="#339933"><nowiki>;</nowiki></font>
+
  '''<font color="#000066">this</font>'''._ws.<font color="#660066">onclose</font><font color="#339933"><nowiki>=</nowiki></font>'''<font color="#000066">this</font>'''._onclose<font color="#339933"><nowiki>;</nowiki></font>
+
<font color="#009900">}</font><font color="#339933">,</font></font>
+
 
+
</source>
+
  
 
<source lang="java">
 
<source lang="java">
  
oin: function(name) {
+
join: function(name) {
 
   this._username=name;
 
   this._username=name;
 
   var location = document.location.toString().replace('http://','ws://').replace('https://','wss://');
 
   var location = document.location.toString().replace('http://','ws://').replace('https://','wss://');
Line 90: Line 93:
 
</source>
 
</source>
  
When the WebSocket successfully connects to the server, it calls the onopen callback, which changes the appearance of the chat room to prompt for a chat message.  It also sends a message saying the user has joined the room:
+
When the WebSocket successfully connects to the server, it calls the <tt>onopen</tt> callback, which changes the appearance of the chat room to prompt for a chat message.  It also sends a message saying the user has joined the room:
  
 
<source lang="java">
 
<source lang="java">
  
<font face="monospace">_onopen<font color="#339933"><nowiki>:</nowiki></font> '''<font color="#003366">function</font>'''<font color="#009900">(</font><font color="#009900">)</font><font color="#009900">{</font>
 
  $<font color="#009900">(</font><font color="#3366CC">'join'</font><font color="#009900">)</font>.<font color="#660066">className</font><font color="#339933"><nowiki>=</nowiki></font><font color="#3366CC">'hidden'</font><font color="#339933"><nowiki>;</nowiki></font>
 
  $<font color="#009900">(</font><font color="#3366CC">'joined'</font><font color="#009900">)</font>.<font color="#660066">className</font><font color="#339933"><nowiki>=</nowiki></font><font color="#3366CC"><nowiki>''</nowiki></font><font color="#339933"><nowiki>;</nowiki></font>
 
  $<font color="#009900">(</font><font color="#3366CC">'phrase'</font><font color="#009900">)</font>.<font color="#000066">focus</font><font color="#009900">(</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
 
  room._send<font color="#009900">(</font>room._username<font color="#339933">,</font><font color="#3366CC">'has joined!'</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
 
<font color="#009900">}</font><font color="#339933">,</font></font>
 
 
</source>
 
 
<source lang="java">
 
 
_onopen: function(){
 
_onopen: function(){
 
   $('join').className='hidden';
 
   $('join').className='hidden';
Line 112: Line 105:
 
</source>
 
</source>
  
STo send a message, you format a string as “username:chat text” and then call the Websocket send method:
+
To send a message, you format a string as ''username:chat text'' and then call the WebSocket <tt>send</tt> method:
 
+
<source lang="java">
+
 
+
<font face="monospace">_send<font color="#339933"><nowiki>:</nowiki></font> '''<font color="#003366">function</font>'''<font color="#009900">(</font>user<font color="#339933">,</font>message<font color="#009900">)</font><font color="#009900">{</font>
+
  user<font color="#339933"><nowiki>=</nowiki></font>user.<font color="#660066">replace</font><font color="#009900">(</font><font color="#3366CC">':'</font><font color="#339933">,</font><font color="#3366CC">'_'</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
  '''<font color="#000066">if</font>''' <font color="#009900">(</font>'''<font color="#000066">this</font>'''._ws<font color="#009900">)</font>
+
    '''<font color="#000066">this</font>'''._ws.<font color="#660066">send</font><font color="#009900">(</font>user<font color="#339933">+</font><font color="#3366CC">':'</font><font color="#339933">+</font>message<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
<font color="#009900">}</font><font color="#339933">,</font>
+
 
+
chat<font color="#339933"><nowiki>:</nowiki></font> '''<font color="#003366">function</font>'''<font color="#009900">(</font>text<font color="#009900">)</font> <font color="#009900">{</font>
+
  '''<font color="#000066">if</font>''' <font color="#009900">(</font>text <font color="#339933"><nowiki>!=</nowiki></font> '''<font color="#003366">null</font>''' <font color="#339933">&</font>amp<font color="#339933"><nowiki>;&</nowiki></font>amp<font color="#339933"><nowiki>;</nowiki></font> text.<font color="#660066">length</font><font color="#339933">&</font>gt<font color="#339933"><nowiki>;</nowiki></font><font color="#CC0000">0</font> <font color="#009900">)</font>
+
      room._send<font color="#009900">(</font>room._username<font color="#339933">,</font>text<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
<font color="#009900">}</font><font color="#339933">,</font></font>
+
 
+
</source>
+
  
 
<source lang="java">
 
<source lang="java">
Line 144: Line 122:
 
</source>
 
</source>
  
When the browser receives a WebSocket message over the connection, the system calls the onmessage callback with a message object. The Jetty implementation looks for  the username and colon, strips out any markup, and then appends the message to the chat room:
+
When the browser receives a WebSocket message over the connection, the system calls the <tt>onmessage</tt> callback with a message object. The Jetty implementation looks for  the username and colon, strips out any markup, and then appends the message to the chat room:
 
+
<source lang="java">
+
 
+
<font face="monospace">_onmessage<font color="#339933"><nowiki>:</nowiki></font> '''<font color="#003366">function</font>'''<font color="#009900">(</font>m<font color="#009900">)</font> <font color="#009900">{</font>
+
  '''<font color="#000066">if</font>''' <font color="#009900">(</font>m.<font color="#660066">data</font><font color="#009900">)</font><font color="#009900">{</font>
+
    '''<font color="#003366">var</font>''' c<font color="#339933"><nowiki>=</nowiki></font>m.<font color="#660066">data</font>.<font color="#660066">indexOf</font><font color="#009900">(</font><font color="#3366CC">':'</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    '''<font color="#003366">var</font>''' from<font color="#339933"><nowiki>=</nowiki></font>m.<font color="#660066">data</font>.<font color="#660066">substring</font><font color="#009900">(</font><font color="#CC0000">0</font><font color="#339933">,</font>c<font color="#009900">)</font>.<font color="#660066">replace</font><font color="#009900">(</font><font color="#3366CC">'&lt;'</font><font color="#339933">,</font><font color="#3366CC">'&lt;'</font><font color="#009900">)</font>.<font color="#660066">replace</font><font color="#009900">(</font><font color="#3366CC">'&gt;'</font><font color="#339933">,</font><font color="#3366CC">'&gt;'</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    '''<font color="#003366">var</font>''' text<font color="#339933"><nowiki>=</nowiki></font>m.<font color="#660066">data</font>.<font color="#660066">substring</font><font color="#009900">(</font>c<font color="#339933">+</font><font color="#CC0000">1</font><font color="#009900">)</font>.<font color="#660066">replace</font><font color="#009900">(</font><font color="#3366CC">'&lt;'</font><font color="#339933">,</font><font color="#3366CC">'&lt;'</font><font color="#009900">)</font>.<font color="#660066">replace</font><font color="#009900">(</font><font color="#3366CC">'&gt;'</font><font color="#339933">,</font><font color="#3366CC">'&gt;'</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    '''<font color="#003366">var</font>''' chat<font color="#339933"><nowiki>=</nowiki></font>$<font color="#009900">(</font><font color="#3366CC">'chat'</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    '''<font color="#003366">var</font>''' spanFrom <font color="#339933"><nowiki>=</nowiki></font> document.<font color="#660066">createElement</font><font color="#009900">(</font><font color="#3366CC">'span'</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    spanFrom.<font color="#660066">className</font><font color="#339933"><nowiki>=</nowiki></font><font color="#3366CC">'from'</font><font color="#339933"><nowiki>;</nowiki></font>
+
    spanFrom.<font color="#660066">innerHTML</font><font color="#339933"><nowiki>=</nowiki></font>from<font color="#339933">+</font><font color="#3366CC">': '</font><font color="#339933"><nowiki>;</nowiki></font>
+
    '''<font color="#003366">var</font>''' spanText <font color="#339933"><nowiki>=</nowiki></font> document.<font color="#660066">createElement</font><font color="#009900">(</font><font color="#3366CC">'span'</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    spanText.<font color="#660066">className</font><font color="#339933"><nowiki>=</nowiki></font><font color="#3366CC">'text'</font><font color="#339933"><nowiki>;</nowiki></font>
+
    spanText.<font color="#660066">innerHTML</font><font color="#339933"><nowiki>=</nowiki></font>text<font color="#339933"><nowiki>;</nowiki></font>
+
    '''<font color="#003366">var</font>''' lineBreak <font color="#339933"><nowiki>=</nowiki></font> document.<font color="#660066">createElement</font><font color="#009900">(</font><font color="#3366CC">'br'</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    chat.<font color="#660066">appendChild</font><font color="#009900">(</font>spanFrom<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    chat.<font color="#660066">appendChild</font><font color="#009900">(</font>spanText<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    chat.<font color="#660066">appendChild</font><font color="#009900">(</font>lineBreak<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    chat.<font color="#660066">scrollTop</font> <font color="#339933"><nowiki>=</nowiki></font> chat.<font color="#660066">scrollHeight</font> <font color="#339933">-</font> chat.<font color="#660066">clientHeight</font><font color="#339933"><nowiki>;</nowiki></font>
+
  <font color="#009900">}</font>
+
<font color="#009900">}</font><font color="#339933">,</font></font>
+
 
+
</source>
+
  
 
<source lang="java">
 
<source lang="java">
Line 194: Line 148:
 
</source>
 
</source>
  
If the server closes the connection, or if the browser times it out, then the onclose callback is called.  This nulls out the chat room and reverts to the starting position:
+
If the server closes the connection, or if the browser times it out, then the <tt>onclose</tt> callback is called.  This nulls out the chat room and reverts to the starting position:
  
 
<source lang="java">
 
<source lang="java">
  
  <font face="monospace">_onclose<font color="#339933"><nowiki>:</nowiki></font> '''<font color="#003366">function</font>'''<font color="#009900">(</font>m<font color="#009900">)</font> <font color="#009900">{</font>
+
  onclose: function(m) {
  '''<font color="#000066">this</font>'''._ws<font color="#339933"><nowiki>=</nowiki></font>'''<font color="#003366">null</font>'''<font color="#339933"><nowiki>;</nowiki></font>
+
  this._ws=null;
  $<font color="#009900">(</font><font color="#3366CC">'join'</font><font color="#009900">)</font>.<font color="#660066">className</font><font color="#339933"><nowiki>=</nowiki></font><font color="#3366CC"><nowiki>''</nowiki></font><font color="#339933"><nowiki>;</nowiki></font>
+
  $('join').className='';
  $<font color="#009900">(</font><font color="#3366CC">'joined'</font><font color="#009900">)</font>.<font color="#660066">className</font><font color="#339933"><nowiki>=</nowiki></font><font color="#3366CC">'hidden'</font><font color="#339933"><nowiki>;</nowiki></font>
+
  $('joined').className='hidden';
  $<font color="#009900">(</font><font color="#3366CC">'username'</font><font color="#009900">)</font>.<font color="#000066">focus</font><font color="#009900">(</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
  $('username').focus();
  $<font color="#009900">(</font><font color="#3366CC">'chat'</font><font color="#009900">)</font>.<font color="#660066">innerHTML</font><font color="#339933"><nowiki>=</nowiki></font><font color="#3366CC"><nowiki>''</nowiki></font><font color="#339933"><nowiki>;</nowiki></font>
+
  $('chat').innerHTML='';
<font color="#009900">}</font></font>
+
}
  
 
</source>
 
</source>
Line 210: Line 164:
 
==Understanding How the Server Works==
 
==Understanding How the Server Works==
  
The [http://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/tags/jetty-7.4.0.v20110414/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/TestServer.java server side code] for  this chat room uses an embedded Jetty server and is written against the Jetty WebSocket APIs that are not part of the WebSocket standard.  There is not yet even a proposed standard for serverside WebSocket APIs, but it is a topic for consideration with the servlet 3.1 JSR.
+
The [http://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/tags/jetty-7.4.0.v20110414/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/TestServer.java server side code] for  this chat room uses an embedded Jetty server and is written against the Jetty WebSocket APIs that are not part of the WebSocket standard. There is not yet even a proposed standard for serverside WebSocket APIs, but it is a topic for consideration with the servlet 3.1 JSR.
  
 
The test server is an extension of an [http://wiki.eclipse.org/Jetty/Tutorial/Embedding_Jetty embedded Jetty server], and the constructor adds a [http://download.eclipse.org/jetty/7.4.0.v20110414/xref/org/eclipse/jetty/server/nio/SelectChannelConnector.html connector] at the required port, creates a [http://download.eclipse.org/jetty/7.4.0.v20110414/xref/org/eclipse/jetty/websocket/WebSocketHandler.html WebSocketHandler] and a [http://download.eclipse.org/jetty/7.4.0.v20110414/xref/org/eclipse/jetty/server/handler/ResourceHandler.html ResourceHandler] and chains them together:
 
The test server is an extension of an [http://wiki.eclipse.org/Jetty/Tutorial/Embedding_Jetty embedded Jetty server], and the constructor adds a [http://download.eclipse.org/jetty/7.4.0.v20110414/xref/org/eclipse/jetty/server/nio/SelectChannelConnector.html connector] at the required port, creates a [http://download.eclipse.org/jetty/7.4.0.v20110414/xref/org/eclipse/jetty/websocket/WebSocketHandler.html WebSocketHandler] and a [http://download.eclipse.org/jetty/7.4.0.v20110414/xref/org/eclipse/jetty/server/handler/ResourceHandler.html ResourceHandler] and chains them together:
Line 216: Line 170:
 
<source lang="java">
 
<source lang="java">
  
  <font face="monospace">'''<font color="#000000">public</font>''' TestServer<font color="#009900">(</font>'''<font color="#000066">int</font>''' port<font color="#009900">)</font>
+
  public TestServer(int port)
<font color="#009900">{</font>
+
{
    _connector <font color="#339933"><nowiki>=</nowiki></font> '''<font color="#000000">new</font>''' SelectChannelConnector<font color="#009900">(</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    _connector = new SelectChannelConnector();
    _connector.<font color="#006633">setPort</font><font color="#009900">(</font>port<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    _connector.setPort(port);
    addConnector<font color="#009900">(</font>_connector<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    addConnector(_connector);
   
+
   
    _wsHandler <font color="#339933"><nowiki>=</nowiki></font> '''<font color="#000000">new</font>''' WebSocketHandler<font color="#009900">(</font><font color="#009900">)</font>
+
    _wsHandler = new WebSocketHandler()
    <font color="#009900">{</font>
+
    {
        '''<font color="#000000">public</font>''' WebSocket doWebSocketConnect<font color="#009900">(</font>HttpServletRequest request, <font color="#003399">String</font> protocol<font color="#009900">)</font>
+
        public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
        <font color="#009900">{</font>
+
        {
            ...
+
            ...
            '''<font color="#000000">return</font>''' _websocket<font color="#339933"><nowiki>;</nowiki></font>
+
            return _websocket;
        <font color="#009900">}</font>
+
        }
    <font color="#009900">}</font><font color="#339933"><nowiki>;</nowiki></font>
+
    };
    setHandler<font color="#009900">(</font>_wsHandler<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    setHandler(_wsHandler);
   
+
   
    _rHandler<font color="#339933"><nowiki>=</nowiki></font>'''<font color="#000000">new</font>''' ResourceHandler<font color="#009900">(</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    _rHandler=new ResourceHandler();
    _rHandler.<font color="#006633">setDirectoriesListed</font><font color="#009900">(</font>'''<font color="#000066">true</font>'''<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    _rHandler.setDirectoriesListed(true);
    _rHandler.<font color="#006633">setResourceBase</font><font color="#009900">(</font>_docroot<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    _rHandler.setResourceBase(_docroot);
    _wsHandler.<font color="#006633">setHandler</font><font color="#009900">(</font>_rHandler<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    _wsHandler.setHandler(_rHandler);
<font color="#009900">}</font></font>
+
}
  
 
</source>
 
</source>
  
The resource handler is responsible for serving the static content like HTML and javascript.  The WebSocketHandler looks for WebSocket handshake request and handles them by calling the doWebSocketConnect method, which we have extended to create a WebSocket depending on the sub protocol passed:
+
The resource handler is responsible for serving the static content like HTML and Javascript. The WebSocketHandler looks for WebSocket handshake requests and handles them by calling the <tt>doWebSocketConnect</tt> method, which we have extended to create a WebSocket depending on the sub protocol passed:
  
 
<source lang="java">
 
<source lang="java">
  
<font face="monospace">_wsHandler <font color="#339933"><nowiki>=</nowiki></font> '''<font color="#000000">new</font>''' WebSocketHandler<font color="#009900">(</font><font color="#009900">)</font>
+
_wsHandler = new WebSocketHandler()
<font color="#009900">{</font>
+
{
    '''<font color="#000000">public</font>''' WebSocket doWebSocketConnect<font color="#009900">(</font>HttpServletRequest request, <font color="#003399">String</font> protocol<font color="#009900">)</font>
+
    public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
    <font color="#009900">{</font>
+
    {
        '''<font color="#000000">if</font>''' <font color="#009900">(</font><font color="#0000ff">"org.ietf.websocket.test-echo"</font>.<font color="#006633">equals</font><font color="#009900">(</font>protocol<font color="#009900">)</font> <font color="#339933"><nowiki>||</nowiki></font> <font color="#0000ff">"echo"</font>.<font color="#006633">equals</font><font color="#009900">(</font>protocol<font color="#009900">)</font> <font color="#339933"><nowiki>||</nowiki></font> <font color="#0000ff">"lws-mirror-protocol"</font>.<font color="#006633">equals</font><font color="#009900">(</font>protocol<font color="#009900">)</font><font color="#009900">)</font>
+
        if ("org.ietf.websocket.test-echo".equals(protocol) || "echo".equals(protocol) || "lws-mirror-protocol".equals(protocol))
            _websocket <font color="#339933"><nowiki>=</nowiki></font> '''<font color="#000000">new</font>''' TestEchoWebSocket<font color="#009900">(</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
            _websocket = new TestEchoWebSocket();
        '''<font color="#000000">else</font>''' '''<font color="#000000">if</font>''' <font color="#009900">(</font><font color="#0000ff">"org.ietf.websocket.test-echo-broadcast"</font>.<font color="#006633">equals</font><font color="#009900">(</font>protocol<font color="#009900">)</font><font color="#009900">)</font>
+
        else if ("org.ietf.websocket.test-echo-broadcast".equals(protocol))
            _websocket <font color="#339933"><nowiki>=</nowiki></font> '''<font color="#000000">new</font>''' TestEchoBroadcastWebSocket<font color="#009900">(</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
            _websocket = new TestEchoBroadcastWebSocket();
        '''<font color="#000000">else</font>''' '''<font color="#000000">if</font>''' <font color="#009900">(</font><font color="#0000ff">"org.ietf.websocket.test-echo-assemble"</font>.<font color="#006633">equals</font><font color="#009900">(</font>protocol<font color="#009900">)</font><font color="#009900">)</font>
+
        else if ("org.ietf.websocket.test-echo-assemble".equals(protocol))
            _websocket <font color="#339933"><nowiki>=</nowiki></font> '''<font color="#000000">new</font>''' TestEchoAssembleWebSocket<font color="#009900">(</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
            _websocket = new TestEchoAssembleWebSocket();
        '''<font color="#000000">else</font>''' '''<font color="#000000">if</font>''' <font color="#009900">(</font><font color="#0000ff">"org.ietf.websocket.test-echo-fragment"</font>.<font color="#006633">equals</font><font color="#009900">(</font>protocol<font color="#009900">)</font><font color="#009900">)</font>
+
        else if ("org.ietf.websocket.test-echo-fragment".equals(protocol))
            _websocket <font color="#339933"><nowiki>=</nowiki></font> '''<font color="#000000">new</font>''' TestEchoFragmentWebSocket<font color="#009900">(</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
            _websocket = new TestEchoFragmentWebSocket();
        '''<font color="#000000">else</font>''' '''<font color="#000000">if</font>''' <font color="#009900">(</font>protocol<font color="#339933"><nowiki>==</nowiki></font>'''<font color="#000066">null</font>'''<font color="#009900">)</font>
+
        else if (protocol==null)
            _websocket <font color="#339933"><nowiki>=</nowiki></font> '''<font color="#000000">new</font>''' TestWebSocket<font color="#009900">(</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
            _websocket = new TestWebSocket();
        '''<font color="#000000">return</font>''' _websocket<font color="#339933"><nowiki>;</nowiki></font>
+
        return _websocket;
    <font color="#009900">}</font>
+
    }
<font color="#009900">}</font><font color="#339933"><nowiki>;</nowiki></font></font>
+
};
  
 
</source>
 
</source>
 
+
A simplification of the test [http://download.eclipse.org/jetty/7.4.0.v20110414/xref/org/eclipse/jetty/websocket/WebSocket.html WebSocket] from the test server follows. It excludes the shared code for the other protocols supported. Like the Javascript API, there is an <tt>onOpen, onClose</tt> and <tt>onMessage</tt> callback. The <tt>onOpen</tt> callback is passed in a [http://download.eclipse.org/jetty/7.4.0.v20110414/xref/org/eclipse/jetty/websocket/WebSocket.html#107 Connection] instance that sends messages. The implementation of <tt>onOpen</tt> adds the WebSocket to a collection of all known WebSockets, and <tt>onClose</tt> removes the WebSocket. The implementation of <tt>onMessage</tt> is to simply iterate through that collection and to send the received message to each WebSocket:
Below is a simplification of the test [http://download.eclipse.org/jetty/7.4.0.v20110414/xref/org/eclipse/jetty/websocket/WebSocket.html WebSocket] from the test server, that excludes the shared code for the other protocols supported. Like the javascript API, there is an onOpen,onClose and onMessage callback. The onOpen callback is passed in a [http://download.eclipse.org/jetty/7.4.0.v20110414/xref/org/eclipse/jetty/websocket/WebSocket.html#107 Connection] instance that is used to send messages. The implementation of onOpen adds the websocket to a collection of all known websockets, and onClose is used to remove the websocket. The implementation of onMessage is to simply iterate through that collection and to send the received message to each websocket:
+
  
 
<source lang="java">
 
<source lang="java">
  
<font face="monospace">ConcurrentLinkedQueue _broadcast <font color="#339933"><nowiki>=</nowiki></font>
+
ConcurrentLinkedQueue _broadcast =
    '''<font color="#000000">new</font>''' ConcurrentLinkedQueue<font color="#009900">(</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
    new ConcurrentLinkedQueue();
'''<font color="#000000">class</font>''' TestEchoBroadcastWebSocket '''<font color="#000000">implements</font>''' WebSocket.<font color="#006633">OnTextMessage</font>
+
class TestEchoBroadcastWebSocket implements WebSocket.OnTextMessage
<font color="#009900">{</font>
+
{
    '''<font color="#000000">protected</font>''' <font color="#003399">Connection</font> _connection<font color="#339933"><nowiki>;</nowiki></font>
+
    protected Connection _connection;
   
+
   
    '''<font color="#000000">public</font>''' '''<font color="#000066">void</font>''' onOpen<font color="#009900">(</font><font color="#003399">Connection</font> connection<font color="#009900">)</font>
+
    public void onOpen(Connection connection)
    <font color="#009900">{</font>
+
    {
        _connection<font color="#339933"><nowiki>=</nowiki></font>connection<font color="#339933"><nowiki>;</nowiki></font>
+
        _connection=connection;
        _broadcast.<font color="#006633">add</font><font color="#009900">(</font>'''<font color="#000000">this</font>'''<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
        _broadcast.add(this);
    <font color="#009900">}</font>
+
    }
    '''<font color="#000000">public</font>''' '''<font color="#000066">void</font>''' onClose<font color="#009900">(</font>'''<font color="#000066">int</font>''' code,<font color="#003399">String</font> message<font color="#009900">)</font>
+
    public void onClose(int code,String message)
    <font color="#009900">{</font>
+
    {
        _broadcast.<font color="#006633">remove</font><font color="#009900">(</font>'''<font color="#000000">this</font>'''<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
        _broadcast.remove(this);
    <font color="#009900">}</font>
+
    }
    '''<font color="#000000">public</font>''' '''<font color="#000066">void</font>''' onMessage<font color="#009900">(</font>'''<font color="#000000">final</font>''' <font color="#003399">String</font> data<font color="#009900">)</font>
+
    public void onMessage(final String data)
    <font color="#009900">{</font>
+
    {
        '''<font color="#000000">for</font>''' <font color="#009900">(</font>TestEchoBroadcastWebSocket ws <font color="#339933"><nowiki>:</nowiki></font> _broadcast<font color="#009900">)</font>
+
        for (TestEchoBroadcastWebSocket ws : _broadcast)
        <font color="#009900">{</font>
+
        {
            '''<font color="#000000">try</font>'''
+
            try
            <font color="#009900">{</font>
+
            {
                ws._connection.<font color="#006633">sendMessage</font><font color="#009900">(</font>data<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
                ws._connection.sendMessage(data);
            <font color="#009900">}</font>
+
            }
            '''<font color="#000000">catch</font>''' <font color="#009900">(</font><font color="#003399">IOException</font> e<font color="#009900">)</font>
+
            catch (IOException e)
            <font color="#009900">{</font>
+
            {
                _broadcast.<font color="#006633">remove</font><font color="#009900">(</font>ws<font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
                _broadcast.remove(ws);
                e.<font color="#006633">printStackTrace</font><font color="#009900">(</font><font color="#009900">)</font><font color="#339933"><nowiki>;</nowiki></font>
+
                e.printStackTrace();
            <font color="#009900">}</font>
+
            }
        <font color="#009900">}</font>
+
        }
    <font color="#009900">}</font>
+
    }
<font color="#009900">}</font></font>
+
}
  
 
</source>
 
</source>
Line 304: Line 257:
 
==Don’t do it this way!==
 
==Don’t do it this way!==
  
Now you know the basics of how websockets works, I repeat my warning that you should not do it this way – unless you are a framework developer.   Even then, you are probably going to want to use the [http://download.eclipse.org/jetty/7.4.0.v20110414/xref/org/eclipse/jetty/websocket/WebSocketServlet.html WebSocketServlet] and a non embedded jetty, but the basic concepts are the same. Note the strength of the jetty solution is that it terminates both WebSocket connections and HTTP requests in the same environment, so that mixed frameworks and applications are easy to create.
+
Now you know the basics of how WebSockets work, I repeat my warning that you should not do it this way – unless you are a framework developer.   Even then, you probably want to use the [http://download.eclipse.org/jetty/7.4.0.v20110414/xref/org/eclipse/jetty/websocket/WebSocketServlet.html WebSocketServlet] and a non embedded Jetty, but the basic concepts are the same. The strength of the Jetty solution is that it terminates both WebSocket connections and HTTP requests in the same environment, so that mixed frameworks and applications are easy to create.
 +
 
 +
Application developers should really look to a framework like [http://cometd.org Cometd] rather than directly coding to WebSockets themselves.  It is not that the mechanics of WebSockets are hard, just that they don’t solve all of the problems that you encounter in a real world Comet application.
 +
 
 +
== Additional Resources ==
  
Application developers should really look to a framework like [http://cometd.org cometd] rather than directly coding to websockets themselves.  It is not that the mechanics of websockets are hard, just that they don’t solve all of the problems that you will encounter in a real world comet application.
+
[http://wiki.eclipse.org/Jetty_WTP_Plugin/Jetty_WTP_Websocket_Wizard Using the WTP WebSocket Wizard]
 +
}}

Latest revision as of 15:22, 23 April 2013



Introduction

Warning2.png
Some or all of this content remains to be ported to Jetty 9 Documentation.
If you are interested in migrating this content see our contribution guide or contact us.


The WebSockets protocol and API is an emerging standard that seeks to provide high-quality, bidirectional communication between a browser (or other web client) and a server. The goal is to eventually replace Comet techniques like long polling. Jetty has supported the various WebSocket drafts in the 7.x and 8.x releases.

You don’t want to do this!

This document shows you how to use WebSockets from the levels closest to the machine; it targets framework developers who want to use WebSockets, and application developers who can't stand not knowing what is under the hood. However, application programmers should not follow these examples to build an application. WebSockets is not a silver bullet, and on its own will never be simple to use for nontrivial applications (see Is WebSocket Chat Simpler?). We recommend that you look toward frameworks like Cometd, that private a higher level of abstraction, hide the technicalities, and allow you to use either Comet long polling or WebSockets transparently.

Feature

Test Client and Server

The simplest way to get started is to download 3 jars: The Jetty aggregate Jar contains all of jetty; the jetty-websocket test jar that contains a client and server; and the servlet jar. You can do this using a browser or with the following command line wgets:

wget -O jetty-all.jar --user-agent=demo \
  http://repo2.maven.org/maven2/org/eclipse/jetty/aggregate/jetty-all/7.6.2.v20120308/jetty-all-7.6.2.v20120308.jar
wget -O jetty-websocket-tests.jar --user-agent=demo \
  http://repo2.maven.org/maven2/org/eclipse/jetty/jetty-websocket/7.6.2.v20120308/jetty-websocket-7.6.2.v20120308-tests.jar
wget --user-agent=demo \
    http://repo2.maven.org/maven2/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar

To run a simple test server (use –help to see more options):

 java -cp jetty-websocket-tests.jar:jetty-all.jar:servlet-api-2.5.jar \
   org.eclipse.jetty.websocket.TestServer \
   --port 8080 \
   --docroot . \
   --verbose
Note.png

Windows Users Take Note:

The above example command line is for Unix/Linux/OSX, change the above example's use of ":" to ";" for the command to work in Windows.



You can test the server with the test client (use –help to see more options):

 java -cp jetty-websocket-tests.jar:jetty-all.jar:servlet-api-2.5.jar \
   org.eclipse.jetty.websocket.TestClient \
   --port 8080 \
   --protocol echo
Note.png

Windows Users Take Note:

The above example command line is for Unix/Linux/OSX, change the above example's use of ":" to ";" for the command to work in Windows.



The output from the test client is similar to ping. You can use the options you discover by using –help to try out different types of tests, including fragmentation and aggregation of WebSocket frames.

Using a Browser

Using a Java client is not compelling unless you want to write a desktop application that uses WebSocket (a viable idea).  But most WebSocket users want to use the browser as a client.  So point your browser at the TestServer at http://localhost:8080.

The WebSocket TestServer also runs an HTTP file server at the directory given by –docroot, so in this case you should see in the browser a listing of the directory in which you ran the test server.

To turn the browser into a WebSocket client, you need to serve some HTML and Javascript that executes in the browser and talks back to the server using Websockets. 

  1. Create the file index.html in the same directory you ran the server from.
  2. Put into it the following contents which you can download from jetty-websocket/src/test/webapp/index.html. This index file contains the HTML, CSS and Javascript for a basic chat room.

You should now be able to point your browser(s) at the test server, see a chat room, and join it.  If your browser does not support WebSockets, you’ll receive a warning.

Understanding How the Client Works

The initial HTML view has a prompt for a user name.  When a you enter a name, the system calls the join method, which creates the WebSocket to the server.  The URI for the WebSocket derives from the document's location. Call back functions are registered for open, message and close events.  The org.ietf.websocket.test-echo-broadcast subprotocol is specified as this echoes all received messages to all other broadcast connections, providing the semantic a chat room requires:

join: function(name) {
  this._username=name;
  var location = document.location.toString().replace('http://','ws://').replace('https://','wss://');
  this._ws=new WebSocket(location,"org.ietf.websocket.test-echo-broadcast");
  this._ws.onopen=this._onopen;
  this._ws.onmessage=this._onmessage;
  this._ws.onclose=this._onclose;
},

When the WebSocket successfully connects to the server, it calls the onopen callback, which changes the appearance of the chat room to prompt for a chat message.  It also sends a message saying the user has joined the room:

_onopen: function(){
  $('join').className='hidden';
  $('joined').className='';
  $('phrase').focus();
  room._send(room._username,'has joined!');
},

To send a message, you format a string as username:chat text and then call the WebSocket send method:

_send: function(user,message){
  user=user.replace(':','_');
  if (this._ws)
    this._ws.send(user+':'+message);
},
 
chat: function(text) {
  if (text != null &amp;&amp; text.length&gt;0 )
     room._send(room._username,text);
},

When the browser receives a WebSocket message over the connection, the system calls the onmessage callback with a message object. The Jetty implementation looks for  the username and colon, strips out any markup, and then appends the message to the chat room:

_onmessage: function(m) {
  if (m.data){
    var c=m.data.indexOf(':');
    var from=m.data.substring(0,c).replace('&lt;','&lt;').replace('&gt;','&gt;');
    var text=m.data.substring(c+1).replace('&lt;','&lt;').replace('&gt;','&gt;');
    var chat=$('chat');
    var spanFrom = document.createElement('span');
    spanFrom.className='from';
    spanFrom.innerHTML=from+': ';
    var spanText = document.createElement('span');
    spanText.className='text';
    spanText.innerHTML=text;
    var lineBreak = document.createElement('br');
    chat.appendChild(spanFrom);
    chat.appendChild(spanText);
    chat.appendChild(lineBreak);
    chat.scrollTop = chat.scrollHeight - chat.clientHeight;
  }
},

If the server closes the connection, or if the browser times it out, then the onclose callback is called.  This nulls out the chat room and reverts to the starting position:

 onclose: function(m) {
  this._ws=null;
  $('join').className='';
  $('joined').className='hidden';
  $('username').focus();
  $('chat').innerHTML='';
}

Understanding How the Server Works

The server side code for  this chat room uses an embedded Jetty server and is written against the Jetty WebSocket APIs that are not part of the WebSocket standard. There is not yet even a proposed standard for serverside WebSocket APIs, but it is a topic for consideration with the servlet 3.1 JSR.

The test server is an extension of an embedded Jetty server, and the constructor adds a connector at the required port, creates a WebSocketHandler and a ResourceHandler and chains them together:

 public TestServer(int port)
{
    _connector = new SelectChannelConnector();
    _connector.setPort(port);
    addConnector(_connector);
 
    _wsHandler = new WebSocketHandler()
    {
        public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
        {
            ...
            return _websocket;
        }
    };
    setHandler(_wsHandler);
 
    _rHandler=new ResourceHandler();
    _rHandler.setDirectoriesListed(true);
    _rHandler.setResourceBase(_docroot);
    _wsHandler.setHandler(_rHandler);
}

The resource handler is responsible for serving the static content like HTML and Javascript. The WebSocketHandler looks for WebSocket handshake requests and handles them by calling the doWebSocketConnect method, which we have extended to create a WebSocket depending on the sub protocol passed:

_wsHandler = new WebSocketHandler()
{
    public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
    {
        if ("org.ietf.websocket.test-echo".equals(protocol) || "echo".equals(protocol) || "lws-mirror-protocol".equals(protocol))
            _websocket = new TestEchoWebSocket();
        else if ("org.ietf.websocket.test-echo-broadcast".equals(protocol))
            _websocket = new TestEchoBroadcastWebSocket();
        else if ("org.ietf.websocket.test-echo-assemble".equals(protocol))
            _websocket = new TestEchoAssembleWebSocket();
        else if ("org.ietf.websocket.test-echo-fragment".equals(protocol))
            _websocket = new TestEchoFragmentWebSocket();
        else if (protocol==null)
            _websocket = new TestWebSocket();
        return _websocket;
    }
};

A simplification of the test WebSocket from the test server follows. It excludes the shared code for the other protocols supported. Like the Javascript API, there is an onOpen, onClose and onMessage callback. The onOpen callback is passed in a Connection instance that sends messages. The implementation of onOpen adds the WebSocket to a collection of all known WebSockets, and onClose removes the WebSocket. The implementation of onMessage is to simply iterate through that collection and to send the received message to each WebSocket:

ConcurrentLinkedQueue _broadcast =
    new ConcurrentLinkedQueue();
class TestEchoBroadcastWebSocket implements WebSocket.OnTextMessage
{
    protected Connection _connection;
 
    public void onOpen(Connection connection)
    {
        _connection=connection;
        _broadcast.add(this);
    }
    public void onClose(int code,String message)
    {
        _broadcast.remove(this);
    }
    public void onMessage(final String data)
    {
        for (TestEchoBroadcastWebSocket ws : _broadcast)
        {
            try
            {
                ws._connection.sendMessage(data);
            }
            catch (IOException e)
            {
                _broadcast.remove(ws);
                e.printStackTrace();
            }
        }
    }
}

Don’t do it this way!

Now you know the basics of how WebSockets work, I repeat my warning that you should not do it this way – unless you are a framework developer.   Even then, you probably want to use the WebSocketServlet and a non embedded Jetty, but the basic concepts are the same. The strength of the Jetty solution is that it terminates both WebSocket connections and HTTP requests in the same environment, so that mixed frameworks and applications are easy to create.

Application developers should really look to a framework like Cometd rather than directly coding to WebSockets themselves.  It is not that the mechanics of WebSockets are hard, just that they don’t solve all of the problems that you encounter in a real world Comet application.

Additional Resources

Using the WTP WebSocket Wizard

Copyright © Eclipse Foundation, Inc. All Rights Reserved.