Ok so I'm working on a simple data entry software for work, using a 3-tier architecture. What I'm doing is basically using a common dll file to store classes with fields that are filled out when a request to the server is initialized. I have various user controls set up to take care of different ui forms, this works just fine if I open a connection and send the object directly from the form itself, but that creates a lot of redundant code. So I thought, lets make a MasterControl and create a public Connect_To_Server method to send objects over and then process the information sent back. Only problem is it keeps freezing my server.
Here is the code for my MasterControl:
public void Connect_To_Server(object obj)
{
using (TcpClient tcpClient = new TcpClient())
{
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("10.20.51.251"), 57657);
try
{
tcpClient.Connect(endPoint);
NetworkStream clientStream = tcpClient.GetStream();
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(clientStream, obj);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + " - " + ex.StackTrace);
}
}
}
Of course it's going to be a bit more complex later on but just for testing I've made it simple. Then from one of my ControlForms I create an object from one of the classes listed in the Common.dll, assign it it's values and then pass it to the Connect_To_Server method.
For instance a couple classes from my Common.dll
[Serializable]
public class Switch_Request // Switch class contains an integer describing a boolean request
{
public int switchID { get; set; }
}
[Serializable]
public class Send_String_List // Returns a list of strings
{
public int switchID { get; set; }
public List<string> stringsList { get; set; }
}
On my Server's side:
private void bt_StartServer_Click(object sender, EventArgs e) // Starts the server listening for clients
{
Set_String("System: Starting Services.");
tm_ActiveTime.Start();
serverListener = new TcpListener(IPAddress.Any, port);
serverThread = new Thread(new ThreadStart(Listen_For_Clients));
serverThread.Start();
}
private void Listen_For_Clients() // Listens for incoming client connections then redirects
{
serverListener.Start();
Set_String("System: Listening for connections...");
while (true)
{
tcpClient = serverListener.AcceptTcpClient();
clientThread = new Thread(new ParameterizedThreadStart(Handle_Client_Communication));
clientThread.Start(tcpClient);
}
}
private void Handle_Client_Communication(object client) // Handles incoming client communications
{
TcpClient tempClient = (TcpClient)client;
clientStream = tempClient.GetStream();
object sentObjet = formatter.Deserialize(clientStream);
Type objectType = sentObjet.GetType();
else if(objectType == typeof(Switch_Request)) // Handle - Switch Request
{
Switch_Request switchRequest = (Switch_Request)sentObjet;
object obj = new object();
if (switchRequest.switchID == 1)
{
}
else if (switchRequest.switchID == 2)
{
}
else if (switchRequest.switchID == 3)
{
}
else if (switchRequest.switchID == 4)
{
}
else if (switchRequest.switchID == 5)
{
}
else if (switchRequest.switchID == 6)
{
Send_String_List sendStringList = new Send_String_List();
sendStringList.switchID = 6;
sendStringList.stringsList = Get_Users();
obj = sendStringList;
}
else if (switchRequest.switchID == 7)
{
}
else if (switchRequest.switchID == 8)
{
}
else if (switchRequest.switchID == 9)
{
}
else if (switchRequest.switchID == 10)
{
}
formatter.Serialize(clientStream, obj);
Now I know that the object is being sent and received on the other end, as I set a little text display to popup when the object arrives on the other end, and it displays the correct object and it's type just before the server freezes. I'm just not sure why it keeps seizing up....
Related
For a few months now I have been using the ktor framework to create servers that expose rest calls and communication via webSockets. For now I have always used clients using kotlin as a programming language (or Android App, or App Desktop).
Specifically, I had a class that was injected with the HttpClient object (from the documentation = Asynchronous client to perform HTTP requests).
Within this class I have 4 methods:
start the session: instantiate the WebSocketSession object (Represents a web socket session between two peers)
send Frame
receives Frame
close the session
In Ktor my class is something that looks a lot like this:
class WebSocketServiceImpl(
private val client: HttpClient
){
private var socket: WebSocketSession? = null
//1)
suspend fun initSession(username: String): Resource<Unit>{
socket = client.webSocketSession {
url("ws://xxx.xxx.xxx.xxx:xxxx/myRoute?username=$username")
}
//2)
suspend fun send(myObj: MyObj) {
try {
val myObjSerialized = Json.encodeToString(myObj)
socket?.send(Frame.Text(myObjSerialized ))
} catch (e: Exception) {
e.printStackTrace()
}
}
//3)
fun observePrintableMessages(): Flow<MyObj> {
return try {
socket?.incoming
?.receiveAsFlow()
?.filter { it is Frame.Text }
?.map {
val myObjString = (it as? Frame.Text)?.readText() ?: ""
val printableMessageDto = Json.decodeFromString<MyObj>(myObjString)
} ?: flow { }
} catch (e: Exception) {
e.printStackTrace()
flow { }
}
}
//4)
suspend fun closeSession() {
socket?.close()
}
}
From the C # documentation instead, I found these examples on how to use Client-side WebSockets:
//1)
const exampleSocket = new WebSocket("wss://www.example.com/socketserver", "protocolOne");
//2)
exampleSocket.send("Here's some text that the server is urgently awaiting!");
//3)
exampleSocket.onmessage = (event) => {
console.log(event.data);
}
//4)
exampleSocket.close();
Admitted and not granted that the methods I found in C # really work, to make the WebSocket object used in C # be equivalent to the WebSocketSession object in Kotlin is enough for me to do so? :
public void initSession(string username)
{
exampleSocket = new WebSocket($"wss://www.example.com/socketserver?username={username}", "");
}
Or is it some other type of object to use?
If for any reason you don't know the answer, you don't need to vote negative, you can just move on.
I used the Websocket.Client library (by Mariusz Kotas) found on NuGet
public class WebSocketService : IWebSocketService
{
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
private void FireMessageReceivedEvent(Message message) => MessageReceived?.Invoke(this, new MessageReceivedEventArgs(message));
public string Url { get => "ws://192.168.1.202:8082/chat-socket"; }
private WebsocketClient webSocketClient;
public async Task<SessionResoult> InitSession(string username)
{
string usernameSession = $"?username={username}";
string urlWithUsername = $"{Url}{usernameSession}";
try
{
webSocketClient = new WebsocketClient(new Uri(urlWithUsername));
await webSocketClient.Start();
if (webSocketClient.IsRunning)
{
SubscribeNewMessages();
return new SessionResoult.Success();
}
else
{
return new SessionResoult.Error("webSocketClient is not running");
}
}
catch(Exception ex)
{
return new SessionResoult.Error(ex.Message);
}
}
private void SubscribeNewMessages()
{
webSocketClient.MessageReceived.Subscribe(m =>
{
MessageDto message = JsonConvert.DeserializeObject<MessageDto>(m.Text);
FireMessageReceivedEvent(message.ToMessage());
});
}
public async Task SendMessageAsync(string message)
{
await Task.Run(() => webSocketClient.Send(message));
}
public void CloseSession()
{
webSocketClient.Dispose();
}
}
In the code, the interesting parts are:
1) initialization of the WebsocketClient object
2) the subscription of receiving messages ( Start() method immediately after initialization)
3) observation of message subscription -> webSocketClient.MessageReceived.Subscribe
4) the 'Fire' of the event linked to the observation of messages -> FireMessageReceivedEvent
5) those who use the class must subscribe to the event of the latter ->
webSocketService.MessageReceived + = (sender, e) => {OnMessageReceived (e.MessageReceived); };
MessageReceivedEventArgs -> Class describing the Arguments of the event
SessionResoult -> Class similar to an Enum but with the possibility of passing a string or not based on which subclass it is
I have to listen to several udp sockets and receive multicast datagrams. After receiving from some socket, data has to be sent to the TransformBlock. I don't want to copy received data for each received packet, so data buffer is sent to the TransformBlock by reference. And that's why each particular socket has to wait for the TransformBlock to finish processing before it can begin reading to it's buffer again.
I used code from this article for async operations with sockets.
Also I need an ability to add or delete sockets which to listen during the program execution.
My multicast listener class:
using ChannelWithDecoder = Tuple<FastChannel, TransformBlock<SocketAsyncEventArgs, List<Message>>>;
class MulticastReceiveManager
{
const int MaxPacketSize = 1400;
readonly ConcurrentDictionary<int, SocketReadData> socketDataByPort = new ConcurrentDictionary<int, SocketReadData>();
public MulticastReceiveManager(IEnumerable<ChannelWithDecoder> channelsWithDecoders)
{
foreach (ChannelWithDecoder tuple in channelsWithDecoders) AddChannel(tuple.Item1, tuple.Item2);
}
static Socket CreateSocket(FastChannel channel)
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
var end = new IPEndPoint(IPAddress.Any, channel.Port);
socket.Bind(end);
//подписываемся на source specific multicast
var membershipAddresses = new byte[12]; // 3 IPs * 4 bytes (IPv4)
IPAddress mcastGroup = IPAddress.Parse(channel.IP);
IPAddress mcastSource = IPAddress.Parse(channel.SourceIP);
Buffer.BlockCopy(mcastGroup.GetAddressBytes(), 0, membershipAddresses, 0, 4);
Buffer.BlockCopy(mcastSource.GetAddressBytes(), 0, membershipAddresses, 4, 4);
Buffer.BlockCopy(IPAddress.Any.GetAddressBytes(), 0, membershipAddresses, 8, 4);
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddSourceMembership, membershipAddresses);
return socket;
}
public void AddChannel(FastChannel channel, TransformBlock<SocketAsyncEventArgs, List<Message>> decoderTransformBlock)
{
var args = new SocketAsyncEventArgs();
args.SetBuffer(new byte[MaxPacketSize], 0, MaxPacketSize);
var socketAwaitable = new ReceiveSocketAwaitable(args);
Socket socket = CreateSocket(channel);
var socketReadData = new SocketReadData(socket, socketAwaitable, decoderTransformBlock);
if (!socketDataByPort.TryAdd(channel.Port, socketReadData))
throw new ArgumentException("Channel with port number = " + channel.Port + " already exists in dictionary. IP = " + channel.IP);
}
public void DeleteChannel(FastChannel channel)
{
SocketReadData socketReadData;
if (!socketDataByPort.TryRemove(channel.Port, out socketReadData))
throw new ArgumentException("Channel with port number = " + channel.Port + " could not be removed from dictionary. IP = " + channel.IP);
}
public async Task StartListen(CancellationToken token)
{
SocketReadData socketReadData = socketDataByPort.Values.First();
while (!token.IsCancellationRequested)
{
await socketReadData.Socket.ReceiveAsync(socketReadData.Awaitable);
SocketAsyncEventArgs args = socketReadData.Awaitable.EventArgs;
if (args.BytesTransferred > 0) await socketReadData.DecoderTransformBlock.SendAsync(args, token);
}
}
}
Socket data:
class SocketReadData
{
public Socket Socket { get; }
public ReceiveSocketAwaitable Awaitable { get; }
public TransformBlock<SocketAsyncEventArgs, List<Message>> DecoderTransformBlock { get; }
public SocketReadData(Socket socket, ReceiveSocketAwaitable awaitable, TransformBlock<SocketAsyncEventArgs, List<Message>> decoderTransformBlock)
{
Socket = socket;
Awaitable = awaitable;
DecoderTransformBlock = decoderTransformBlock;
}
}
And just in case, the code from the article for socket awaitable:
class ReceiveSocketAwaitable : INotifyCompletion
{
static readonly Action Sentinel = () => { };
Action mContinuation;
public bool WasCompleted { get; set; }
public SocketAsyncEventArgs EventArgs { get; }
public bool IsCompleted => WasCompleted;
public ReceiveSocketAwaitable(SocketAsyncEventArgs eventArgs)
{
Contract.Requires<ArgumentNullException>(eventArgs != null, "eventArgs can't be null");
EventArgs = eventArgs;
eventArgs.Completed += delegate
{
Action prev = mContinuation ?? Interlocked.CompareExchange(ref mContinuation, Sentinel, null);
prev?.Invoke();
};
}
public void OnCompleted(Action continuation)
{
if (mContinuation == Sentinel || Interlocked.CompareExchange(ref mContinuation, continuation, null) == Sentinel)
{
Task.Run(continuation);
}
}
public void Reset()
{
WasCompleted = false;
mContinuation = null;
}
public ReceiveSocketAwaitable GetAwaiter()
{
return this;
}
public void GetResult()
{
if (EventArgs.SocketError != SocketError.Success) throw new SocketException((int)EventArgs.SocketError);
//return EventArgs.BytesTransferred;
}
}
And extensions:
static class SocketExtensions
{
public static ReceiveSocketAwaitable ReceiveAsync(this Socket socket, ReceiveSocketAwaitable awaitable)
{
awaitable.Reset();
if (!socket.ReceiveAsync(awaitable.EventArgs)) awaitable.WasCompleted = true;
return awaitable;
}
public static ReceiveSocketAwaitable SendAsync(this Socket socket, ReceiveSocketAwaitable awaitable)
{
awaitable.Reset();
if (!socket.SendAsync(awaitable.EventArgs)) awaitable.WasCompleted = true;
return awaitable;
}
}
As you can see, this code achieves my goal for a single socket. In a loop it awaits for data and then await for transformblock to accept the packet. And only after that it can read data from a socket to the buffer again.
So the question is: how can i achieve this behavior for many sockets using async/await pattern, without creating threads and copying data for each packet?
I have a problem when trying to consume a server implementation of MessagePack RPC. I wrote one implementation for client and one for server based on a Python code provided by my company's client.
The server implementation should be consumed by Python, but as far as I see that won't be a problem.
Server implementation:
public class Program
{
static void Main(string[] args)
{
try
{
DefaultServiceTypeLocator def = new DefaultServiceTypeLocator();
ServiceTypeLocator ser = def;
def.AddService(new Methods().GetType());
var services = ser.FindServices();
var configuration = new RpcServerConfiguration();
IPAddress ipAddress = GetIp();
configuration.BindingEndPoint = new IPEndPoint(ipAddress, 8089);
Console.WriteLine(new IPEndPoint(ipAddress, 8089).ToString());
using (var server = new RpcServer(configuration))
{
server.Start();
Console.ReadKey();
}
}
catch (Exception ex)
{
Console.Write(ex);
Console.ReadKey();
}
}
[MessagePackRpcServiceContractAttribute]
public class Methods
{
[MessagePackRpcMethodAttribute]
public int hello0()
{
Console.WriteLine("hello0");
return 0;
}
}
Client implementation:
public class Program
{
static void Main(string[] args)
{
try
{
var configuration = new RpcClientConfiguration();
IPAddress ipAddress = GetIp();
using (dynamic proxy = new DynamicRpcProxy(new IPEndPoint(ipAddress, 8089), configuration))
{
dynamic res = proxy.hello0();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadKey();
}
}
private static IPAddress GetIp()
{
string myHost = System.Net.Dns.GetHostName();
IPAddress myIP = null;
for (int i = 0; i <= System.Net.Dns.GetHostEntry(myHost).AddressList.Length - 1; i++)
{
if (System.Net.Dns.GetHostEntry(myHost).AddressList[i].IsIPv6LinkLocal == false)
{
if (System.Net.Dns.GetHostEntry(myHost).AddressList[i].AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
myIP = System.Net.Dns.GetHostEntry(myHost).AddressList[i];
}
}
return myIP;
}
}
My client can't connect to my server, it can't see the methods over there. The error is: "operation does not exist".
Anyone has any clue?
Thank you!!
Well after many tries and many help from friends, I finally managed to fix the issue.
First of all you have to download the Message Pack RPC solution here and create your new project (server or client) inside this solution. In my case the server didn’t work when I created a new solution and referenced the dlls, only inside the whole project. The client worked only referencing the dlls.
Implementation for the Server Side:
internal class Program
{
public static void Main(string[] args1)
{
var config = new RpcServerConfiguration();
config.BindingEndPoint = new IPEndPoint(IPAddress.Loopback, 8089);
config.PreferIPv4 = true;
config.IsDebugMode = true;
//UseFullMethodName is a property that if it is false allows you in the CLIENT to call the methods only by it's name, check example further.
config.UseFullMethodName = false;
var defaultServiceTypeLocator = new DefaultServiceTypeLocator();
//Methods is the class I created with all the methods to be called.
defaultServiceTypeLocator.AddService(typeof(Methods));
config.ServiceTypeLocatorProvider = conf => defaultServiceTypeLocator;
using (var server = new RpcServer(config))
{
server.Start();
Console.ReadKey();
}
}
[MessagePackRpcServiceContract] //Define the contract to be used
public class Methods
{
[MessagePackRpcMethod] //Define the methods that are going to be exposed
public string Hello()
{
return "Hello";
}
[MessagePackRpcMethod]
public string HelloParam(string i)
{
return "Hello " + i;
}
}
Implementation for the Client Side:
static void Main(string[] args)
{
using (var target = CreateClient())
{
//var result1 = target.Call("Hello:Methods:0", null); /*if in the server the property UseFullMethodName is true, or not defined as false (default is true) you have to call the method on the server using *methodname:class:version**/
var result2 = target.Call("Hello", null); //Parameter is null
var result3 = target.Call("HelloParam", “Mariane”);//Method with parameter
}
}
public static RpcClient CreateClient()
{
return new RpcClient(new IPEndPoint(IPAddress.Loopback, 8089), new RpcClientConfiguration() { PreferIPv4 = true });
}
I hope this is clear and help you guys to fix it. Don’t forget to set the methods as public on the server. Thanks to https://github.com/yfakariya /msgpack-rpc-cli/issues/6 that answered my question. Really special thanks to #DanielGroh that spent some time with me trying to fix it and Gilmar Pereira which has no profile here but is a great developer and helped me a lot (https:// www. facebook .com/ gilmarps?fref=ts).
What is the correct way to handle socket failure in a ConnectionMultiplexer? I know it will try again silently in the background, but is there any accepted canonical way to handle the time between such disconnects? Since I wrap this up in our own client anyway, I was thinking something like the following:
private async Task<IDatabase> GetDb(int dbToGet)
{
int numberOfRetries = 0;
while (!multiplexer.IsConnected && numberOfRetries < MAX_RETRIES)
{
await Task.Delay(20);
numberOfRetries++;
}
if (!multiplexer.IsConnected)
{
// Panic, die, etc.
}
// Continue as though connected here
}
It seems a bit clumsy, though, so I'm wondering if there's a better way to handle this.
(This is all in version 1.0.414 of StackExchange.Redis, the latest version from NuGet)
I just wrapped multiplexer,
by default it has auto reconnect definition,
the real problem is that you have subscribe/Psubscribe to Redis with current socket connection,
therefore I used the ConnectionRestored Event to re-Register the subscribe patterns to the relevant channels/actions.
Class Example:
public class RedisInstanceManager
{
public RedisInstanceCredentials m_redisInstanceCredentials { get; set; }
public DateTime? m_lastUpdatedDate { get; set; }
public ConnectionMultiplexer redisClientsFactory { get; set; }
public Timer _ConnectedTimer;
public Action _reconnectAction;
public RedisInstanceManager(ConnectionMultiplexer redisClients, Action _reconnectActionIncoming)
{
string host,port;
string[] splitArray = redisClients.Configuration.Split(':');
host = splitArray[0];
port = splitArray[1];
this.redisClientsFactory = redisClients;
this.m_redisInstanceCredentials = new RedisInstanceCredentials(host, port);
this.m_lastUpdatedDate = null;
_ConnectedTimer = new Timer(connectedTimer, null, 1500, 1500);
_reconnectAction = _reconnectActionIncoming;
this.redisClientsFactory.ConnectionRestored += redisClientsFactory_ConnectionRestored;
}
private void connectedTimer(object o)
{
_ConnectedTimer.Change(Timeout.Infinite, Timeout.Infinite);
if (!this.redisClientsFactory.IsConnected)
{
Console.WriteLine("redis dissconnected");
}
_ConnectedTimer.Change(1500,1500);
}
void redisClientsFactory_ConnectionRestored(object sender, ConnectionFailedEventArgs e)
{
Console.WriteLine("Redis Connected again");
if (_reconnectAction != null)
_reconnectAction();
}
public ConnectionMultiplexer GetClient()
{
return this.redisClientsFactory;
}
}
I've created a simple chatroom program .
Basic message sending and receiving works well on the client, and server,too.
But I want to broadcast the connection list to all users when any user leave.
I use a thread to handle this task.
private void monitorProc()
{
while(true)
{
try
{
int bytereads = 0;
byte[] buffer = new byte[4096];
bytereads = clientStream.Read(buffer, 0, buffer.Length);
updateMonitor(encoder.GetString(buffer), monitor);
}
catch
{
}
}
}
And I use this thread like this ...
monitorThread = new Thread(new ThreadStart(monitorProc));
monitorThread.Start();
And server is ...
private void broadcast(string message)
{
foreach (User user in userlist)
{
NetworkStream clientStream;
clientStream = user.Userclient.GetStream();
clientStream.Write(encoder.GetBytes(message), 0, 4096);
clientStream.Flush();
}
}
When server received message from client , it call this function , and then all user will
In that way , client will put the message when receive message from server.
I define a User class . It contain the following info...
[Serializable]
public class User
{
public enum stat
{
ALIVE,
DEAD
}
private string username;
private IPEndPoint userEP;
private TcpClient userclient;
private stat userstat;
public string Username { get; set; }
public IPEndPoint UserEP { get { return userEP; } }
public TcpClient Userclient { get { return userclient; } }
public stat Userstat { get; set; }
public User(TcpClient _userclient)
{
userclient = _userclient;
userstat = stat.ALIVE;
}
}
I use a list to store every user in server .
Question1: how to send the list to every client ?
(I know that serialization can help . But an error occurred when I use the following code to serialize the list...
foreach (User user in userlist) {
Stream statBroadcast = user.Userclient.GetStream();
var bin = new BinaryFormatter();
bin.Serialize(statBroadcast , userlist);
connlist.Items.Add(user.Username);
}
It said that I didn't mark TcpClient as serializable.
Question 2: If I can send custom object to client . How to send connection list and message at the same time ?
(If I define a function in client called "updateList" and it receive the data through stream . How to distinct from the data (whether message of connectionlist))