WebSocket subscriber using Stomp with Arduino - spring-boot

I have created a simple Web socket application using Spring boot that's running on my local network on 192.168.1.50:8080(I've enabled CORS).
You can see the Spring boot application code here.
The server is waiting for a call from the URL /arduino/api/v1/getArduinoResponse and should forward the message to /arduino/subscribers channel.
The WebSocket server is fully functional.
#RestController
#MessageMapping("/arduino/api/v1")
#RequestMapping("/arduino/api/v1")
public class ArduinoControllerV1 {
#Autowired
private SimpMessagingTemplate template;
#MessageMapping("/getArduinoResponse")
#SendTo("/arduino/subscribers")
public WebSocketResponseEntity getArduinoResponse(WebSocketResponseEntity message) throws Exception {
return message;
}
#PostMapping("/sendCommand")
public String sendCommand(#RequestBody CommandEntity commandEntity) throws Exception {
this.template.convertAndSend("/arduino/subscribers", commandEntity.getCommand());
return String.format("Sending to %s the command: %s ", commandEntity.getDeviceSerial(), commandEntity.getCommand());
}
}
Configuration file:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/arduino");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/arduino-websocket").setAllowedOriginPatterns("*").withSockJS();
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
};
}
}
I have created a sketch on my Arduino MEGA that suppose to subscribe to the broker that belongs to the Spring boot server.
But for some reason, I'm receiving a message that I am unable to connect to the WS server.
I know that I'm missing the channel name in the Arduino code, and I don't know where to put it.
This is my Arduino code:
#include <Arduino.h>
#include <WebSocketsClient.h>
#include <Ethernet.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 178 }; // ip in lan (that's what you need to use in your browser. ("192.168.1.178")
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 };
const char* ws_host = "192.168.1.50";
const int ws_port = 8080;
const char* stompUrl = "/arduino-websocket"; // don't forget the leading "/" !!!
WebSocketsClient webSocket;
void sendMessage(String & msg) {
webSocket.sendTXT(msg.c_str(), msg.length() + 1);
}
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
Serial.println("[WSc] Disconnected!\n");
break;
case WStype_CONNECTED:
{
Serial.print("[WSc] Connected to url: ");
Serial.println((char *)payload);
// send message to server when Connected
webSocket.sendTXT("Connected");
}
break;
case WStype_TEXT:
Serial.print("[WSc] get text: ");
Serial.println((char *)payload);
// send message to server
// webSocket.sendTXT("message here");
break;
case WStype_BIN:
Serial.print("[WSc] get binary length: ");
Serial.println(length);
// hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
}
}
void setup() {
Serial.begin(115200);
Serial.println("Started");
while (!Serial) {
; // wait for serial port to connect.
}
Serial.println("Serial Started");
// connect to Ethernet
Ethernet.begin(mac, ip, gateway, subnet);
while (!Ethernet.begin(mac)) {
Serial.println("failed. Retrying in 5 seconds.");
delay(5000);
Serial.print("Starting W5100...");
}
Serial.println("Ethernet Started");
Serial.print("I'm sitting on: ");
Serial.println(Ethernet.localIP());
// connect to websocket
webSocket.begin(ws_host, ws_port, stompUrl);
//webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config
// webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}
Thanks for your help

Related

TCP connection in Unity

I'm working on TCP connection between two Windows10 laptops. I made the applications using Unity 2019.2.17f1. However, the TCP connection doesn't work. The Client application connects to the server only when I don't run the server application (this is strange though...), otherwise the client application shows the message "server is not found...".
I put the part of the codes here.
Client Program:
public class TCPClientManager : MonoBehaviour
{
// ip address(server) and port number
public string ipAddress = "192.0.0.1";
public int port = 3000;
private TcpClient m_tcpClient;
private NetworkStream m_networkStream;
private bool m_isConnection;
private string message;
void Start()
{
try
{
// connect to the server
m_tcpClient = new TcpClient(ipAddress, port);
m_networkStream = m_tcpClient.GetStream();
m_isConnection = true;
}
catch (SocketException)
{
m_isConnection = false;
// show a error message
// ...
}
}
void OnGUI()
{
if (m_isConnection)
{
GUILayout.Label("server is not found...");
return;
}
// some codes here
}
// some codes here
}
Server Program:
public class TCPServerManager : MonoBehaviour
{
// ip address(server) and port number
public string ipAddress = "192.0.0.1";
public int port = 3000;
private TcpListener m_tcpListener;
private TcpClient m_tcpClient;
private NetworkStream m_networkStream;
private bool m_isConnection;
private string message = string.Empty;
private void Awake()
{
Task.Run(() => OnProcess());
}
private void OnProcess()
{
var n_IpAddress = IPAddress.Parse(ipAddress);
m_tcpListener = new TcpListener(n_IpAddress, port);
m_tcpListener.Start();
m_tcpClient = m_tcpListener.AcceptTcpClient();
m_networkStream = m_tcpClient.GetStream();
while (true)
{
var buffer = new byte[256];
var count = m_networkStream.Read(buffer, 0, buffer.Length);
if (count == 0)
{
OnDestroy();
Task.Run(() => OnProcess());
break;
}
message += Encoding.UTF8.GetString(buffer, 0, count) + "\n";
}
}
// ....
}
Thank you very much for your comments in advice.
I think you inverted you m_isConnection variables values. You set it to true after connecting the server and false if not. But in OnGUI, if you found the connection then you print an error message and leave. Which means you do your //some code here only if no server was found.

Building a Future API on top of Netty

I want to build an API based on Futures (from java.util.concurrent) that is powered by a custom protocol on top of Netty (version 4). Basic idea is to write a simple library that would abstract the underlying Netty implementation and make it easier to make requests.
Using this library, one should be able to write something like this:
Request req = new Request(...);
Future<Response> responseFuture = new ServerIFace(host, port).call(req);
// For example, let's block until this future is resolved
Reponse res = responseFuture.get().getResult();
Underneath this code, a Netty client is connected
public class ServerIFace {
private Bootstrap bootstrap;
private EventLoopGroup workerGroup;
private String host;
private int port;
public ServerIFace(String host, int port) {
this.host = host;
this.port = port;
this.workerGroup = new NioEventLoopGroup();
bootstrap();
}
private void bootstrap() {
bootstrap = new Bootstrap();
bootstrap.group(workerGroup);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ObjectEncoder());
ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(Response.class.getClassLoader())));
ch.pipeline().addLast("response", new ResponseReceiverChannelHandler());
}
});
}
public Future<Response> call(final Request request) throws InterruptedException {
CompletableFuture<Response> responseFuture = new CompletableFuture<>();
Channel ch = bootstrap.connect(host, port).sync().channel();
ch.writeAndFlush(request).addListener((f) -> {
if (f.isSuccess()) {
System.out.println("Wrote successfully");
} else {
f.cause().printStackTrace();
}
});
ChannelFuture closeFuture = ch.closeFuture();
// Have to 'convert' ChannelFuture to java.util.concurrent.Future
closeFuture.addListener((f) -> {
if (f.isSuccess()) {
// How to get this response?
Response response = ((ResponseReceiverChannelHandler) ch.pipeline().get("response")).getResponse();
responseFuture.complete(response);
} else {
f.cause().printStackTrace();
responseFuture.cancel(true);
}
ch.close();
}).sync();
return responseFuture;
}
}
Now, as you can see, in order to abstract Netty's inner ChannelFuture, I have to 'convert' it to Java's Future (I'm aware that ChannelFuture is derived from Future, but that information doesn't seem useful at this point).
Right now, I'm capturing this Response object in the last handler of my inbound part of the client pipeline, the ResponseReceiverChannelHandler.
public class ResponseReceiverChannelHandler extends ChannelInboundHandlerAdapter {
private Response response = null;
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
this.response = (Response)msg;
ctx.close();
}
public Response getResponse() {
return response;
}
}
Since I'm new to Netty and these things in general, I'm looking for a cleaner, thread-safe way of delivering this object to the API user.
Correct me if I'm wrong, but none of the Netty examples show how to achieve this, and most of the Client examples just print out whatever they get from Server.
Please note that my main goal here is to learn more about Netty, and that this code has no production purposes.
For the reference (although I don't think it's that relevant) here's the Server code.
public class Server {
public static class RequestProcessorHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ChannelFuture future;
if (msg instanceof Request) {
Request req = (Request)msg;
Response res = some function of req
future = ctx.writeAndFlush(res);
} else {
future = ctx.writeAndFlush("Error, not a request!");
}
future.addListener((f) -> {
if (f.isSuccess()) {
System.out.println("Response sent!");
} else {
System.out.println("Response not sent!");
f.cause().printStackTrace();
}
});
}
}
public int port;
public Server(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(Request.class.getClassLoader())));
ch.pipeline().addLast(new ObjectEncoder());
// Not really shutting down this threadpool but it's ok for now
ch.pipeline().addLast(new DefaultEventExecutorGroup(2), new RequestProcessorHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new Server(port).run();
}
}

Http proxy in Netty websocket client to connect to internet

My application is running behind a corporate firewall and I need to use http proxy(http://theclientproxy.net:8080) to connect to internet
I have used the Netty client as below,
https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/http/websocketx/client
Code:
public final class WebSocketClient {
static final String URL = System.getProperty("url", "wss://127.0.0.1:8080/websocket");
public static void main(String[] args) throws Exception {
URI uri = new URI(URL);
String scheme = uri.getScheme() == null? "ws" : uri.getScheme();
final String host = uri.getHost() == null? "127.0.0.1" : uri.getHost();
final int port;
final boolean ssl = "wss".equalsIgnoreCase(scheme);
final SslContext sslCtx;
if (ssl) {
sslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
} else {
sslCtx = null;
}
EventLoopGroup group = new NioEventLoopGroup();
try {
final WebSocketClientHandler handler =
new WebSocketClientHandler(
WebSocketClientHandshakerFactory.newHandshaker(
uri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders()));
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addFirst(new HttpProxyHandler(new InetSocketAddress("theclientproxy.net", 8080) ) );
p.addLast(sslCtx.newHandler(ch.alloc(), host, port));
}
p.addLast(
new HttpClientCodec(),
new HttpObjectAggregator(8192),
WebSocketClientCompressionHandler.INSTANCE,
handler);
}
});
Channel ch = b.connect(uri.getHost(), port).sync().channel();
handler.handshakeFuture().sync();
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String msg = console.readLine(); //THIS IS NULL IN DATA CENTER LOGS
if (msg == null) {
break;
} else if ("bye".equals(msg.toLowerCase())) {
ch.writeAndFlush(new CloseWebSocketFrame());
ch.closeFuture().sync();
break;
} else if ("ping".equals(msg.toLowerCase())) {
WebSocketFrame frame = new PingWebSocketFrame(Unpooled.wrappedBuffer(new byte[] { 8, 1, 8, 1 }));
ch.writeAndFlush(frame);
} else {
WebSocketFrame frame = new TextWebSocketFrame(msg);
ch.writeAndFlush(frame);
}
}
} finally {
group.shutdownGracefully();
}
Handler:
public class WebSocketClientHandler extends SimpleChannelInboundHandler<Object> {
private final WebSocketClientHandshaker handshaker;
private ChannelPromise handshakeFuture;
public WebSocketClientHandler(WebSocketClientHandshaker handshaker) {
this.handshaker = handshaker;
}
public ChannelFuture handshakeFuture() {
return handshakeFuture;
}
#Override
public void handlerAdded(ChannelHandlerContext ctx) {
handshakeFuture = ctx.newPromise();
}
#Override
public void channelActive(ChannelHandlerContext ctx) {
handshaker.handshake(ctx.channel());
}
#Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("WebSocket Client disconnected!");
}
#Override
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel ch = ctx.channel();
if (!handshaker.isHandshakeComplete()) {
try {
handshaker.finishHandshake(ch, (FullHttpResponse) msg);
System.out.println("WebSocket Client connected!");
handshakeFuture.setSuccess();
} catch (WebSocketHandshakeException e) {
System.out.println("WebSocket Client failed to connect");
handshakeFuture.setFailure(e);
}
return;
}
The application is able to connect to the websocket server endpoint from my local machine successfully.
But in the company datacenter where my application is deployed, I see the msg value is null and the websocket client is disconnected
Does that mean my connection is blocked at firewall? If that is the case then why did the statement "WebSocket Client connected!" is printed at all?
Thanks
The httpproxyhandler you used is correct
Just remove the BufferredReader code as mentioned below when deploying in linux, docker, etc:
Netty WebSocket Client Channel always gets inactive on Linux Server

Spark-Streaming CustomReceiver Unknown Host Exception

I am new to spark streaming. I want to stream a url online in order to retrieve info from a certain URL, I used the JavaCustomReceiver in order to stream a url.
This is the code I'm using (source)
public class JavaCustomReceiver extends Receiver<String> {
private static final Pattern SPACE = Pattern.compile(" ");
public static void main(String[] args) throws Exception {
SparkConf sparkConf = new SparkConf().setAppName("JavaCustomReceiver");
JavaStreamingContext ssc = new JavaStreamingContext(sparkConf, new Duration(1000));
JavaReceiverInputDStream<String> lines = ssc.receiverStream(
new JavaCustomReceiver("http://stream.meetup.com/2/rsvps", 80));
JavaDStream<String> words = lines.flatMap(new
FlatMapFunction<String, String>() {
#Override
public Iterator<String> call(String x) {
return Arrays.asList(SPACE.split(x)).iterator();
}
});
JavaPairDStream<String, Integer> wordCounts = words.mapToPair(
new PairFunction<String, String, Integer>() {
#Override
public Tuple2<String, Integer> call(String s) {
return new Tuple2<>(s, 1);
}
}).reduceByKey(new Function2<Integer, Integer, Integer>() {
#Override
public Integer call(Integer i1, Integer i2) {
return i1 + i2;
}
});
wordCounts.print();
ssc.start();
ssc.awaitTermination();
}
String host = null;
int port = -1;
public JavaCustomReceiver(String host_, int port_) {
super(StorageLevel.MEMORY_AND_DISK_2());
host = host_;
port = port_;
}
public void onStart() {
new Thread() {
#Override
public void run() {
receive();
}
}.start();
}
public void onStop() {
}
private void receive() {
try {
Socket socket = null;
BufferedReader reader = null;
String userInput = null;
try {
// connect to the server
socket = new Socket(host, port);
reader = new BufferedReader(
new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
// Until stopped or connection broken continue reading
while (!isStopped() && (userInput = reader.readLine()) != null) {
System.out.println("Received data '" + userInput + "'");
store(userInput);
}
} finally {
Closeables.close(reader, /* swallowIOException = */ true);
Closeables.close(socket, /* swallowIOException = */ true);
}
restart("Trying to connect again");
} catch (ConnectException ce) {
// restart if could not connect to server
restart("Could not connect", ce);
} catch (Throwable t) {
restart("Error receiving data", t);
}
}
}
However, I keep getting a java.net.UnknownHostException
How can I fix this? What is wrong with the code that I'm using ?
After reading the code of the custom receiver referenced, it is clear that it is a TCP receiver that connects to a host:port and not an HTTP receiver that could take an URL. You'll have to change the code to read from an HTTP endpoint.

How to instantiate a WebSocketAdapter instance for Jetty websockets

I followed the example to create a websocket server:
Server server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(port);
server.addConnector(connector);
ServletContextHandler servletContextHandler = new ServletContextHandler(server, "/", true, false);
EventServlet es = injector.getInstance(EventServlet.class);
servletContextHandler.addServlet(new ServletHolder(es), "/events/*");
The EventServlet class looks like:
public class EventServlet extends WebSocketServlet {
#Override
public void configure(WebSocketServletFactory factory) {
factory.getPolicy().setIdleTimeout(10000);
factory.register(EventSocketCache.class);
}
}
The EventSocketCache looks like:
public class EventSocketCache extends WebSocketAdapter {
private static int i = 0;
private static int counter = 0;
private static Map<Integer, Session> sessionMap = new HashMap<>();
private final Cache<String, String> testCache;
#Inject
public EventSocketCache(Cache<String, String> testCache) {
this.testCache = testCache;
}
#Override
public void onWebSocketConnect(Session session) {
super.onWebSocketConnect(session);
System.out.println("Socket Connected: " + session);
System.out.println("Connect: " + session.getRemoteAddress().getAddress());
try {
session.getRemote().sendString("Hello Webbrowser");
session.setIdleTimeout(50000);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onWebSocketText(String message) {
super.onWebSocketText(message);
System.out.println("Received TEXT message: " + message);
}
#Override
public void onWebSocketBinary(byte[] payload, int offset, int len) {
byte[] newData = Arrays.copyOfRange(payload, offset, offset + len);
try {
Common.Success success = Common.Success.parseFrom(newData);
System.err.println("------> " + success.getIsSuccess());
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
#Override
public void onWebSocketClose(int statusCode, String reason) {
System.err.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
// Remove from the list here....
super.onWebSocketClose(statusCode, reason);
System.out.println("Socket Closed: [" + statusCode + "] " + reason);
}
#Override
public void onWebSocketError(Throwable cause) {
System.err.println("######################################");
super.onWebSocketError(cause);
cause.printStackTrace(System.err);
}
}
Now when I use my client and send a request, I end up getting:
org.eclipse.jetty.websocket.api.UpgradeException: Didn't switch protocols
at org.eclipse.jetty.websocket.client.io.UpgradeConnection.validateResponse(UpgradeConnection.java:249)
at org.eclipse.jetty.websocket.client.io.UpgradeConnection.read(UpgradeConnection.java:181)
at org.eclipse.jetty.websocket.client.io.UpgradeConnection.onFillable(UpgradeConnection.java:126)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:358)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
at java.lang.Thread.run(Thread.java:722)
Disconnected from the target VM, address: '127.0.0.1:63256', transport: 'socket'
java.util.concurrent.ExecutionException: org.eclipse.jetty.websocket.api.UpgradeException: Didn't switch protocols
at org.eclipse.jetty.util.FuturePromise.get(FuturePromise.java:123)
at com.gamecenter.websockets.EventClient.main(EventClient.java:25)
Caused by: org.eclipse.jetty.websocket.api.UpgradeException: Didn't switch protocols
at org.eclipse.jetty.websocket.client.io.UpgradeConnection.validateResponse(UpgradeConnection.java:249)
at org.eclipse.jetty.websocket.client.io.UpgradeConnection.read(UpgradeConnection.java:181)
at org.eclipse.jetty.websocket.client.io.UpgradeConnection.onFillable(UpgradeConnection.java:126)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:358)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
at java.lang.Thread.run(Thread.java:722)
It seems as there is a problem creating an instance of EventSocketCache; if I don't have the constructor in there, everything works fine.
I'd like to know how to instantiate EventSocketCache properly and register it with EventServlet so things work?
I guess I've found a solution for your problem. You have to use a WebSocketCreator in your WebSocketServlet:
public class MenuServlet extends WebSocketServlet {
#Inject
private Injector injector;
#Override
public void configure(WebSocketServletFactory webSocketServletFactory) {
// Register your Adapater
webSocketServletFactory.register(MenuSocket.class);
// Get the current creator (for reuse)
final WebSocketCreator creator = webSocketServletFactory.getCreator();
// Set your custom Creator
webSocketServletFactory.setCreator(new WebSocketCreator() {
#Override
public Object createWebSocket(ServletUpgradeRequest servletUpgradeRequest, ServletUpgradeResponse servletUpgradeResponse) {
Object webSocket = creator.createWebSocket(servletUpgradeRequest, servletUpgradeResponse);
// Use the object created by the default creator and inject your members
injector.injectMembers(webSocket);
return webSocket;
}
});
}
}
there you can inject your members into your WebSocketAdapater. This actually worked for me.

Resources