在网络库中初始化 Winsock 的最佳实践
Best practice for initializing Winsock in a networking library
我正在编写一个小型库,它在内部使用 OS 套接字 API 进行一些网络连接。
在Windows上,需要在开头调用WSAStartup
,在结尾调用WSACleanup
来初始化Winsock。但是 documentation 表示允许多次初始化 Winsock 然后清理相同的次数,因为它在内部使用引用计数。
现在我正在解决一个难题。图书馆作家的更好做法是什么?
- 提供调用
WSAStartup
/WSACleanup
的全局函数 initializeLibrary
/terminateLibrary
并指示用户在其应用程序的 beginning/end 调用它们.
- 在我的 类 的 constructors/destructors 内部调用
WSAStartup
/WSACleanup
,根本不要打扰用户。
现在我看到第二个选项看起来更方便,但它是个好主意吗?这样做没有任何隐藏的不良后果吗?它会对性能产生影响吗?图书馆秘密地做这件事是一种好习惯吗?
Is it a good practice for a library to do this secretly?
我可能忽略了这里的要点,但对我来说 any 库的要点是从 end-user 中抽象出细节,使用它 hassle-free。当然,抽象可能会在某种程度上限制灵活性,但我不认为这里是这种情况。请注意,我只是在解决 'secrecy' 问题,老实说,我不知道它如何影响 e。 G。性能。
What is a better practice for library writers?
您在这里错过了第三个选项。我通常反对 init()
和 finalize()
函数,因为它违背了 RAII - 用户可能只是忘记调用它们。但是,您可以设计一个 'token' class,仅在应用程序的 'root' 处创建,需要使用 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
class 作为 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
我正在编写一个小型库,它在内部使用 OS 套接字 API 进行一些网络连接。
在Windows上,需要在开头调用WSAStartup
,在结尾调用WSACleanup
来初始化Winsock。但是 documentation 表示允许多次初始化 Winsock 然后清理相同的次数,因为它在内部使用引用计数。
现在我正在解决一个难题。图书馆作家的更好做法是什么?
- 提供调用
WSAStartup
/WSACleanup
的全局函数initializeLibrary
/terminateLibrary
并指示用户在其应用程序的 beginning/end 调用它们. - 在我的 类 的 constructors/destructors 内部调用
WSAStartup
/WSACleanup
,根本不要打扰用户。
现在我看到第二个选项看起来更方便,但它是个好主意吗?这样做没有任何隐藏的不良后果吗?它会对性能产生影响吗?图书馆秘密地做这件事是一种好习惯吗?
Is it a good practice for a library to do this secretly?
我可能忽略了这里的要点,但对我来说 any 库的要点是从 end-user 中抽象出细节,使用它 hassle-free。当然,抽象可能会在某种程度上限制灵活性,但我不认为这里是这种情况。请注意,我只是在解决 'secrecy' 问题,老实说,我不知道它如何影响 e。 G。性能。
What is a better practice for library writers?
您在这里错过了第三个选项。我通常反对 init()
和 finalize()
函数,因为它违背了 RAII - 用户可能只是忘记调用它们。但是,您可以设计一个 'token' class,仅在应用程序的 'root' 处创建,需要使用 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
class 作为 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