3

我正在尝试将这个预制的 C# tftp 服务器应用程序与我的 windows c# 表单一起使用。在作者的服务器示例中,效果很好,他使用了一个控制台应用程序。当我尝试将他的控制台示例移植到我的表单应用程序中时,它不起作用(没有错误,只是无法连接),我相信我的问题出在“使用”语句中:

using (var server = new TftpServer())
{
    server.OnReadRequest += new TftpServerEventHandler(server_OnReadRequest);
    server.OnWriteRequest += new TftpServerEventHandler(server_OnWriteRequest);
    server.Start();
    Console.Read();
}

不确定我是否理解正确,但我相信Console.Read()阻止应用程序退出的块。如果是这种情况,我将如何使用表单应用程序实现等价物。我只是无法理解“使用”。对不起,我是 C# 的新手。

4

4 回答 4

5

Windows 窗体将始终保持打开状态,直到它们被用户明确关闭。它们总是有一个线程读取消息队列以获取用户输入,因此它们不会像无限制的控制台应用程序那样退出。在 Windows 窗体中,我们必须比在控制台应用程序中更多地担心多线程和并发性。它大多是自然产生的,但并非总是如此。

因此,您不能真正使用等效的 toConsole.Read()来推迟using处理的执行,直到用户请求它。如果你这样做了,你的表单只会显得没有响应。

不过,你很幸运!C# 中的using块只不过IDisposable.Dispose()是在完成对象后记住调用的语法糖。因此,在 Forms 项目中与此等价的可能只是将server对象存储在类范围的字段中,然后调用server.Dispose(),比如说,一个Button.Click事件。当然,这只是一个例子。Form.Closing如果感觉更合适,您也可以这样做。

高级别的,你想做这样的事情:

  1. 在表单类中声明一个字段TftpServer server;
  2. 注册一个Load事件以及您server在构造函数中运行所需的任何内容。
  3. 在活动中打开您的server领域Form_Load
  4. 在您的生活中使用server您认为合适的事件Form。您可能不必担心并发性,但这是另一个问题的问题。
  5. 调用server.Dispose()表单的Dispose事件。

在本质上,

class main : Form
{
    private TftpServer server;

    public main()
    {
        InitializeComponent();

        this.Load += main_Load;

        server = new TftpServer();
        server.OnReadRequest += new TftpServerEventHandler(server_OnReadRequest);
        server.OnWriteRequest += new TftpServerEventHandler(server_OnWriteRequest);
    }

    private void main_Load(object sender, EventArgs e)
    {
        server.Start();
    }

    private void server_OnReadRequest(/* I wasn't sure of the arguments here */)
    {
        // use the read request: give or fetch its data (depending on who defines "read")
    }
    private void server_OnWriteRequest(/* I wasn't sure of the arguments here */)
    {
        // use the write request: give or fetch its data (depending on who defines "write")
    }

    protected override void Dispose(bool disposing)
    {
        if (server != null) // since Dispose can be called multiple times
        {
            server.Dispose();
            server = null;
        }
    }
}
于 2014-07-31T23:21:29.610 回答
3

问题是处置服务器是关闭它的原因。请记住,使用只是语法糖。以下两个代码块 [实际上] 等效:

var foo = new Foo();
try
{
   foo.Do();
}
finally
{
   foo.Dispose();
}

using (var foo = new Foo())
{
   foo.Do();
}

您可以阻止主线程在控制台应用程序中退出,但在表单应用程序中它是不同的。问题不在于您需要通过执行某种阻塞操作将线程保持在 using 中。那会很糟糕,并且该行为会锁定您的表单应用程序。问题是你不想使用 using。您希望在启动服务器时对其进行更新,然后在应用程序退出或停止单击时,使用 Dispose() 显式处理它。

于 2014-07-31T23:22:35.897 回答
1

一个小笔记也可以作为答案,你可以在这里使用一个 using 块,你只需将它放在你的 main 函数中:

...(make your form and stuff) 
using (var server = new TftpServer())
{
   server.OnReadRequest += new TftpServerEventHandler(server_OnReadRequest);
   server.OnWriteRequest += new TftpServerEventHandler(server_OnWriteRequest);
   server.Start();
   Application.Run(yourFormHere); //This blocks until the form is closed
}

我忘记提到的另一个选项是在您的表单中覆盖 Dispose。你可能想这样做。使用此选项,您可以保证您的服务器将被处置(禁止一些会阻止它以任何方式处置的事件[即内存不足])

于 2014-07-31T23:28:21.740 回答
1

在控制台应用程序中,您的TftpServer实例一直在侦听,直到线程退出,这仅在按下一个键后才被检测到Console.Read()

在您的表单应用程序中,Console.Read()它没有等待,因此using块完成,这导致您的服务器实例超出范围。

因此,您并没有完全滥用,using而是预期用途根本没有帮助您。看看使用任务并行库让一些后台任务异步运行。

于 2014-07-31T23:17:53.807 回答