My sent messages don't get received, whats wrong?
janniksam opened this issue · 14 comments
I am having a Client console application and a Server console application.
But neither of them receive messages. I set a breakpoint in the OnAskForLogin() method, but it never gets called, what is the problem?
My server looks like this:
class Program
{
private readonly List<CustomClient> m_clients;
public Program()
{
m_clients = new List<CustomClient>();
}
private static void Main()
{
var p = new Program();
p.Start();
while (true)
{
Thread.Sleep(100);
}
}
private async void Start()
{
var listener = new TcpSocketListener();
listener.ConnectionReceived +=
(sender, args) => m_clients.Add(new CustomClient(args.SocketClient));
await listener.StartListeningAsync(12352);
}
}
public class CustomClient
{
private readonly JsonProtocolMessenger<BaseMessage> m_messenger;
public CustomClient(ITcpSocketClient client)
{
m_messenger = new JsonProtocolMessenger<BaseMessage>(client);
m_messenger.Messages.Subscribe(msg => Debug.WriteLine(msg));
m_messenger.Messages.OfType<TestRequestMessage>().Subscribe(OnLoginRequest);
m_messenger.StartExecuting();
Thread.Sleep(5000);
m_messenger.Send(new ServerConnectionEstablishedMessage());
}
private void OnLoginRequest(TestRequestMessage message)
{
m_messenger.Send(new TestResponseMessage { LoginInfo = "TEST!" });
}
}
My client looks like this:
class Program
{
private Client m_client;
static void Main()
{
var p = new Program();
p.Start();
while (true)
{
Thread.Sleep(10);
}
}
private async void Start()
{
var client = new TcpSocketClient();
await client.ConnectAsync("127.0.0.1", 12352);
m_client = new Client(client);
}
}
public class Client
{
private readonly JsonProtocolMessenger<BaseMessage> m_messenger;
public Client(TcpSocketClient client)
{
m_messenger = new JsonProtocolMessenger<BaseMessage>(client);
m_messenger.Messages.Subscribe(msg => Debug.WriteLine(msg));
m_messenger.Messages.OfType<ServerConnectionEstablishedMessage>().Subscribe(OnAskForLogin);
m_messenger.Messages.OfType<TestResponseMessage>().Subscribe(OnLoginResponse);
m_messenger.StartExecuting();
}
private void OnAskForLogin(ServerConnectionEstablishedMessage message)
{
m_messenger.Send(new TestRequestMessage());
}
private void OnLoginResponse(TestResponseMessage message)
{
Console.WriteLine(message);
}
}
Both the server and the client do share a project, where all the messages are placed in.
Hi again.
Reading your code, I think the usage looks fine. I guess that BaseMessage
lives in a seperate assembly referenced by both client and server projects. Because it is in a seperate assembly, I think that the final note in the README.MD
on type resolution may apply here. Can you try adding the line:
m_messenger.AdditionalTypeResolutionAssemblies = new [] { typeof(BaseMessage).Assembly }.ToList();
before the call to StartExecuting
for both server and client? I think this will allow BaseMessage
and any other types in the shared assembly to be resolved.
Yeah, this actually does the trick, thank you very much :) Keep up the good work :)
I am trying to use the nugetpackage in my Xamarin.Portable Library right now, but it says, that Newtonsoft.Json 3.5.8 could not be installed, cause ".NETPortable, Version=v4.5, Profile=Profile78" isnt compatible to the framework, whats wrong?
Oops - I apologise. I originally posted about PCL profiles, but forgot that you were working with sockethelpers, not sockets-for-pcl. I was able to reproduce the error when installing sockethelpers. I think it is related to the way that NuGet package dependency policy changed between VS2013 and VS2015, and the policy specified in sockethelpers not being specific enough.
A workaround for right now is to manually install the problematic dependencies first.
If you use the Package Manager Console, this order will solve the problem:
Install-Package Newtonsoft.Json
Install-Package Splat
Install-Package rda.SocketHelpers -Pre
(of course, you also need sockets-for-pcl).
Let me know how you go!
One other small undocumented tip -
If you add this class to your shared project
internal class ConsoleLogger : ILogger
{
public LogLevel Level { get; set; }
public void Write(string message, LogLevel logLevel)
{
if (Level >= logLevel)
Console.WriteLine(message);
}
}
and add this line to your Program.cs
Locator.CurrentMutable.RegisterConstant(new ConsoleLogger(), typeof (ILogger));
You will get some internal sockethelpers message logging. It may be too verbose for everyday use, but if something is going wrong you might find it useful.
Thank you for your answers. After hours of figuring out a reference issue in my project, I finally have a buildable solution your sockethelper library now :)
I have another, this time more general question:
First of all, how do you handle disconnections in general and especially using your libraries (e.g. a user temporarily has no internet connection, because he is sitting in a train and ís stuck currently stuck in a tunnel? When the user has Internet again, will the TCP connection still be up and running?
And is the TCP Connection the right thing to use in my scenario? Let me explain what I am trying to accomplish: I am trying to accomplish a some sort of pokergame. The users should not have to immediately answer when it's his turn, he can take one or two days to think about his next step.
So, is a TCP connection resistent enough to survive this long time spans, even if there are multiple Internet disconnections?
If not:
How would do it? What I can think of: When logging in initially, save a user identifier (like a hash out of username + pass) in the Servers client-class. When the client gets disconnected, the Server still has the Client class held in a list and when the Client tries to connect again, the Server sees there is already a Client class with that identifier, so it uses that class? Is that a good way doing it? Just wondering, how you would do it?
Another thing I could think of is doing it completely stateless (e.g. with a cookie), but I dont like that idea at all...
Greetings from Germany,
Jannik1
BTW:
My server console application always terminates when I use the following line in my code:
await listener.StartListeningAsync(port);
It doesnt give me any exception, it just terminates a few statements later.. ;/
Okay, I opened a new issue, because my latest question had nothing to do with the helpers, but with the main problem. You can find it here, rdavisau/sockets-for-pcl#40
Sorry again for the delay in responding. I will answer your question 1 anyway, despite my recommendation for question 2.
- I sympathise with your train scenario, and I can say upfront that you would expect with almost 100% likelihood that the connection will be dropped, particularly if you attempt to send or receive whilst out of connectivity. Detecting disconnection with sockets raw can be a bit painful, you really need some 'active' mechanism for verifying connectivity. For a little bit of past discussion, take a look at this issue on sockets-for-pcl. The end result of that was that I created a wrapper called
BetterTcpSocketClient
, which you can get here, that by actively reading from the underlying socket aims to detect disconnection immediately and raise it via theDisconnected
event. Fortunately, variant of this behaviour is included already inJsonProtocolMessenger
, see theDisconnected
event that it exposes. - However, with a better understanding of your requirements, I would advise against using raw TCP sockets for your game. Working at this low level is best suited for realtime-like needs where a persistent connection can be established and maintained for the duration of the interaction. If your game were to be played in a single session, peer-to-peer (as in from one mobile to another), then I would recommend it. But, because you have the long-term requirement and the luxury of an intermediate server, I would think it easier to work with something a little higher level. My approach would probably be one of the following:
- SignalR hub on the server and SignalR client for the players:
This will give you realtime-like communications when connected, some built-in connection resiliency and automatic serialisation/deserialisation of messages. If you expect to have multiple players in a single game, this also gives you an easy method to group and partition the games, easy broadcast of (for example) chat messages, etc. With this approach, both client and server can communicate with each other at any point, more closely matching the original socket design. - WebAPI on the server and something like Refit on the client:
This moves away from maintaining any persistent connection, so you lose some of the realtime-like capabilities. Here you define an API on the server with methods that the clients will all call. Here there is no need for connection resiliency, you only need a connection when you go to make a call to the server. You also get free serialisation of messages. With this approach, only the client can communicate to the server at any point (the server can only respond when the client makes a request to it), which is less like the original socket design.
In both of these cases you are able to share your message types in a base class, which is nice. You will need to implement some kind of persistence - saving the state of the game on the server, so that you can retrieve it when a client connects back or decides to play the next move. This is good though, because you should assume that sometimes the server will go down, or will need to be reset, so we can't hold all the games in memory all the time.
So ultimately, my recommendation is against using sockets for your requirements because they are very low level and you do not have the realtime peer-to-peer requirement that they enable. Because you have the luxury of a master server, I would suggest one of the above methods to make your life easier and so you can take advantage of many of the built in features that the higher level of abstractions enable. Let me know if you have any more questions!
Thanks for the really good answer :) I am kinda interested in SignalR now, I am really curious, will that work on Linux and mobile aswell? I read about it and it says ASP.NET everywhere, but it does work without it right?
Yes - there is a "self-host" version of signalr that will work outside of an ASP.NET site, for example in a console application. I have used it on many occasions and it works perfectly. As for linux, you will simply run your self-host application under mono - I have not done this personally, but it is a specific use case that the SignalR team supports.
On mobile, the signalr client is already packaged and available via the Xamarin component store here for iOS/Android/WP (free, of course). But, you should also be able to install the client from NuGet if you prefer that.
I'm not sure if you use LINQPad, but if you do I will say that I have found it very handy for quickly iterating and testing my signalr hub (the server component) before moving it to a console application, etc.
Sry for not telling you earlier, I know you indirectly asked me about it already: I dont use LINQPad, :)
But thanks, I will read me in tommorrow and maybe start to implement SignalR, we will see if I can find a neat tutorial for a good start.
Again, thank you for your recommondations, I will keep you posted and we will see if this will work out for me. I was always using TCP clients until now, but it never worked properly for my scenarios, IIn my older games I even did all the byte to object convertion etc myself (and even before that I splitted strings, lol), which wasnt comfortable at all, hopefully this will get better now.
BTW: About the async & await: Thanks for the advice, but I am good with those mechanics, it was just a bad day and sometimes you just need another pair of eyes to see those basic mistakes (I know, that was a pretty dumb one though)
Just wanted to let you know, but I am using SignalR right know and it works pretty good so far. So, I think, you can close this issue now
Good to know it's working out for you. Thanks for letting me know!