I have some trouble getting application-to-application communication via web sockets (that is without a browser to work). Since this does not seem to be the most usual application of web sockets, I wonder if anybody has any experience doing this.
Why do I want to use web sockets?
Because of firewall issues I need to go through port 80/8080 (and I need to continue to handle some other HTTP communication, so I can't just use plain TCP/IP socket communication).
How did I try to make this work?
I'm using Jetty 8.0 both for the server and for the client. My server code:
public class WebSocketTestServlet extends WebSocketServlet {
public WebSocket doWebSocketConnect(HttpServletRequest arg0, String arg1) {
return new TestWebSocket();
}
class TestWebSocket implements WebSocket, WebSocket.OnTextMessage
{
public void onClose(int arg0, String arg1) {
}
public void onOpen(Connection arg0) {
}
public void onMessage(String messageText) {
}
}
}
My client code:
public class MyWebSocketClient{
MyWebSocketClient() throws IOException
{
WebSocketClientFactory factory = new WebSocketClientFactory();
try {
factory.start();
} catch (Exception e1) {
e1.printStackTrace();
}
WebSocketClient client = factory.newWebSocketClient();
WebSocket.Connection connection = client.open(new URI("ws://myserver:8080/testws"), new WebSocket.OnTextMessage()
{
public void onOpen(Connection connection)
{
}
public void onClose(int closeCode, String message)
{
}
public void onMessage(String data)
{
}
}).get(50, TimeUnit.SECONDS)
}
What problem do I see?
A ProtocolException
Caused by: java.net.ProtocolException: Bad response status 302 Found
at org.eclipse.jetty.websocket.WebSocketClientFactory$HandshakeConnection.closed(WebSocketClientFactory.java:423)
at org.eclipse.jetty.websocket.WebSocketClientFactory$WebSocketClientSelector.endPointClosed(WebSocketClientFactory.java:235)
at org.eclipse.jetty.io.nio.SelectorManager$SelectSet.destroyEndPoint(SelectorManager.java:948)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.doUpdateKey(SelectChannelEndPoint.java:523)
at org.eclipse.jetty.io.nio.SelectorManager$SelectSet.doSelect(SelectorManager.java:469)
at org.eclipse.jetty.io.nio.SelectorManager$1.run(SelectorManager.java:283)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:598)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:533)
at java.lang.Thread.run(Thread.java:619)
Any idea why this is not working?
Try to add "/" at the end of your address in the client's code:
"ws://myserver:8080/testws/"
It just fixed the issue for me.
Trying to do something similar to allow WS calls to an embedded Jetty REST API...here's my echo test code (Jetty 7), HTH
public class myApiSocketServlet extends WebSocketServlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException ,IOException
{
OutputStream responseBody = response.getOutputStream();
responseBody.write("Socket API".getBytes());
responseBody.close();
}
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
{
return new APIWebSocket();
}
class APIWebSocket implements WebSocket, WebSocket.OnTextMessage
{
Connection connection;
#Override
public void onClose(int arg0, String arg1)
{
}
#Override
public void onOpen(Connection c)
{
connection = c;
}
#Override
public void onMessage(String msg)
{
try
{
this.connection.sendMessage("I received: " + msg);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
Related
I need help with connect on android to my WebSocket server based on Spring boot. Source code of this server I have taken https://spring.io/guides/gs/messaging-stomp-websocket/
Everything works fine on server and browser client on this sample,
but if I use StompClient (https://github.com/NaikSoftware/StompProtocolAndroid) to connect on my socket I am getting mStompClient.isConnected() == false and conand mStompClient.send(...) doesn't send anything (?).
After few minutes socketweb server closes the connection and I get in my log: '~~ Stomp connection closed'.
Web server locates on Heroku cloud system.
There is my connecting code from android activity:
private StompClient mStompClient;
private void connectStomp(){
mStompClient = Stomp.over(WebSocket.class, "wss://myserver/gs-guide-websocket");
mStompClient.topic("/topic/greetings").subscribe(new Action1<StompMessage>() {
#Override
public void call(StompMessage stompMessage) {
Log.w(TAG, "== "+stompMessage.getPayload());
}
});
mStompClient.connect();
mStompClient.lifecycle().subscribe(new Action1<LifecycleEvent>() {
#Override
public void call(LifecycleEvent lifecycleEvent) {
switch (lifecycleEvent.getType()) {
case OPENED:
Log.w(TAG, "~~ Stomp connection opened");
break;
case ERROR:
Log.e(TAG, "~~ Error", lifecycleEvent.getException());
break;
case CLOSED:
Log.w(TAG, "~~ Stomp connection closed "+lifecycleEvent.getMessage());
break;
}
}
});
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
connectStomp();
}
// Send test request to server
public void onSend(View view){
Log.w(TAG,"onSend: click");
mStompClient.send("/app/hello","Test").subscribe(new Observer<Void>() {
#Override
public void onCompleted() {
Log.w(TAG, "~~~~ onCompleted");
}
#Override
public void onError(Throwable e) {
Log.w(TAG, "~~~~ onCompleted "+e.getMessage());
}
#Override
public void onNext(Void aVoid) {
Log.w(TAG, "~~~~ onNext ");
}
});
if (mStompClient.isConnected()){
mStompClient.send("/app/hello","test msg").subscribe();
Log.w("aaaa : ","onCreate: connected");
}
}
It would be my mistake but if I connect to my server socket with spring boot WebSocketStompClient everithing works fine:
private SockJsClient sockJsClient;
private WebSocketStompClient stompClient;
private final WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
#Before
public void setup() {
List<Transport> transports = new ArrayList<>();
transports.add(new WebSocketTransport(new StandardWebSocketClient()));
this.sockJsClient = new SockJsClient(transports);
this.stompClient = new WebSocketStompClient(sockJsClient);
this.stompClient.setMessageConverter(new MappingJackson2MessageConverter());
}
#Test
public void getGreeting() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<Throwable> failure = new AtomicReference<>();
StompSessionHandler handler = new TestSessionHandler(failure) {
#Override
public void afterConnected(final StompSession session, StompHeaders connectedHeaders) {
session.subscribe("/topic/greetings", new StompFrameHandler() {
#Override
public Type getPayloadType(StompHeaders headers) {
return Greeting.class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
Greeting greeting = (Greeting) payload;
try {
System.out.println(greeting.getContent());
assertEquals("Hello, Spring!", greeting.getContent());
} catch (Throwable t) {
System.out.println(t.getMessage());
failure.set(t);
} finally {
session.disconnect();
latch.countDown();
}
}
});
try {
session.send("/app/hello", "Test");
} catch (Throwable t) {
failure.set(t);
latch.countDown();
}
}
};
this.stompClient.connect("wss://myserver/gs-guide-websocket", this.headers, handler, 443);
if (latch.await(10, TimeUnit.SECONDS)) {
if (failure.get() != null) {
throw new AssertionError("", failure.get());
}
}
else {
fail("Greeting not received");
}
}
private class TestSessionHandler extends StompSessionHandlerAdapter {
private final AtomicReference<Throwable> failure;
public TestSessionHandler(AtomicReference<Throwable> failure) {
this.failure = failure;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
this.failure.set(new Exception(headers.toString()));
}
#Override
public void handleException(StompSession s, StompCommand c, StompHeaders h, byte[] p, Throwable ex) {
this.failure.set(ex);
}
Any ideas? Thanks a lot!
I used the same Library in order to connect with Stomp based web socket. There were some configurations on the server side. On the android side, I was using URL as starting with "ws://" and ending with "websocket" like "ws://" + SERVER_URL + "/websocket".
See this answer for server side https://stackoverflow.com/a/41751897/5392825
I'm trying to make a custom "http" sever embedding a Jetty (verion 9) into my application.
The problem I'm facing right now is that Websocket Events are not being called.
This is what I have to so far (relevant parts):
SocketServer:
public class SocketServer
{
public static void main(String[] args) throws Exception
{
Server server = new Server(9091);
HandlerCollection handlerCollection = new HandlerCollection();
ServletContextHandler sch = new ServletContextHandler();
sch.addServlet(new ServletHolder(new RandomWebSocketServlet()), "");
handlerCollection.addHandler(sch);
server.setHandler(handlerCollection);
server.start();
server.join();
}
}
RandomWebSocketServlet:
public class RandomWebSocketServlet extends WebSocketServlet {
private static final long serialVersionUID = -1134228059326523215L;
public RandomWebSocketServlet() {
}
#Override
public void configure(WebSocketServletFactory factory) {
factory.setCreator(new AdvancedSocketCreator());
}
}
AdvancedSocketCreator:
public class AdvancedSocketCreator implements WebSocketCreator {
public AdvancedSocketCreator() {
}
#Override
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) {
return new Handler();
}
}
Handler:
#WebSocket
public class Handler {
protected Session session = null;
public Handler() {
}
#OnWebSocketConnect
public void onConnect(Session session) {
Log.log("WebSocket connected to client: "+session.getRemoteAddress().getHostName()+":"+session.getRemoteAddress().getPort());
this.session = session;
}
#OnWebSocketError
public void onError(Throwable t) {
Log.log("WebSocket error");
}
#OnWebSocketMessage
public void onMessage(String message) {
Log.log("Message: "+message);
}
#OnWebSocketClose
public void onClose(int statusCode, String reason) {
Log.log("WebSocket closed: "+statusCode+" - "+reason);
}
}
When I try to open a connection (using telnet, for instance), the #OnWebSocketConnect onConnect method is not being called.
I tried many ways, following many internet tutorials... but with no success.
Any help?
Hello I am trying to create an RMI programm that sends and receives data from a server. However after I create my registries and look them up etc, and point my program to a method in the interface it throws itself into an integerpage (via debugger). my code is as follows
For the client
public static void SendData(int score,String name) throws RemoteException, NotBoundException {
try {
Registry reg = LocateRegistry.getRegistry("127.0.0.1",1099);
TestRemote remote = (TestRemote) reg.lookup("TestRMI");
remote.SendMyData(score,name);
} catch (Exception e) {
String ex = e.toString();
}
For the server
public class RMIserver {
public static void main(String[] args ) throws RemoteException, AlreadyBoundException {
RemoteImp imp = new RemoteImp();
Registry reg = LocateRegistry.createRegistry(1099);
reg.rebind("TestRMI", imp);
System.out.println("Server is Started");
}
}
Interface
public interface TestRemote extends Remote {
public ArrayList<TempPlayer> getOpdata() throws RemoteException;
public void SendMyData(int score, String player) throws RemoteException;}
And for the interface implementation:
private static final long serialVersionUID = 1L;
private ArrayList<TempPlayer> opdata = new ArrayList<TempPlayer>();
private ArrayList<String> lobbydata = new ArrayList<String>();
protected RemoteImp() throws RemoteException
{
}
#Override
public ArrayList<TempPlayer> getOpdata() throws RemoteException {
return opdata;
}
#Override
public void SendMyData(int score, String name) throws RemoteException {
//opdata.add(String.valueOf(score));
for (TempPlayer player : opdata) {
if (player.player == name) {
player.setScore(score);
} else {
opdata.add(new TempPlayer(score, name));
}
}
}
}
Now, when I try to invoke SendData by calling Remote.senddata, it goes to the Integer.java page. My question now is what could cause this and how do I get the program to actually go to the function I want it to go to?
I'm new to Jerry, and trying to implement WebSocket Client on Jetty9.
I saw an example on Jetty8.
org.eclipse.jetty.websocket Class WebSocketClient
http://archive.eclipse.org/jetty/8.0.0.v20110901/apidocs/org/eclipse/jetty/websocket/WebSocketClient.html
to create a new instance of WebSocketClient is :
WebSocketClientFactory factory = new WebSocketClientFactory();
factory.start();
WebSocketClient client = factory.newWebSocketClient();
// Configure the client
WebSocket.Connection connection = client.open(new
URI("ws://127.0.0.1:8080/"), new WebSocket.OnTextMessage()
{
public void onOpen(Connection connection)
{
// open notification
}
public void onClose(int closeCode, String message)
{
// close notification
}
public void onMessage(String data)
{
// handle incoming message
}
}).get(5, TimeUnit.SECONDS);
connection.sendMessage("Hello World");
However, I've never seen a document for Jetty9 for this.
So far, referring to
org.eclipse.jetty.websocket.common
Interface SessionFactory
//----------------------------------------------
WebSocketSession createSession(URI requestURI,
EventDriver websocket,
LogicalConnection connection)
//----------------------------------------------
I've tried
private WebSocketSessionFactory factory = new WebSocketSessionFactory();
try
{
WebSocketSession session = factory.createSession(uri,
eventDriver, connection);
RemoteEndpoint ep = session.getRemote();
}
catch (Exception ex)
{
System.out.println("=ERROR= " + ex);
//=ERROR= java.lang.NullPointerException
}
private EventDriver eventDriver = new EventDriver()
{
#Override
public WebSocketPolicy getPolicy()
{
return null;
}
//......................................
#Override
public void incomingFrame(Frame frame)
{
}
};
private LogicalConnection connection = new LogicalConnection()
{
#Override
public void close()
{
}
//...............................
#Override
public void resume()
{
}
};
but I've encounter java.lang.NullPointerException
How do we implement Jetty9 WebSocket Client ??
Thanks for your advise.
Hope this helpful: EventClient.java
I'm trying implement server code of Server-Sent Events in a generic way that any Object of my application could send a message to client, so I've decided implement a specific Servlet just for SSE. The initial test codes worked like a charm, but wasn't flexible enought to send messages from different parts of my application. So I've rewrite the code in a way that all objects that has a reference to Servlet object could send a message to the clients:
public class PushServlet extends HttpServlet {
private Thread threadServlet;
private boolean processando=true;
private MensagemSSEBean mensagem;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
threadServlet=Thread.currentThread();
response.setContentType("text/event-stream; charset=utf-8");
while (processando){
if(!pausarThread())
break;
enviarMensagemParaOCliente(response.getWriter());
}
enviarMensagemDeFechamento(response.getWriter());
}
private void enviarMensagemParaOCliente(PrintWriter saida) {
ConversorMensagemSSE conversor = new ConversorMensagemSSE();
saida.print(conversor.converter(mensagem));
saida.flush();
}
private synchronized void enviarMensagemDeFechamento(PrintWriter saida) {
mensagem.setMensagem("#FECHAR_CONEXAO#");
enviarMensagemParaOCliente(saida);
saida.close();
}
public synchronized void enviarMensagem(MensagemSSEBean mensagem) throws IOException {
this.mensagem=mensagem;
threadServlet.notifyAll();
}
public synchronized void finalizar(){
processando=false;
}
private boolean pausarThread() {
try {
threadServlet.wait();
return true;
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
}
As you can see, I pause the Servlet Thread until something call "enviarMensagem". I didn't tested this code, basically cause I don't know how I can get this Servlet object. Could someone explain me how could I get this Servlet object from any Object?? Another important question, is this the ideal approach for this kind of problem??
Finally I implemented it in a generic way. The servlet class now send keep-alive every ten seconds or the messages in a shared queue:
public class PushServlet extends HttpServlet {
private boolean processing = true;
private HttpServletResponse response;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.response = response;
configureAndStart();
while (processing) {
try {
sendMessages();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void configureAndStart() throws IOException {
processing = true;
response.setContentType("text/event-stream; charset=utf-8");
sendMessage(new SSEMessageBean(SSEEventType.START));
}
private void sendMessages() throws IOException, InterruptedException {
SSEMessageBean message = MessageQueueController.getInstance().getNextMessage();
while (message != null) {
sendMessage(message);
message = MessageQueueController.getInstance().getNextMessage();
if (message.getEventType() != SSEEventType.END)
return;
}
Thread.sleep(10000);
sendMessage(new SSEMessageBean(SSEEventType.KEEP_ALIVE));
}
public void sendMessage(SSEMessageBean message) throws IOException {
SSEMessageConverter converter = new SSEMessageConverter();
PrintWriter out = response.getWriter();
out.print(converter.convert(message));
out.flush();
if (message.getEventType() == SSEEventType.END) {
processing = false;
out.close();
}
}
}
The objects that want send events to clients simply write in shared queue.