Skip to content

Commit 44d5c9b

Browse files
Fun with WebSockets: Processing and GH examples
1 parent 084937d commit 44d5c9b

7 files changed

Lines changed: 301 additions & 0 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import java.lang.reflect.Method;
2+
import java.net.URI;
3+
import org.eclipse.jetty.client.HttpClient;
4+
import org.eclipse.jetty.util.ssl.SslContextFactory;
5+
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
6+
import org.eclipse.jetty.websocket.client.WebSocketClient;
7+
import processing.core.PApplet;
8+
9+
/**
10+
*
11+
* @author Lasse Steenbock Vestergaard
12+
*
13+
* Class for creating websocket client connections to any websocket server. Sub-protocols have not yet been implemented, and it's therefore only possible to connect to regular websocket servers.
14+
*
15+
*/
16+
public class WebsocketClient {
17+
private Method webSocketEvent;
18+
private Method webSocketEventBinary;
19+
private WebsocketClientEvents socket;
20+
21+
/**
22+
*
23+
* Initiating the client connection
24+
*
25+
* @param parent Processing's PApplet object
26+
* @param endpointURI The URI to connect to Ex. ws://localhost:8025/john
27+
*/
28+
public WebsocketClient(PApplet parent, String endpointURI) {
29+
this(parent, parent, endpointURI, new StringList());
30+
}
31+
32+
/**
33+
*
34+
* Initiating the client connection
35+
*
36+
* @param parent Processing's PApplet object
37+
* @param endpointURI The URI to connect to Ex. ws://localhost:8025/john
38+
* @param headers A list of custom headers to attach to the WS request, like "User-Agent:Processing"
39+
*/
40+
public WebsocketClient(PApplet parent, String endpointURI, StringList headers) {
41+
this(parent, parent, endpointURI, headers);
42+
}
43+
44+
/**
45+
* More flexible constructor in case you don't want callbacks called
46+
* in your PApplet but in a different class. Use this if you are
47+
* instantiating WebsocketClient in a class.
48+
*
49+
* @param parent Processing's PApplet object
50+
* @param callbacks The object implementing .webSocketEvent()
51+
* @param endpointURI The URI to connect to Ex. ws://localhost:8025/john
52+
* @param headers A list of custom headers to attach to the WS request, like "User-Agent:Processing"
53+
*/
54+
public WebsocketClient(PApplet parent, Object callbacks,
55+
String endpointURI, StringList headers) {
56+
parent.registerMethod("dispose", this);
57+
58+
try {
59+
webSocketEvent = callbacks.getClass().getMethod("webSocketEvent",
60+
String.class);
61+
webSocketEventBinary = callbacks.getClass().getMethod("webSocketEvent", byte[].class, int.class, int.class);
62+
} catch (Exception e) {
63+
// no such method, or an error.. which is fine, just ignore
64+
}
65+
66+
WebSocketClient client = null;
67+
68+
if(endpointURI.startsWith("wss")) {
69+
SslContextFactory ssl = new SslContextFactory();
70+
client = new WebSocketClient(ssl);
71+
} else {
72+
client = new WebSocketClient();
73+
}
74+
75+
try {
76+
socket = new WebsocketClientEvents(callbacks, webSocketEvent,
77+
webSocketEventBinary);
78+
client.start();
79+
URI echoUri = new URI(endpointURI);
80+
ClientUpgradeRequest request = new ClientUpgradeRequest();
81+
82+
// If headers are present, set them
83+
for (String header : headers)
84+
{
85+
String[] h = header.split(":");
86+
if (h.length != 2)
87+
{
88+
throw new Exception("Incorrectly formatted header \"" + header +
89+
"\", please use \":\" to separate key and value, " +
90+
"as in \"User-Agent:Processing\"");
91+
}
92+
request.setHeader(h[0], h[1]);
93+
}
94+
95+
client.connect(socket, echoUri, request);
96+
socket.getLatch().await();
97+
98+
} catch (Throwable t) {
99+
t.printStackTrace();
100+
}
101+
}
102+
103+
/**
104+
*
105+
* Send message to the websocket server. At a later stage it should be possible to send messages to specific clients connected to the same server
106+
*
107+
* @param message The message to send
108+
*/
109+
public void sendMessage(String message){
110+
socket.sendMessage(message);
111+
}
112+
113+
public void sendMessage(byte[] data){
114+
socket.sendMessage(data);
115+
}
116+
117+
public void dispose(){
118+
// Anything in here will be called automatically when
119+
// the parent sketch shuts down. For instance, this might
120+
// shut down a thread used by this library.
121+
}
122+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import java.io.IOException;
2+
import java.lang.reflect.Method;
3+
import java.util.concurrent.CountDownLatch;
4+
import java.nio.ByteBuffer;
5+
6+
import org.eclipse.jetty.websocket.api.Session;
7+
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
8+
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
9+
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
10+
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
11+
12+
/**
13+
*
14+
* @author Lasse Steenbock Vestergaard
15+
*
16+
* Class responsible for handling all websocket events
17+
*
18+
*/
19+
@WebSocket
20+
public class WebsocketClientEvents {
21+
private Session session;
22+
CountDownLatch latch = new CountDownLatch(1);
23+
private Object parent;
24+
private Method onMessageEvent;
25+
private Method onMessageEventBinary;
26+
27+
public WebsocketClientEvents(Object p, Method event, Method eventBinary) {
28+
parent = p;
29+
onMessageEvent = event;
30+
onMessageEventBinary = eventBinary;
31+
}
32+
33+
/**
34+
*
35+
* Sending incoming messages to the Processing sketch's websocket event function
36+
*
37+
* @param session The connection between server and client
38+
* @param message The received message
39+
* @throws IOException If no event fonction is registered in the Processing sketch then an exception is thrown, but it will be ignored
40+
*/
41+
@OnWebSocketMessage
42+
public void onText(Session session, String message) throws IOException {
43+
if (onMessageEvent != null) {
44+
try {
45+
onMessageEvent.invoke(parent, message);
46+
} catch (Exception e) {
47+
System.err
48+
.println("Disabling webSocketEvent() because of an error.");
49+
e.printStackTrace();
50+
onMessageEvent = null;
51+
}
52+
}
53+
}
54+
55+
@OnWebSocketMessage
56+
public void onBinary(Session session, byte[] buf, int offset, int length) throws IOException {
57+
if (onMessageEventBinary != null) {
58+
try {
59+
onMessageEventBinary.invoke(parent, buf, offset, length);
60+
} catch (Exception e) {
61+
System.err
62+
.println("Disabling webSocketEvent() because of an error.");
63+
e.printStackTrace();
64+
onMessageEventBinary = null;
65+
}
66+
}
67+
}
68+
69+
/**
70+
*
71+
* Handling establishment of the connection
72+
*
73+
* @param session The connection between server and client
74+
*/
75+
@OnWebSocketConnect
76+
public void onConnect(Session session) {
77+
this.session = session;
78+
latch.countDown();
79+
}
80+
81+
/**
82+
*
83+
* Sends message to the websocket server
84+
*
85+
* @param str The message to send to the server
86+
*/
87+
public void sendMessage(String str) {
88+
try {
89+
session.getRemote().sendString(str);
90+
} catch (IOException e) {
91+
e.printStackTrace();
92+
}
93+
}
94+
95+
public void sendMessage(byte[] data) {
96+
try {
97+
ByteBuffer buf = ByteBuffer.wrap(data);
98+
session.getRemote().sendBytes(buf);
99+
} catch (IOException e) {
100+
e.printStackTrace();
101+
}
102+
}
103+
104+
/**
105+
*
106+
* Handles errors occurring and writing them to the console
107+
*
108+
* @param cause The cause of an error
109+
*/
110+
@OnWebSocketError
111+
public void onError(Throwable cause) {
112+
System.out.printf("onError(%s: %s)%n",cause.getClass().getSimpleName(), cause.getMessage());
113+
cause.printStackTrace(System.out);
114+
}
115+
116+
public CountDownLatch getLatch() {
117+
return latch;
118+
}
119+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Global parameters
2+
WebsocketClient wsc;
3+
String SERVER_URL = "ws://simple-websocket-server-echo.glitch.me";
4+
float EASING_SPEED = 0.05;
5+
PVector pos = new PVector();
6+
PVector target = new PVector();
7+
8+
void setup() {
9+
size(600, 400);
10+
11+
StringList headers = new StringList();
12+
headers.append("User-Agent:Processing");
13+
14+
wsc = new WebsocketClient(this, SERVER_URL, headers);
15+
16+
background(0);
17+
noStroke();
18+
}
19+
20+
21+
void draw() {
22+
fill(0, 15);
23+
rect(0, 0, width, height);
24+
25+
fill(255);
26+
circle(pos.x, pos.y, 20);
27+
28+
// Ease position into target
29+
pos.x += EASING_SPEED * (target.x - pos.x);
30+
pos.y += EASING_SPEED * (target.y - pos.y);
31+
}
32+
33+
void mousePressed() {
34+
setTarget(mouseX, mouseY);
35+
sendTargetToServer();
36+
}
37+
38+
void setTarget(float tx, float ty) {
39+
target = new PVector(tx, ty);
40+
}
41+
42+
void sendTargetToServer() {
43+
float nx = target.x / (float) width;
44+
float ny = target.y / (float) height;
45+
46+
JSONObject json = new JSONObject();
47+
json.setFloat("x", nx);
48+
json.setFloat("y", ny);
49+
50+
wsc.sendMessage(json.toString());
51+
}
52+
53+
void webSocketEvent(String msg) {
54+
println("Received: " + msg);
55+
JSONObject json = parseJSONObject(msg);
56+
float tx = width * json.getFloat("x");
57+
float ty = height * json.getFloat("y");
58+
59+
setTarget(tx, ty);
60+
}

0 commit comments

Comments
 (0)