【问题标题】:Best practice for initializing Winsock in a networking library在网络库中初始化 Winsock 的最佳实践
【发布时间】:2021-02-14 21:55:09
【问题描述】:

我正在编写一个小型库,它在内部使用 OS 套接字 API 进行一些网络连接。

在 Windows 上,需要通过在开头调用 WSAStartup 并在结尾调用 WSACleanup 来初始化 Winsock。但是documentation 表示允许多次初始化 Winsock,然后清理相同次数,因为它在内部使用引用计数。

现在我正在解决一个难题。对于图书馆作者来说,更好的做法是什么?

  1. 提供调用WSAStartup/WSACleanup的全局函数initializeLibrary/terminateLibrary,并指示用户在其应用程序的开头/结尾调用它们。
  2. 在我的类的构造函数/析构函数内部调用WSAStartup/WSACleanup,根本不用打扰用户。

现在我看到第二个选项看起来更方便,但这是个好主意吗?这样做不会有什么隐藏的不良后果吗?它会对性能产生影响吗?图书馆偷偷做这件事是个好习惯吗?

【问题讨论】:

  • 它会影响性能吗? -- 你认为引用计数会阻止什么?另外,像 boost::asio 这样的库是如何处理这个问题的?
  • 嗯,是的,但它仍然可能会锁定一些互斥锁,因为它希望是线程安全的。

标签: c++ sockets networking network-programming winsock2


【解决方案1】:

图书馆偷偷做这件事是个好习惯吗?

我可能在这里遗漏了重点,但对我而言,any 库的重点是从最终用户那里抽象出细节,从而轻松使用它。当然,抽象可能会在一定程度上限制灵活性,但我觉得这里不是这种情况。请注意,我只是在解决“保密”问题,老实说,我不知道它如何影响 e。 G。性能。

对于图书馆作者来说,更好的做法是什么?

您在这里缺少第三个选项。我通常反对init()finalize() 函数,因为它违背了RAII——用户可能只是忘记调用它们。但是,您可以设计一个仅在应用程序的“根”处创建的“令牌”类,这是使用 API 的任何其他部分所必需的。考虑一下:

#include <WinSock2.h>

class ApiKey {
public:
    ApiKey() {
        auto wsadata = WSADATA();
        auto startupResult = WSAStartup(MAKEWORD(2, 2), &wsadata);
        // ...
    }

    ~ApiKey() {
        auto cleanupResult = WSACleanup();
        // ...
    }
};

class Socket {
public:
    Socket(ApiKey& key) {
    }
    // ...
};

我不是 boost::asio 用户,但据我所知,他们就是这样做的。 io_context 类用作ApiKey

如果您想了解更多信息,可以在他们的源代码中查找:

https://github.com/boostorg/asio/blob/develop/include/boost/asio/detail/impl/winsock_init.ipp

https://github.com/boostorg/asio/blob/develop/include/boost/asio/io_context.hpp

【讨论】:

  • 嗯,上下文对象。实际上不是一个坏主意。
猜你喜欢
  • 1970-01-01
  • 2010-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-20
相关资源
最近更新 更多