I am new to WebSockets.
I have already made a simple server-client chat in WebSockets.
And now I am trying to make client-server-client chat application.
I have a question that in java server how can we send a message to particular WebSocket connection.
If user-A want to send a message to User-B.
Then how can I manage that User-B is using this or that connection or send a message to that particular connection?
I am searching too much for this on google but could not find anything good.
You have to design an architecture for that.
When a client establishes a connection with the server (opens the WebSocket), the server has to keep the connection somewhere (howsoever you're identifying a specific connection with the Java backend you're using), in a data structure that will depend on what you're trying to do. A good identifier would be an ID the user provides (like a nickname that's not already picked by another peer connected to the same server). Otherwise, simply use the socket object as a unique identifier and, when listing other users on the frontend, associate them with their unique identifier so that a client can send a message to a specific peer.
A HashMap would be a good choice for a data structure if a client is going to chat with another specific client, as you can map the unique ID of a client to the socket and find an entry with in O(1) in a hash table.
If you want to broadcast a message from a client to all other clients, although a HashMap would also work pretty well (with something like HashMap.values()), you may use a simple List, sending the incoming message to all connected clients except the original sender.
Of course, you also want to remove a client from the data structure when you lose connection with it, which is easy using a WebSocket (the Java framework you are using should call you back when a socket closes).
Here's an (almost complete) example using a Jetty 9 WebSocket (and JDK 7):
package so.example;
import java.io.IOException;
import java.util.HashMap;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
#WebSocket
public class MyWebSocket {
private final static HashMap<String, MyWebSocket> sockets = new HashMap<>();
private Session session;
private String myUniqueId;
private String getMyUniqueId() {
// unique ID from this class' hash code
return Integer.toHexString(this.hashCode());
}
#OnWebSocketConnect
public void onConnect(Session session) {
// save session so we can send
this.session = session;
// this unique ID
this.myUniqueId = this.getMyUniqueId();
// map this unique ID to this connection
MyWebSocket.sockets.put(this.myUniqueId, this);
// send its unique ID to the client (JSON)
this.sendClient(String.format("{\"msg\": \"uniqueId\", \"uniqueId\": \"%s\"}",
this.myUniqueId));
// broadcast this new connection (with its unique ID) to all other connected clients
for (MyWebSocket dstSocket : MyWebSocket.sockets.values()) {
if (dstSocket == this) {
// skip me
continue;
}
dstSocket.sendClient(String.format("{\"msg\": \"newClient\", \"newClientId\": \"%s\"}",
this.myUniqueId));
}
}
#OnWebSocketMessage
public void onMsg(String msg) {
/*
* process message here with whatever JSON library or protocol you like
* to get the destination unique ID from the client and the actual message
* to be sent (not shown). also, make sure to escape the message string
* for further JSON inclusion.
*/
String destUniqueId = ...;
String escapedMessage = ...;
// is the destination client connected?
if (!MyWebSocket.sockets.containsKey(destUniqueId)) {
this.sendError(String.format("destination client %s does not exist", destUniqueId));
return;
}
// send message to destination client
this.sendClient(String.format("{\"msg\": \"message\", \"destId\": \"%s\", \"message\": \"%s\"}",
destUniqueId, escapedMessage));
}
#OnWebSocketClose
public void onClose(Session session, int statusCode, String reason) {
if (MyWebSocket.sockets.containsKey(this.myUniqueId)) {
// remove connection
MyWebSocket.sockets.remove(this.myUniqueId);
// broadcast this lost connection to all other connected clients
for (MyWebSocket dstSocket : MyWebSocket.sockets.values()) {
if (dstSocket == this) {
// skip me
continue;
}
dstSocket.sendClient(String.format("{\"msg\": \"lostClient\", \"lostClientId\": \"%s\"}",
this.myUniqueId));
}
}
}
private void sendClient(String str) {
try {
this.session.getRemote().sendString(str);
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendError(String err) {
this.sendClient(String.format("{\"msg\": \"error\", \"error\": \"%s\"}", err));
}
}
The code is self explanatory. About JSON formatting and parsing, Jetty has some interesting utilities within package org.eclipse.jetty.util.ajax.
Also note that if your WebSocket server framework is not thread-safe, you will need to synchronize the data structure to make sure there's no data corruption (here MyWebSocket.sockets).
Related
I'm trying to connect java Server and Javascript client with socket.io. When i see the debugger at browser, it looks like the data is being received, but i'm getting this error: "Reason: CORS header 'Access-Control-Allow-Origin' missing" and i am not being able to print data at client-side.
import...
public class MeuServerSocket {
//initialize socket and input stream
private Socket socket = null;
private ServerSocket server = null;
private DataInputStream in = null;
public MeuServerSocket(int port) {
// starts server and waits for a connection
try {
while(true){
server = new ServerSocket(port);
System.out.println("Server started");
System.out.println("Waiting for a client ...");
socket = server.accept();
System.out.println("Client accepted");
ObjectOutputStream saida = new ObjectOutputStream(socket.getOutputStream());
saida.flush();
// send available data from server to client
saida.writeObject("Texto enviado 123...");
// takes input from the client socket
in = new DataInputStream(
new BufferedInputStream(socket.getInputStream()));
String line = "";
// reads message from client until "Over" is sent
boolean fim = false;
while (!line.equals("Over") && !fim)
{
try
{
line = in.readUTF();
System.out.println(line);
}
catch(IOException i)
{
fim = true;
System.out.println(i.toString());
}
}
System.out.println("Closing connection");
// close connection
socket.close();
saida.close();
in.close();
}
} catch (IOException i) {
System.out.println(i);
}catch(Exception e){
System.out.println(e.toString());
}
}
public static void main(String[] args) {
MeuServerSocket server = new MeuServerSocket(5000);
}
}
var socket = io('http://localhost:5000');
socket.on('connect', function () {
socket.send('hi \nOver');
socket.on('get', function (msg) {
// my msg
console.log('msg: '+msg)
})
socket.on('disconnect',()=>{
console.log('disconnected')
})
})
When i look at Firefox network, i see that the data was sent inside one of the packages...
https://imgur.com/vDAS00B
The biggest issue I'm seeing here is a misunderstanding of socket.io. Socket.io for javascript is not compatible with the Socket library in java. The naming conventions can be confusing for sure.
socket.io is a library that is related to web sockets (ws://). It implements all the basic websocket features plus some bonuses.
What you have for your java code is a TCP socket server. While websockets and socket.io are built on TCP socket, you can not connect a socket.io client to a "naked" socket server.
SOLUTION:
If your javascript is running from nodejs, you can use their net library found here. If you are running javascript from a webbrowser, than you are limited to websockets, which means you're going to change your java code to a websocket server. You can find a library for that somewhere online.
TLDR: Use ws://... instead of http://....
Details:
https is used for HTTP protocol. In such case it is correct that browser first asks your server if CORS is allowed. You have not enabled CORS. That's why it is normal that browser refuses to send CORS request.
But you say you want to use Web Sockets. Then you should use ws://, not http://. For Web Sockets there is no CORS policy and browser will send your request without CORS restrictions.
Summary:
IDE: Eclipse
Server: Java
Client: Html/Javascript
After sending data via ajax/jquery to a servlet and using JadeGateway to send those information to an agent, resulting data should go back to html/javascript.
I need a way to connect either a servlet or an agent with an existing and running html file (ofc without reloading the page).
Background:
I want to create a agent based game using babylon.js and jade/pug (let's call it jade).
During a game, information about game states should go to an agent, so that he could figure out what to do (e.g. create more units or attack). After this, the agent needs to send the information to the game, to call the appropriate function.
What I did so far:
I connected the game (html/javascript) with a servlet using ajax. There I used JadeGateway to send an ACLMessage to an agent.
I also used websockets to connect client/server but this method is too limited for what i need i think.
I now need a tool/method or tips to send information from an agent or the servlet. Both is possible, because the agent can send information back to the jadegateway agent/servlet.
I know how to use ajax to call a servlet from html and right back, but now the information is tranfered to some other classes, so that didn't work anymore.
In the babylon.js scene.registerBeforeRender function I do this:
$.ajax({
url: "AgentListenerServlet",
type: "POST",
data: {playerString: convertPlayerToJson(player1)},
success: function(data) {
alert(data.command)
},
error: function() {
alert("Listener Failed")
}
});
Sending message to an agent via jadegateway:
JadeGateway.execute(new CyclicBehaviour() {
public void action() {
final ACLMessage msgSend = new ACLMessage(ACLMessage.INFORM);
msgSend.setContent(message);
msgSend.addReceiver(new AID("First", AID.ISLOCALNAME));
myAgent.send(msgSend);
final ACLMessage msgReceive = myAgent.receive();
if(msgReceive != null && msgReceive.getContent() != null){
//used this as immediate answer for ajax success function, but maybe it can also be used to send the returning agent message?
Map <String, Object> map = new HashMap<String, Object>();
map.put("command", "information");
write(response, map);
} else {
block();
}
}
});
private void write(HttpServletResponse response, Map<String, Object> map) throws IOException {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(new Gson().toJson(map));
}
agent code (simplified):
public void action() {
final ACLMessage msgSend = new ACLMessage(ACLMessage.INFORM); //sending message
final ACLMessage msgReceive = receive(); //receiving message
//Here i get information out of sent json objects from jadegateway
//Maybe here send handled information back to html?
} else {
block();
}
}
Maybe showing this codes is not necessary, because they are working and the problem is not there, but the template for asking a question requested these.
Expected and results:
So the problem is not, that it isn't working, but that I need to know how to continue.
I don't know what I expect, because i don't know the possibilities.
Maybe, what would help me, is to send a simple string from the agent or servlet that i can call with the alert function in the game.
Or maybe i should ask this: is it possible to get an answer in ajax (success function) after the data was sent to the servlet and passed to an agent?
Sorry for the huge spam. Hope I made my problem clear.
No need to reply, websockets is the solution after all.
I just figured out, that you can push data from server to client without a client request with
session.getBasicRemote().sendText(msgReceive.getContent());
The session is automatically generated and you can reveice it with websockets onopen method:
#OnOpen
public void onOpen(Session session){
this.session = session;
}
I'm going on a long flight tomorrow and I'd like to be able to keep testing my cloud endpoints REST API while offline. The problem is that the User object is integral to most of my methods, and I need an internet connection to create valid OAuth tokens to call them from the client side (JavaScript).
On the Dev server though, no matter what account you log in on, the user is always the same (with email example#example.com). But if you feed it bogus tokens, it throws an OAuthRequestException.
Is there any way I can generate valid test tokens offline for the dev server or a way to access the User object without providing tokens at all?
Here's an example of a method I'd like to test while offline:
#ApiMethod(name = "hylyts.get")
public Hylyt getHylyt(#Named("url") String url, #Named("id") long id, User user)
throws OAuthRequestException, UnauthorizedException {
return ofy().load().type(Hylyt.class).parent(util.getArticleKey(url, user)).id(id).now();
}
There's a little documented way to inject a custom Authenticator class in Cloud Endpoints. This allows you to change the way the User is detected.
Here's how it works :
#Api(name = "myapi", version = "v1", authenticators = {MyDummyAuthenticator.class})
public class MyAPI {
#ApiMethod(name = "hylyts.get")
public Hylyt getHylyt(#Named("url") String url, #Named("id") long id, User user)
throws OAuthRequestException, UnauthorizedException {
return ofy().load().type(Hylyt.class).parent(util.getArticleKey(url, user)).id(id).now();
}
}
And here's what your Authenticator implementation could look like :
public class MyDummyAuthenticator implements Authenticator {
#Override
public User authenticate(HttpServletRequest httpServletRequest) {
return new User("mytestuser#domain.com");
}
}
You can of course make it more complicated. Since you have access to the HttpServletRequest you can get the user's email from a HTTP header or something like it.
Note that with an Authenticator you have access to the session in the local server but not in production. In production, httpServletRequest.getSession() will return null. THere's a trick to still fetch the session from the datastore, which I explain here.
Then there's the question of how to keep both the normal authentication solution and your DummyAuthenticator implementation. I think you can chain authenticators, but I'm not sure how it works. In the worst case, you can just swap the Authenticator implementation during your flights.
I am new in the area of websocket.
I can connect to websocket server using JavaScript using this code:
var webSocket = new WebSocket(url);
But for my application, I need to connect to the same server using c#. The code I am using is:
ClientWebSocket webSocket = null;
webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(url), CancellationToken.None);
3rd line of the code results following error:
"Server returned status code 200 when status code 101 was expected"
After little bit of survey, I realised that somehow server can't switch http protocol to websocket protocol during connection process.
Am I doing anything stupid in my C# code or there is something going wrong with the server. I don't have any access to the server, as the url I am using is a third party one .
Could you please give me any suggestion regarding the issue?
TL; DR:
Use ReceiveAsync() in loop until Close frame is received or CancellationToken is canceled. That's how you get your messages. Sending is straightworward, just SendAsync(). Do not use CloseAsync() before CloseOutputAsync() - because you want to stop your receiving loop first. Otherwise - either the CloseAsync() would hang, or if you use CancellationToken to quit ReceiveAsync() - the CloseAsync() would throw.
I learned a lot from https://mcguirev10.com/2019/08/17/how-to-close-websocket-correctly.html .
Full answer:
Use Dotnet client, here, have an example cut out from my real life code, that illustrate how the handshaking is made. The most important thing most people don't understand about how the thing operates is that there is no magic event when a message is received. You create it yourself. How?
You just perform ReceiveAsync() in a loop that ends, when a special Close frame is received. So when you want to disconnect you have to tell the server you close with CloseOutputAsync, so it would reply with a similar Close frame to your client, so it would be able to end receiving.
My code example illustrates only the most basic, outer transmission mechanism. So you send and receive raw binary messages. At this point you cannot tell the specific server response is related to the specific request you've sent. You have to match them yourself after coding / decoding messages. Use any serialization tool for that, but many crypto currency markets use Protocol Buffers from Google. The name says it all ;)
For matching any unique random data can be used. You need tokens, in C# I use Guid class for that.
Then I use request / response matching to make request work without dependency on events. The SendRequest() methods awaits until matching response arrives, or... the connection is closed. Very handy and allows to make way more readable code than in event-based approach. Of course you can still invoke events on messages received, just make sure they are not matched to any requests that require response.
Oh, and for waiting in my async method I use SemaphoreSlim. Each request puts its own semaphore in a special dictionary, when I get the response, I find the entry by the response token, release the semaphore, dispose it, remove from the dictionary. Seems complicated, but it's actually pretty simple.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
namespace Example {
public class WsClient : IDisposable {
public int ReceiveBufferSize { get; set; } = 8192;
public async Task ConnectAsync(string url) {
if (WS != null) {
if (WS.State == WebSocketState.Open) return;
else WS.Dispose();
}
WS = new ClientWebSocket();
if (CTS != null) CTS.Dispose();
CTS = new CancellationTokenSource();
await WS.ConnectAsync(new Uri(url), CTS.Token);
await Task.Factory.StartNew(ReceiveLoop, CTS.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
public async Task DisconnectAsync() {
if (WS is null) return;
// TODO: requests cleanup code, sub-protocol dependent.
if (WS.State == WebSocketState.Open) {
CTS.CancelAfter(TimeSpan.FromSeconds(2));
await WS.CloseOutputAsync(WebSocketCloseStatus.Empty, "", CancellationToken.None);
await WS.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
}
WS.Dispose();
WS = null;
CTS.Dispose();
CTS = null;
}
private async Task ReceiveLoop() {
var loopToken = CTS.Token;
MemoryStream outputStream = null;
WebSocketReceiveResult receiveResult = null;
var buffer = new byte[ReceiveBufferSize];
try {
while (!loopToken.IsCancellationRequested) {
outputStream = new MemoryStream(ReceiveBufferSize);
do {
receiveResult = await WS.ReceiveAsync(buffer, CTS.Token);
if (receiveResult.MessageType != WebSocketMessageType.Close)
outputStream.Write(buffer, 0, receiveResult.Count);
}
while (!receiveResult.EndOfMessage);
if (receiveResult.MessageType == WebSocketMessageType.Close) break;
outputStream.Position = 0;
ResponseReceived(outputStream);
}
}
catch (TaskCanceledException) { }
finally {
outputStream?.Dispose();
}
}
private async Task<ResponseType> SendMessageAsync<RequestType>(RequestType message) {
// TODO: handle serializing requests and deserializing responses, handle matching responses to the requests.
}
private void ResponseReceived(Stream inputStream) {
// TODO: handle deserializing responses and matching them to the requests.
// IMPORTANT: DON'T FORGET TO DISPOSE THE inputStream!
}
public void Dispose() => DisconnectAsync().Wait();
private ClientWebSocket WS;
private CancellationTokenSource CTS;
}
}
BTW, why use other libraries than the .NET built in? I can't find any reason other than maybe poor documentation of the Microsoft's classes. Maybe - if for some really weird reason you would want to use modern WebSocket transport with an ancient .NET Framework ;)
Oh, and I haven't tested the example. It's taken from the tested code, but all inner protocol parts were removed to leave only the transport part.
Since WebsocketSharp is not .NET Core compatible I suggest using websocket-client instead.
Here's some sample code
static async Task Main(string[] args)
{
var url = new Uri("wss://echo.websocket.org");
var exitEvent = new ManualResetEvent(false);
using (var client = new WebsocketClient(url))
{
client.MessageReceived.Subscribe(msg => Console.WriteLine($"Message: {msg}"));
await client.Start();
await client.Send("Echo");
exitEvent.WaitOne();
}
Console.ReadLine();
}
Be sure to use ManualResetEvent. Otherwise it doesn't work.
If you connect with a WebSocket client and you get an HTTP 200 as response, means that probably you are connecting to the wrong place (host, path and/or port).
Basically, you are connecting to a normal HTTP endpoint that is not understanding your WebSocket requirement, and it is just returning the "OK" response (HTTP 200). Probably the WebSocket server runs in another port or path in the same server.
Check your URL.
Not quite sure what happened to WebSocketSharp nuget package, however I noticed that now WebSocket# is showing up as most relevant result in nuget repo. It took me some time before I realized that Connect() is now returning Task, hopefully this example will be useful to someone:
using System;
using System.Threading.Tasks;
using WebSocketSharp;
namespace Example
{
class Program
{
private static void Main(string[] args)
{
using (var ws = new WebSocket(url: "ws://localhost:1337", onMessage: OnMessage, onError: OnError))
{
ws.Connect().Wait();
ws.Send("Hey, Server!").Wait();
Console.ReadKey(true);
}
}
private static Task OnError(ErrorEventArgs errorEventArgs)
{
Console.Write("Error: {0}, Exception: {1}", errorEventArgs.Message, errorEventArgs.Exception);
return Task.FromResult(0);
}
private static Task OnMessage(MessageEventArgs messageEventArgs)
{
Console.Write("Message received: {0}", messageEventArgs.Text.ReadToEnd());
return Task.FromResult(0);
}
}
}
All the libraries mentioned above are Wrappers. The .Net Frameworks class doing this is System.Net.WebSockets.ClientWebSocket
Websocket URLs should start with ws:// or wss:// where the latter is secure websocket.
I have generated a UDID string on the client side of Android. I need to send this to the javascript side so that it could communicate to the database server (via php) in order to store this UDID.
Is there a way to do it either directly (by avoiding php) or otherwise?
** My Android application has a WebView
Post to http with params:
public void postData() {
// Create a new HttpClient and Post Header
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://www.yoursite.com/yourexample.php");
try {
// Add your data
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair("UDID", "12345"));
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
} catch (IOException e) {
// TODO Auto-generated catch block
}
}
for your server side maybe it would be useful to learn somethings about web api's. take a look at this 2 links to start from some place:
Creating a RESTful API with PHP
[GUIDE] Android Client-Server Communication (PHP-MYSQL REST API)
You should show what you have already done, take a look at this to make better - to the point- questions
If you want to store data in a database from an Android app, without having to learn a server-side language, the simplest soulution is to use a MBaaS (Mobile Backend as a service) like :
Firebase
Appback
If you want to have lower fees you could also host your own solution but this could get more complicated:
http://hood.ie/
http://firehose.io/
http://remotestorage.io/
http://socky.org/
http://sockethub.org/
http://deployd.com/
My personal preference is Hoodie.
You could also generate your own RESTful API using Node.js + Express with MongoDB :
Yeoman generator for RESTful API