Skip to content

Instantly share code, notes, and snippets.

@yallie
Created January 22, 2021 12:09
Show Gist options
  • Save yallie/af9b4d4936820d67377ef2780b867cf3 to your computer and use it in GitHub Desktop.
Save yallie/af9b4d4936820d67377ef2780b867cf3 to your computer and use it in GitHub Desktop.
TcpEx: server-side connection locking issue
// http://zyan.com.de
//
// Compile using: csc test.cs /r:Zyan.Communication.dll
//
// Start up test.exe several times.
// The first process is the server, the rest are clients.
//
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using Zyan.Communication;
using Zyan.Communication.Protocols.Tcp;
class GlobalLockSuspicionTest
{
static void Main()
{
try
{
StartServer();
}
catch
{
StartClient();
}
}
// ------------ Server code --------------
static void StartServer()
{
var proto = new TcpDuplexServerProtocolSetup(4321);
using (var host = new ZyanComponentHost(nameof(GlobalLockSuspicionTest), proto))
{
host.RegisterComponent<IService, Service>();
host.SubscriptionCanceled += (s, e) => Console.WriteLine("Subscription canceled: {0}.{1}", e.ComponentType, e.DelegateMemberName);
Console.WriteLine("Server started. Press ENTER to quit.");
Console.Title = nameof(GlobalLockSuspicionTest) + " - Server #" + Process.GetCurrentProcess().Id;
var ps = new TcpDuplexClientProtocolSetup();
ThreadPool.QueueUserWorkItem(x => TryExternalConnection(ps, "example.com", 4322));
ThreadPool.QueueUserWorkItem(x => TryExternalConnection(ps, "example.com", 4323));
ThreadPool.QueueUserWorkItem(x => TryExternalConnection(ps, "example.com", 4324));
Console.ReadLine();
}
}
static void TryExternalConnection(TcpDuplexClientProtocolSetup proto, string host, int port)
{
var url = proto.FormatUrl(host, port, nameof(GlobalLockSuspicionTest));
try
{
new ZyanConnection(url);
}
catch (Exception ex)
{
Console.WriteLine("Connection to {0} failed. Exception: {1}, Error message: {2}", url, ex.GetType(), ex.Message);
}
}
public class Service : IService
{
private static int Counter { get; set; }
public string Hello()
{
Counter++;
Console.WriteLine("Called: " + Counter);
return "Hello: " + Counter;
}
public void ExternalConnection()
{
try
{
var proto = new TcpDuplexClientProtocolSetup();
var url = proto.FormatUrl("example.com", 4321, nameof(GlobalLockSuspicionTest));
new ZyanConnection(url);
}
catch (Exception ex)
{
Console.WriteLine("Connection failed. Error message: {0}", ex.Message);
throw;
}
}
}
// ------------ Shared code --------------
public interface IService
{
string Hello();
void ExternalConnection();
}
// ------------ Client code --------------
static void StartClient()
{
Console.Title = nameof(GlobalLockSuspicionTest) + " - Client #" + Process.GetCurrentProcess().Id;
var proto = new TcpDuplexClientProtocolSetup();
var url = proto.FormatUrl("localhost", 4321, nameof(GlobalLockSuspicionTest));
Console.WriteLine("Connecting to {0}...", url);
var sw = Stopwatch.StartNew();
using (var conn = new ZyanConnection(url))
{
sw.Stop();
Console.WriteLine("Connected. Time elapsed: {0}", sw.Elapsed);
var proxy = conn.CreateProxy<IService>();
Console.WriteLine("Client started. Commands: H = hello, P = poll, C = connect to example.com, S = ask server to connect to example.com, ^C to quit.");
while (true)
{
var command = Console.ReadLine();
switch (command.ToLower().FirstOrDefault())
{
case 'h':
var result = proxy.Hello();
Console.WriteLine("Server reply: {0}", result);
break;
case 'p':
Console.WriteLine("Polling...");
ThreadPool.QueueUserWorkItem(x => Poll(proxy));
break;
case 'c':
Console.WriteLine("Connecting to example.com...");
ThreadPool.QueueUserWorkItem(x => Connect());
break;
case 's':
Console.WriteLine("Asking server to connect to example.com...");
ThreadPool.QueueUserWorkItem(x => ExternalConnection(proxy));
break;
default:
Console.WriteLine("Unknown command");
break;
}
}
}
}
static void ExternalConnection(IService proxy)
{
try
{
proxy.ExternalConnection();
}
catch (Exception ex)
{
Console.WriteLine("Connection failed. Error message: {0}", ex.Message);
}
}
static void Connect()
{
try
{
var proto = new TcpDuplexClientProtocolSetup();
var url = proto.FormatUrl("example.com", 4321, nameof(GlobalLockSuspicionTest));
new ZyanConnection(url);
}
catch (Exception ex)
{
Console.WriteLine("Connection failed. Error message: {0}", ex.Message);
}
}
static void Poll(IService proxy)
{
for (var i = 0; i < 100; i++)
{
try
{
Thread.Sleep(TimeSpan.FromSeconds(1));
Console.WriteLine("Iteration #{0}: server reply: {1}", i, proxy.Hello());
}
catch (Exception ex)
{
while (ex is TargetInvocationException && ex.InnerException != null)
{
ex = ex.InnerException;
}
Console.WriteLine("Polling failure: should never happen. Error message: {0}", ex.Message);
}
}
}
}
@yallie
Copy link
Author

yallie commented Jan 22, 2021

On Zyan v2.14 and below, the client cannot connect to server until the server fails to connect to example.com.

Related issue: zyanfx/Zyan#87

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment