Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I can't transmit a simple message [question] #13

Closed
atiris opened this issue Mar 2, 2022 · 9 comments
Closed

I can't transmit a simple message [question] #13

atiris opened this issue Mar 2, 2022 · 9 comments

Comments

@atiris
Copy link

atiris commented Mar 2, 2022

I'm having trouble moving a simple message (my first step with this library).
This is a minimal new .NET Framework 4.7.2 project (Desktop Application) with nothing only Load event for form.
I only added libraries: H.Pipes and H.Formatters.System.Text.Json
This is the entire form code.

using H.Formatters;
using H.Pipes;
using System;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Test1
{
    public partial class Frm_Test : Form
    {
        private PipeServer<MyMessage> server;
        private PipeClient<MyMessage> client;
        private string pipeName = "a";
        private string test = "THIS IS TEST MESSAGE";

        public Frm_Test() { InitializeComponent(); }

        private void Frm_Test_Load(object sender, EventArgs e)
        {
            server = new PipeServer<MyMessage>(pipeName, formatter: new SystemTextJsonFormatter());
            server.MessageReceived += (c, args) =>
            {
                Console.WriteLine("Received: " + args.Message.Text);
                if (args.Message.Text != test) { Console.WriteLine("WRONG MESSAGE"); }
            };
            Task.Run(async () => { await server.StartAsync(); });

            client = new PipeClient<MyMessage>(pipeName, formatter: new SystemTextJsonFormatter());
            client.Connected += (o, args) =>
            {
                Console.WriteLine("Sending: " + test);
                client.WriteAsync(new MyMessage { Text = test });
            };
            client.ConnectAsync();
        }
    }

    public class MyMessage { public string Text; }
}

Console output is like this (message.Text is always empty):

Sending: THIS IS TEST MESSAGE
Received: 
WRONG MESSAGE

I get the same problem even with two separated client-server applications.
Please advise me where I am making a mistake?

@atiris
Copy link
Author

atiris commented Mar 2, 2022

Hm, this is possible bug with SystemTextJsonFormatter.
If I install H.Formatters.Newtonsoft.Json and change SystemTextJsonFormatter to NewtonsoftJsonFormatter, everything start working. Console log: Received: THIS IS TEST MESSAGE.
Well, I really lost a lot of time with this. 🙄

@HavenDV
Copy link
Owner

HavenDV commented Mar 4, 2022

Thanks for the problem. I'll check. I want to note that be sure to call Dispose before closing the program / ending the use of PipeServer/PipeClient, otherwise you may have problems after restarting. Pipes are the resource of the entire system and must be properly cleaned after use.

@atiris
Copy link
Author

atiris commented Mar 4, 2022

I want to note that be sure to call Dispose before closing the program

Hi @HavenDV
thank you for this, I didn't really realize that. It would undoubtedly be a good idea to add this information to the readme.

@HavenDV
Copy link
Owner

HavenDV commented Mar 4, 2022

I can't repeat it in tests:

[TestMethod]
public async Task ParallelTest()
{
    const string pipeName = "absolutely_random_name";
    const string message = "THIS IS TEST MESSAGE";

    var receivedMessage = string.Empty;

    var server = new PipeServer<string>(pipeName, formatter: new SystemTextJsonFormatter());
    server.MessageReceived += (c, args) =>
    {
        Console.WriteLine($"Received: {args.Message}");

        receivedMessage = args.Message;
    };
    var startTask = server.StartAsync();

    var client = new PipeClient<string>(pipeName, formatter: new SystemTextJsonFormatter());
    client.Connected += async (o, args) =>
    {
        Console.WriteLine($"Sending: {message}");

        await client.WriteAsync(message);

        Console.WriteLine($"Sent: {message}");
    };
    await client.ConnectAsync();
    await startTask;

    await Task.Delay(TimeSpan.FromMilliseconds(100));

    Assert.AreEqual(message, receivedMessage);
}

It works correctly.

I want to note that there are some peculiarities here when using asynchronous methods in Windows Forms. If you don't use ConfigureAwait(false), the default scheduler will be the UI scheduler. This can cause various problems with the initialization sequence.
Look towards using async void which contains try/catch inside. Also read this - https://devblogs.microsoft.com/dotnet/configureawait-faq/

@HavenDV
Copy link
Owner

HavenDV commented Mar 4, 2022

It would undoubtedly be a good idea to add this information to the readme.

Updated README, added Notes block to Usage

@HavenDV
Copy link
Owner

HavenDV commented Mar 4, 2022

Also try using Task.Run in general when creating the PipeServer, it might help.

@atiris
Copy link
Author

atiris commented Mar 4, 2022

I can't repeat it in tests

Thanks, I applied to the code everything I understood. This article https://devblogs.microsoft.com/dotnet/configureawait-faq/ requires a more advanced programmer than me 😄

Your test has probably been simplified to a level where serialization is not used as with a normal object.
Modified version of unit test:

namespace HPipesTests
{
    public class MyMessage
    {
        public string Text;
    }

    [TestClass]
    public class SystemTextJsonFm
    {
        [TestMethod]
        public async Task MessageString()
        {
            const string pipeName = "random_751902396";
            const string message = "THIS IS TEST MESSAGE";

            var receivedMessage = string.Empty;

            var server = new PipeServer<string>(pipeName, formatter: new SystemTextJsonFormatter());
            server.MessageReceived += (c, args) =>
            {
                Console.WriteLine($"Received: {args.Message}");

                receivedMessage = args.Message;
            };
            var startTask = server.StartAsync();

            var client = new PipeClient<string>(pipeName, formatter: new SystemTextJsonFormatter());
            client.Connected += async (o, args) =>
            {
                Console.WriteLine($"Sending: {message}");

                await client.WriteAsync(message);

                Console.WriteLine($"Sent: {message}");
            };
            await client.ConnectAsync();
            await startTask;

            await Task.Delay(TimeSpan.FromMilliseconds(100));

            Assert.AreEqual(message, receivedMessage);
        }

        [TestMethod]
        public async Task MessageClass()
        {
            const string pipeName = "random_54821002374";
            const string message = "THIS IS TEST MESSAGE";

            var receivedMessage = string.Empty;

            var server = new PipeServer<MyMessage>(pipeName, formatter: new SystemTextJsonFormatter());
            server.MessageReceived += (c, args) =>
            {
                Console.WriteLine($"Received: {args.Message.Text}");

                receivedMessage = args.Message.Text;
            };
            var startTask = server.StartAsync();

            var client = new PipeClient<MyMessage>(pipeName, formatter: new SystemTextJsonFormatter());
            client.Connected += async (o, args) =>
            {
                Console.WriteLine($"Sending: {message}");

                await client.WriteAsync(new MyMessage() { Text = message });

                Console.WriteLine($"Sent: {message}");
            };
            await client.ConnectAsync();
            await startTask;

            await Task.Delay(TimeSpan.FromMilliseconds(100));

            Assert.AreEqual(message, receivedMessage);
        }
    }
}

Result: 1 Passed / 1 Failed

@HavenDV
Copy link
Owner

HavenDV commented Mar 6, 2022

Thanks, you're right, the problem started when I used your MyMessage class.

https://stackoverflow.com/questions/58139759/how-to-use-class-fields-with-system-text-json-jsonserializer
It is described in detail here. You just need to use the property.

public class MyMessage
{
    public string Text { get; set; }
}

There is also an option here to add JsonSerializerOptions { IncludeFields = true } to the SystemTextJsonFormatter. I need to understand what caused the refusal to serialize fields in the System.Text.Json library in order to make this decision.

@HavenDV
Copy link
Owner

HavenDV commented Mar 6, 2022

For now, I've just added the ability to customize the SystemTextJsonFormatter via the Options property:

var server = new PipeServer<MyMessage>(pipeName, formatter: new SystemTextJsonFormatter { Options = { IncludeFields = true } });

@HavenDV HavenDV closed this as completed Mar 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants