I need a JSON-Rpc client to communicate over websocket to the server. In particular, I need to create an interface and use methods to send JSON requests to the server.
Does someone know how to do that?
I found the StreamJsonRpc library, but it works over stream and not over websocket.
Can I get the stream from websocket connection and pas it to StreamJsonRpc?
Do you have other ideas?
You only need Json.net and WebSocket4Net.
You can see there.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Security.Authentication;
using WebSocket4Net;
namespace LightStreamSample
{
class WebSocket4NetSample
{
static void Main(string[] args)
{
var channelName = "[your websocket server channel name]";
// note: reconnection handling needed.
var websocket = new WebSocket("wss://[your web socket server websocket url]", sslProtocols: SslProtocols.Tls12);
websocket.Opened += (sender, e) =>
{
websocket.Send(
JsonConvert.SerializeObject(
new
{
method = "subscribe",
#params = new { channel = channelName },
id = 123,
}
)
);
};
websocket.MessageReceived += (sender, e) =>
{
dynamic data = JObject.Parse(e.Message);
if (data.id == 123)
{
Console.WriteLine("subscribed!");
}
if (data.#params != null)
{
Console.WriteLine(data.#params.channel + " " + data.#params.message);
}
};
websocket.Open();
Console.ReadKey();
}
}
}
To update this post, it is now possible to pass the stream from the websocket using StreamJsonRpc.
Github websocket sample for .NetCore
using (var jsonRpc = new JsonRpc(new WebSocketMessageHandler(socket)))
{
try
{
jsonRpc.AddLocalRpcMethod("Tick", new Action<int>(tick => Console.WriteLine($"Tick {tick}!")));
jsonRpc.StartListening();
Console.WriteLine("JSON-RPC protocol over web socket established.");
int result = await jsonRpc.InvokeWithCancellationAsync<int>("Add", new object[] { 1, 2 }, cancellationToken);
Console.WriteLine($"JSON-RPC server says 1 + 2 = {result}");
// Request notifications from the server.
await jsonRpc.NotifyAsync("SendTicksAsync");
await jsonRpc.Completion.WithCancellation(cancellationToken);
}
catch (OperationCanceledException)
{
// Closing is initiated by Ctrl+C on the client.
// Close the web socket gracefully -- before JsonRpc is disposed to avoid the socket going into an aborted state.
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Client closing", CancellationToken.None);
throw;
}
}
The .Net Framework implemenation is similar but slightly different
Related
I am using NetMQ v3.3.3.4 to develop a simple server(RouterSocket) - client(RequestSocket) scenario. It works as expected except for one thing.
Sometimes the server takes too long to return a response and the client reaches a timeout. This behavior is desired. The issue here is that the server does not detect that the client is already gone and it continues processing the request. At some point the processing is done and the server tries to return a response. The server never realizes that the client is not listening anymore and thinks that a response has been successfully delivered. This leads to a misleading user feedback and wrong log file entries.
Is it possible for the server to detect if a client is still waiting for a response?
Server
private void Listen()
{
using (var poller = new NetMQPoller())
using (var responseSocket = new RouterSocket())
using (var poisonedSocket = new ResponseSocket())
{
responseSocket.Bind(_bindServerString);
poisonedSocket.Bind(_bindTerminationString);
var keepAliveTimer = new NetMQTimer(KeepAliveInterval);
keepAliveTimer.Elapsed += (s, a) =>
{
_monitorPublisher.Status["KeepAlive"] = Application.LocalDateTime.Now.ToString();
_monitorPublisher.SendStatus();
};
poisonedSocket.ReceiveReady += (s, a) =>
{
a.Socket.ReceiveFrameBytes();
poller.Stop();
};
responseSocket.ReceiveReady += (s, a) =>
{
try
{
bool header = true;
byte[] response = null;
while (a.Socket.TryReceiveFrameBytes(out byte[] message))
{
// Using a ROUTER socket...
if (header)
{
// We have to first, wait for the first frame,
// which is the address (header) of the REQ socket.
// Afterwards, we have to wait for the second frame,
// which is 0 and act as a delimiter between the REQ address
// and the actual body of the request.
a.Socket.SendFrame(message, true);
// Once we have the header delimiter, we can be sure that
// the next frame is the actual request.
header = !(message.Length == 0);
}
else
{
// Parse the request and return a response
BridgeRequestReceived?.Invoke(message, out response);
a.Socket.SendFrame(response);
}
}
}
catch (Exception e)
{
Logger.Instance.Log(e);
}
};
poller.Add(keepAliveTimer);
poller.Add(responseSocket);
poller.Add(poisonedSocket);
Logger.Instance.Log($"Receiving requests on: {_bindServerString}", Application.LogCategory.Info);
poller.Run();
responseSocket.Close();
poisonedSocket.Close();
}
}
Client
private string SendRequest(IBridgeMessage request, out Exception e)
{
e = null;
string response = null;
using (var socket = new RequestSocket())
{
try
{
socket.Connect(_endpoint);
byte[] encryptedRequest = CryptoAES.StandardCrypto.Encrypt(Encoding.UTF8.GetBytes(NMTBridgeMessageFactory.Serialize(request)));
socket.SendFrame(encryptedRequest);
if (socket.TryReceiveFrameBytes(_timeout, out byte[] reply))
{
response = Encoding.UTF8.GetString(CryptoAES.StandardCrypto.Decrypt(reply));
}
else
{
e = new TimeoutException("Timeout occured!");
response = null;
}
}
catch (Exception ex)
{
e = ex;
response = null;
}
}
return response;
}
I expect the server detects a client disconnection.
I have a tcp connection I want to keep open in the HandleConnectionAsync method of the server class. It will be receiving continuous updates from a client.
private async void HandleConnectionAsync(TcpClient tcpClient)
{
Console.WriteLine("Got connection request from {0}", tcpClient.Client.RemoteEndPoint.ToString());
try
{
using (var networkStream = tcpClient.GetStream())
using (var reader = new StreamReader(networkStream))
using (var writer = new StreamWriter(networkStream))
{
writer.AutoFlush = true;
while (true)
{
string dataFromClient = await reader.ReadLineAsync();
Console.WriteLine(dataFromClient);
await writer.WriteLineAsync("FromServer-" + dataFromClient);
}
}
}
catch (Exception exp)
{
Console.WriteLine(exp.Message);
}
}
I want to be able to receive the updates the reader puts into dataFromClient without having to put my code in the midst of my server implementation class.
So:
static void Main(string[] args)
{
Server s = new Server();
s.start();
//get dataFromClient as it comes in here
}
Problem is I'm not sure how to do this. Some kind of callback or event?
There are many many ways.
The least code is to pass an Action to your server eg:
Server s = new Server();
s.start(dataFromClient =>
{
// do something with data from client
});
And in HandleConnectionAsync call the action eg:
private async void HandleConnectionAsync(TcpClient tcpClient, Action<string> callback)
{
..
// Console.WriteLine(dataFromClient);
callback(dataFromClient);
..
However this does not address a few things e.g. many clients at the same time and needing to know which client is which.
I have been having a horrible problem with trying to get this socket on the raspberry pi to correctly communicate to the PC socket. It is a TCP client-to-server setup. Both are running .net C# applications to do the communication. The main issue is not the connection, but when I send info from the raspberry pi to the computer, the computer only gets the info when I shutdown the raspberry... I believe it has to do with caching, but how would I fix this? Just a fair warning I am not well verse with sockets.
the Raspberry Pi Client Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using Windows.UI.ViewManagement;
using System.Net.Sockets;
using Windows.Web.Http;
using Windows.Devices.Gpio;
using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using System.Threading;
using System.Runtime.InteropServices;
namespace raspberrypipad
{
class TCPclient_Client
{
private string _ip;
private int _port;
private Socket _socket;
private DataWriter _writer;
private DataReader _reader;
public delegate void Error(string message);
public event Error OnError;
public delegate void DataRecived(string data);
public event DataRecived OnDataRecived;
public string Ip
{
get { return _ip; }
set { _ip = value; }
}
public int Port
{
get { return _port; }
set { _port = value; }
}
private static ManualResetEvent _clientDone = new ManualResetEvent(false);
const int TIMEOUT_MILLISECONDS = 1000;
public async void Connect(string devicename, string pin_number, string get_portNumber, string current_ip)
{
try
{
if (Port == 0)
{
_port = 4444;
}
string result = string.Empty;
DnsEndPoint hostEntry = new DnsEndPoint(Ip, Port);
var hostName = new HostName(Ip);
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
socketEventArg.RemoteEndPoint = hostEntry;
// Inline event handler for the Completed event.
// Note: This event handler was implemented inline in order to make this method self-contained.
socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate (object s, SocketAsyncEventArgs e)
{
// Retrieve the result of this request
result = e.SocketError.ToString();
// Signal that the request is complete, unblocking the UI thread
_clientDone.Set();
});
_clientDone.Reset();
// Make an asynchronous Connect request over the socket
_socket.ConnectAsync(socketEventArg);
_socket.AcceptAsync(socketEventArg);
// Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds.
// If no response comes back within this time then proceed
_clientDone.WaitOne(TIMEOUT_MILLISECONDS);
_socket.NoDelay = true;
}
catch (Exception ex)
{
//ignore
}
}
public string Send(string data)
{
string response = "Operation Timeout";
// We are re-using the _socket object initialized in the Connect method
if (_socket != null)
{
// Create SocketAsyncEventArgs context object
SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
// Set properties on context object
socketEventArg.RemoteEndPoint = _socket.RemoteEndPoint;
socketEventArg.UserToken = null;
// Inline event handler for the Completed event.
// Note: This event handler was implemented inline in order
// to make this method self-contained.
socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate (object s, SocketAsyncEventArgs e)
{
response = e.SocketError.ToString();
e.Dispose();
// Unblock the UI thread
_clientDone.Set();
});
// Add the data to be sent into the buffer
byte[] payload = Encoding.UTF8.GetBytes(data);
socketEventArg.SetBuffer(payload, 0, payload.Length);
// Sets the state of the event to nonsignaled, causing threads to block
_clientDone.Reset();
// Make an asynchronous Send request over the socket
_socket.SendBufferSize = payload.Length;
_socket.SendToAsync(socketEventArg);
// _socket.Shutdown(SocketShutdown.Send);
// Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds.
// If no response comes back within this time then proceed
_clientDone.WaitOne(TIMEOUT_MILLISECONDS);
}
else
{
response = "Socket is not initialized";
}
return response;
}
public void close()
{
_socket.Dispose();
}
}
}
The server code for the computer is a little confusing, I know it works because other programs could send data to it in other languages... here is the code:
public TcpServerCode(int port)
{
_server = new TcpListener(IPAddress.Any, port);
_server.Start();
_isRunning = true;
if (!Directory.Exists(Environment.CurrentDirectory + "\\serverCOM"))
Directory.CreateDirectory(Environment.CurrentDirectory + "\\serverCOM");
var th = new Thread(LoopClients);
th.Start();
}
public void LoopClients()
{
while (_isRunning)
try
{
// wait for client connection
var newClient = _server.AcceptTcpClient();
// client found.
// create a thread to handle communication
var t = new Thread(HandleClient);
var ch = new clientHolder();
ch.generateRandomName();
ch._tcp = newClient;
clientHolderList.Add(ch);
t.Start(ch);
}
catch (Exception i)
{
Log.handleException(i);
}
}
public void HandleClient(object obj)
{
// retrieve client from parameter passed to thread
var client = (clientHolder) obj;
// sets two streams
//StreamWriter sWriter = new StreamWriter(client._tcp.GetStream(), Encoding.ASCII);
var ns = client._tcp.GetStream();
client._tcp.GetStream().ReadTimeout = 100;
var sReader = new StreamReader(client._tcp.GetStream(), Encoding.ASCII);
// you could use the NetworkStream to read and write,
// but there is no forcing flush, even when requested
var bClientConnected = true;
string sData = null;
Task getter = null;
Task sender = null;
while (bClientConnected)
if (getter != null)
{
if (getter.IsCompleted)
getter = _get(sReader, client.clientName);
}
else
{
getter = _get(sReader, client.clientName);
}
}
private static async Task _get(StreamReader reader, string clientName)
{
string sData = null;
sData = await reader.ReadLineAsync();
reader.DiscardBufferedData();
Console.WriteLine(sData);
// shows content on the console.
if (sData != null)
try
{
if (File.Exists(Environment.CurrentDirectory + "\\serverCOM\\" + clientName + "_get.txt"))
File.Delete(Environment.CurrentDirectory + "\\serverCOM\\" + clientName + "_get.txt");
File.WriteAllText(Environment.CurrentDirectory + "\\serverCOM\\" + clientName + "_get.txt", sData);
}
catch (Exception i)
{
Log.handleException(i);
}
}
I am assuming it is something simple to change on one of the programs, thank you in advance!
I am relatively new to ZeroMq.
What i want to do is send requests and receive replies at the same time.
I read a lot of examples and the ZeroMq guide but i dont know how to achieve this.
Currently my code looks this way:
public void SendRequestAsync<T>(T request)
{
Task.Factory.StartNew(() =>
{
using (ZmqSocket socket = _context.CreateSocket(SocketType.REQ))
{
string endPoint = string.Format("tcp://{0}:{1}", ServiceIpAddress, Port);
socket.Connect(endPoint);
var serializer = new XmlSerializer(request.GetType());
using (var byteStream = new MemoryStream())
{
serializer.Serialize(byteStream, request);
socket.Send(byteStream.ToArray());
}
}
});
if (!_isListening)
{
StartListeningForReplies();
}
}
private void StartListeningForReplies()
{
_responseThread = new Thread(ReplyThreadStart);
_responseThread.Start();
_isListening = true;
}
private void ReplyThreadStart()
{
using (var replySocket = _context.CreateSocket(SocketType.REQ))
{
string endPoint = string.Format("tcp://*:{0}", Port);
replySocket.Bind(endPoint);
while (!_listeningAborted)
{
byte[] buffer = new byte[1024];
replySocket.Receive(buffer);
// put into queue for processing
}
}
}
I have created a single context before and use this context to create the sockets.
Do i need to use different ports for sending and receiving, and yet another port for publishing?
Please help.
ther is a server and client sockets. I want to learn a way how can I send a List<T>, IEnumerable<T> object to client to server or server to client.
i want to send server-side TaskManager Threads List to client-side. this code send line by line, I want to send complate list. How can I do it?
private static IEnumerable<string> TaskManager()
{
List<string> lst = new List<string>();
foreach (System.Diagnostics.Process p in
Process.GetProcesses().OrderBy(o => o.ProcessName))
{
lst.Add(p.ProcessName + "_" + p.Id);
}
return lst.AsParallel();
}
....
....
....
while (true)
{
Socket socket = Listener.AcceptSocket();
try
{
//open stream
Stream stream = new NetworkStream(socket);
StreamReader sr = new StreamReader(stream);
StreamWriter sw = new StreamWriter(stream);
sw.AutoFlush = true;
while (true)
{
Parallel.ForEach(
TaskManager(), item=>
sw.WriteLine(item)
);
}
stream.Close();
stream.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("Disconnected: {0}", socket.RemoteEndPoint);
socket.Close();
socket.Dispose();
}
You can use BinaryFormatter to serialize and deserialze a List.
using System.Runtime.Serialization.Formatters.Binary;
// Server side
Stream stream = new NetworkStream(socket);
var bin = new BinaryFormatter();
bin.Serialize(stream, TaskManager());
// Client side
Stream stream = new NetworkStream(socket);
var bin = new BinaryFormatter();
var list = (List<string>)bin.Deserialize(stream);
You could implement all aspects of a solution yourself, i.e. serialisation, compression, sending, receiving etc. Most things can be found by reading about WCF or the System.Net namespace. I can however give you a very concise solution using the network library NetworkComms.Net here.
For the client:
using System;
using NetworkCommsDotNet;
namespace Client
{
class Program
{
static void Main(string[] args)
{
//Create a connection
Connection connection = TCPConnection.GetConnection(new ConnectionInfo("127.0.0.1", 10000));
//Make a request for the threadIds and get the answer in one statement.
string[] taskManagerThreadIds = connection.SendReceiveObject<string[]>("ThreadIdRequest", "ThreadIds", 2000);
Console.WriteLine("Server provided an array containing {0} ids", taskManagerThreadIds.Length);
Console.WriteLine("Send completed. Press any key to exit client.");
Console.ReadKey(true);
NetworkComms.Shutdown();
}
}
}
For the server:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using NetworkCommsDotNet;
namespace Server
{
class Program
{
static void Main(string[] args)
{
NetworkComms.AppendGlobalIncomingPacketHandler<object>("ThreadIdRequest", (packetHeader, connection, incomingPlayer) =>
{
//Reply with the requested threadIds
Console.WriteLine("Received thread ID request from {0}.", connection.ToString());
connection.SendObject("ThreadIds", TaskManager().ToArray());
});
//Start listening for incoming TCP Connections
TCPConnection.StartListening(true);
Console.WriteLine("Server ready. Press any key to shutdown server.");
Console.ReadKey(true);
NetworkComms.Shutdown();
}
private static IEnumerable<string> TaskManager()
{
List<string> lst = new List<string>();
foreach (System.Diagnostics.Process p in
Process.GetProcesses().OrderBy(o => o.ProcessName))
{
lst.Add(p.ProcessName + "_" + p.Id);
}
return lst.AsParallel();
}
}
}
You will obviously need to download the NetworkComms.Net DLL from the website so that you can add it in the 'using NetworkCommsDotNet' reference. Also see the server IP address in the client example is currently "127.0.0.1", this should work if you run both the server and client on the same machine.
Disclaimer: I'm one of the developers for this library.
If you want a reliable and robust solution, use WCF instead of implementing serialization yourself.
Also, writing to stream from parallel threads would not work correctly. Parts of lines from different threads would mix together to some intangible garbage:
Parallel.ForEach(
TaskManager(), item=>
sw.WriteLine(item)
);