我有一个Topshelf Windows服务,充当TCP服务器。在这项服务中,我还有一个自托管(OWIN)WebAPI。
我的目标是以某种方式允许WebAPI与包含并运行在同一服务中的TCP服务器进行通信。当然,我可以简单地使用像“触发器”文件或共享数据库那样的东西。虽然我想了解更多优化/原生的方法来实现这一目标,但我可以经常进行民意调查。
为了更好地了解项目,请考虑单个页面应用程序使用我的API并使用任意字符串参数进行某些调用。然后,应将此数据传递给连接到正在运行的TCP服务器的客户端(使用winsock的C ++控制台应用程序)。
实例化以下Container并将其传递给Topshelf HostConfigurator
class ContainerService
{
private APIService _apiService;
private EngineService _engineService;
protected IDisposable WebAppHolder { get; set; }
public bool Start(HostControl hostControl)
{
var host = hostControl;
_apiService = new APIService();
_engineService = new EngineService();
// Initialize API service
if (WebAppHolder == null)
{
WebAppHolder = _apiService.Initialize();
}
// Initialize Engine service
_engineService.Initialize();
return true;
}
public bool Stop(HostControl hostControl)
{
// Stop API service
if (WebAppHolder != null)
{
WebAppHolder.Dispose();
WebAppHolder = null;
}
// Stop Engine service
_engineService.Stop();
return true;
}
}
程序入口点的标准Topshelf内容(如上所述):
HostFactory.Run(hostConfigurator =>
{
hostConfigurator.Service<ContainerService>(containerService =>
{
containerService.WhenStarted((service, control) => service.Start(control));
containerService.WhenStopped((service, control) => service.Stop(control));
});
hostConfigurator.RunAsLocalSystem();
hostConfigurator.SetServiceName("Educe Service Host");
hostConfigurator.SetDisplayName("Communication Service");
hostConfigurator.SetDescription("Responsible for API and Engine services");
});
TCP服务器:
public void Initialize()
{
_serverListener = new TcpListener(new IPEndPoint(hostAddress, (int)port));
_serverListener.Start();
_threadDoBeginAcceptTcpClient = new Thread(() => DoBeginAcceptTcpClient(_serverListener));
_threadDoBeginAcceptTcpClient.Start();
}
...
public void DoBeginAcceptTcpClient(TcpListener listener)
{
while(!_breakThread)
{
// Set the event to nonsignaled state.
TcpClientConnected.Reset();
// Start to listen for connections from a client.
Console.WriteLine("Waiting for a connection...");
// Accept the connection.
listener.BeginAcceptTcpClient(DoAcceptTcpClientCallback, listener);
// Wait until a connection is made and processed before continuing.
TcpClientConnected.WaitOne();
}
}
// Process the client connection.
public void DoAcceptTcpClientCallback(IAsyncResult ar)
{
// Get the listener that handles the client request.
TcpListener listener = (TcpListener)ar.AsyncState;
// End the operation and display the received data on the console.
Console.WriteLine("Client connection completed");
Clients.Add(listener.EndAcceptTcpClient(ar));
// Signal the calling thread to continue.
TcpClientConnected.Set();
}
WebAPI控制器:
public class ValuesController : ApiController
{
// GET api/values/5
public string Get(int id)
{
return $"Foo: {id}";
}
}
如前所述,我所寻求的是WebAPI和Windows服务之间的“通信”。如何将“id”参数从WebAPI调用传递到Windows服务中的_engineService对象?也许类似于WPF的MVVM Light Messenger?这个想法是它将被解析并发送到存储在客户端列表中的适当的TcpClient。
有关如何实现这一目标的任何建议将不胜感激。请随时要求澄清/更多代码。
答案 0 :(得分:0)
您是否找到了问题的答案?
我不太明白你试图在两者之间寻求沟通的目的是什么?你想以某种方式依靠TCP / IP来传递这个id或内存吗?
潜在地,您可以考虑使用Mediator模式并使用这种在我理解的情况下看起来非常有用的库:https://github.com/jbogard/MediatR
在一种更简单的方法中,我会依靠事件来实现您要做的事情,即从HTTP请求到c ++用户的反应通信。
我了解你的需要吗?我对解决方案很好奇
答案 1 :(得分:0)
我假设您正在尝试获取HTTP GET请求的ID参数并将其发送到连接到EngineService的TCP客户端。如果在ApiService之前初始化了EngineService,我认为这是一个如何从ApiService的控制器实例中获取一个唯一的EngineService实例的问题。
如果我关注您,您可以将EngineService设置为ContainerService的公共静态属性,并将其作为ContainerService.EngineService
从控制器(或应用程序中的任何位置)引用,或者更好地将您的EngineService注册为DI容器中的单例将其注入ApiService。
答案 2 :(得分:0)
解决方案(调用WebAPI触发EngineService)
我现在使用RabbitMQ / EasyNetQ来实现WebApi和包含我的TCP客户端的EngineService对象之间的通信。
我现在偶然将它们分成两个独立的Projects / Topshelf服务。
以下是新的“通信”组件,它在EngineService构造函数中实例化。
public class Communication
{
private readonly Logger _logger;
private readonly IBus _bus;
public delegate void ReceivedEventHandler(string data);
public event ReceivedEventHandler Received;
protected virtual void OnReceive(string data)
{
Received?.Invoke(data);
}
public Communication()
{
_logger = new Logger();
_bus = RabbitHutch.CreateBus("host=localhost", reg => reg.Register<IEasyNetQLogger>(log => _logger));
SubscribeAllQueues();
}
private void SubscribeAllQueues()
{
_bus.Receive<Message>("pipeline", message =>
{
OnReceive(message.Body);
});
}
public void SubscribeQueue(string queueName)
{
_bus.Receive<Message>(queueName, message =>
{
OnReceive(message.Body);
});
}
}
然后添加事件处理程序。 这意味着只要消息到达总线,数据就会被中继到事件处理程序,然后事件处理程序将其转发到列表中第一个连接的TCP客户端。
public void Handler(string data)
{
//Console.WriteLine(data);
_clients[0].Client.Send(Encoding.UTF8.GetBytes(data));
}
...
_comPipe.Received += Handler;
最后在WebApi的控制器上:
public string Get(int id)
{
ServiceCom.SendMessage("ID: " + id);
return "value";
}
ServiceCom类。允许在总线上发送字符串消息。
public static class ServiceCom
{
public static void SendMessage(string messageBody)
{
var messageBus = RabbitHutch.CreateBus("host=localhost");
messageBus.Send("pipeline", new Message { Body = messageBody });
}
}
现在已经完成了,我现在正在寻找为连接的TCP客户端实现一种方法,以触发另一个SPA项目中的更新/事件,该项目将充当门户/客户端管理应用程序。
我的方法可能会使用KnockOut.js和SignalR来实现动态视图,其中立即显示TCP客户端事件,类似地,对WebAPI的操作将触发TCP客户端中的事件。我知道这听起来像是一个奇怪的过程组合,但它完全按计划进行,并按预期工作:)