【问题标题】:Help with event driven TCP server帮助事件驱动的 TCP 服务器
【发布时间】:2023-03-29 00:46:01
【问题描述】:

我正在开发一个“应用程序系统”,我还需要在其中制作一个服务器应用程序。我正在使用 C# (.NET 4.0)。服务器将主要从不同的 POS 应用程序/客户端收集数据(应该在 50-100 左右,但服务器也应该能够处理大约 200-300 个客户端)。从单个客户端,服务器每天可能会收到大约 100 倍的大约 1KB 数据。服务器主要需要接受数据,解密并存储到磁盘。它还应该检查特定目录中的更改,以便向客户端发送新配置,这不应该很频繁。

我对 C# 和服务器编程很陌生,所以请多多包涵。我考虑过使用线程池和异步方法(在“C# in a nutshell”一书中有一个很好的例子)。但是我花了很多时间寻找最佳解决方案,我发现了这一点。但就我而言,多线程带来的问题多于好处。因此,我甚至想到了驱动服务器。 “单个进程,在回调中处理每个事件(接受的连接、可读取的数据、可以写入客户端……)。”来自“what is event driven web server”。我发现这是解决我的问题的最佳方法。

但我不知道如何编写代码,我找不到任何有关事件驱动服务器的示例。据我了解,我应该创建一个线程(GUI + 1),然后创建一个 TCP 侦听器,然后以某种方式创建事件,以便当 TCP 侦听器可以接受客户端时,事件会触发并唤醒服务器,当数据从客户端读取将可用它会唤醒服务器。

请帮我编写代码,我完全迷路了。我知道如何使用

while(true)
{
   check if client wants to connect
        accept client and add it to client list
   iterate through client list and check if anyone is sending data ...
        accept data and store it
   ...
  }

但这不是事件驱动的,而且会浪费 CPU。服务器不会很活跃,所以我想让它尽可能高效。

一些例子真的很有帮助。

感谢您的时间和回答。

附言我可以为所有客户端只使用一个端口吗?

编辑:为了澄清,我想编写一个事件驱动的服务器,但我不知道怎么做,因此我只是举了一个我知道的例子(客户端轮询)。

【问题讨论】:

    标签: c# tcp event-driven


    【解决方案1】:

    我参加聚会有点晚了,但我最近开始了一份新工作,开发与科学仪器集成的软件,目前正在经历学习线程、通信、异步处理等的痛苦。我已经发现 Joe Albahari 的 Threading in C# 是学习线程方面的绝佳资源。

    【讨论】:

      【解决方案2】:

      首先,如果您是 C# 以及多线程和套接字的新手,那么对于您的第一个项目来说,这很多。我建议单独学习这些。

      也就是说,您可能会发现Nito.Async.Sockets 很有帮助;它包括一个事件驱动的服务器套接字并为您处理多线程问题。

      【讨论】:

        【解决方案3】:

        这是您可能需要的服务器框架。不处理任何异常。

        class Program
        {
            public static ManualResetEvent connected = new ManualResetEvent(false);
        
            static void Main(string[] args)
            {
                string ip = "127.0.0.1";
                int port = 14500;
        
                TcpListener server = new TcpListener(IPAddress.Parse(ip), port);
                server.Start();
        
                Console.WriteLine("Server started...");
        
                while (true)
                {
                    connected.Reset();
                    server.BeginAcceptTcpClient(new AsyncCallback(AcceptCallback), server);
                    connected.WaitOne();
                }
            }
        
            public static void AcceptCallback(IAsyncResult ar)
            {
                TcpListener listener = (TcpListener)ar.AsyncState;
                TcpClient client = listener.EndAcceptTcpClient(ar);
        
                byte[] buffer = new byte[1024];
                NetworkStream ns = client.GetStream();
                if (ns.CanRead)
                {
                    ns.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCallback), new object[] { ns, buffer });
                }
        
                connected.Set();
            }
        
            public static void ReadCallback(IAsyncResult ar)
            {
                NetworkStream ns = (NetworkStream)((ar.AsyncState as object[])[0]);
                byte[] buffer = (byte[])((ar.AsyncState as object[])[1]);
                int n = ns.EndRead(ar);
                if (n > 0)
                {
                    Console.WriteLine(Encoding.ASCII.GetString(buffer, 0, n));
                }
                ns.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCallback), new object[] { ns, buffer });
            }
        }
        

        【讨论】:

        • 可怕的例子。它不处理异常或与远程端断开连接(读取 0 字节)。它将异步接受转换为与事件同步,为什么不直接使用AcceptTcpClient。不要使用魔法上下文进行异步操作(在您的情况下是对象数组);
        【解决方案4】:

        事件驱动并不能准确描述您期望系统的工作方式,因为您描述的是轮询客户端以获取要处理的数据,而不是客户端将其数据推送到您的服务(触发事件)。

        如果您对如何编写这样的系统知之甚少,您可以查看适用于您的场景的现有解决方案/产品。

        我建议查看 EAI 工具,例如 BlueIntegratorBizTalk 以集成 POS 客户端。

        关于推出客户端更新的要求,您可以查看BITS

        【讨论】:

          【解决方案5】:

          我建议使用作为 Windows 服务托管的 WCF,它提供了可扩展的多线程平台。它也可以根据协议要求进行定制。这是一个可以作为参考的例子:

          http://msdn.microsoft.com/en-us/library/ms733069.aspx

          【讨论】:

          • WCF 让它变得非常复杂,如果你有选择的话,请避免使用它。使用 tcplistener 和 async 更容易做到这一点
          【解决方案6】:

          我不了解 C# 框架,但你可以看看 Twisted,一个事件驱动的 Python 框架。您可以找到与您的需求类似的服务器和客户端代码示例。

          这是一个非常简单的服务器示例,它会向客户端回显它从客户端接收到的任何信息:

          #!/usr/bin/env python
          
          # Copyright (c) 2001-2009 Twisted Matrix Laboratories.
          # See LICENSE for details.
          
          from twisted.internet.protocol import Protocol, Factory
          from twisted.internet import reactor
          
          ### Protocol Implementation
          
          # This is just about the simplest possible protocol
          class Echo(Protocol):
              def dataReceived(self, data):
                  """
                  As soon as any data is received, write it back.
                  """
                  self.transport.write(data)
          
          
          def main():
              f = Factory()
              f.protocol = Echo
              reactor.listenTCP(8000, f)
              reactor.run()
          
          if __name__ == '__main__':
              main()
          

          关于你最后一个问题:

          附:我可以为所有客户端只使用一个端口吗?

          是的,您可以(无论您使用什么语言/框架)。

          【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-09-12
          • 2021-01-06
          • 1970-01-01
          • 1970-01-01
          • 2020-04-30
          • 2010-11-08
          • 2015-04-11
          • 1970-01-01
          相关资源
          最近更新 更多