如何使用 RAII 对套接字进行建模
Posted
技术标签:
【中文标题】如何使用 RAII 对套接字进行建模【英文标题】:How to model sockets with RAII 【发布时间】:2012-04-26 19:27:56 【问题描述】:我是 C++ 的新手(来自 C)。我从概念上理解 RAII 应该如何工作,但我无法在其中安装一个简单的套接字连接处理程序。
当前代码:
void accept_ev(event_handler::token &t, int listenfd)
int newfd = accept(listenfd, NULL, NULL);
if (newfd < 0)
throw api_server_accept_failed(*this, errno);
connections.insert(api_server_connection(newfd));
这显然不安全,因为 api_server_connection 构造函数可能会在将 fd 分配给其成员变量之前引发异常。
所以我的下一个想法是将接受移动到构造函数中。问题是我真的希望 api_server_connection 不知道 fd 的来源。例如。如果我以后想支持inetd,它也可以像fd 0一样传递到程序中。
那么我该怎么做。我应该为每种获取 fd 的方式使用不同的构造函数吗?我应该做子类吗?另一种选择可能是有一个 lambda 函数?
或者在这种情况下我应该抓住任何错误并关闭调用者中的 fd 吗?
【问题讨论】:
你看过/读过Boost ASIO是如何工作的吗? (你有没有考虑过完全跳过这个而只使用 ASIO?)api_server_connection
如何引发异常?您还没有向我们展示该代码。
@R.MartinhoFernandes 我还没写……但我知道它会有 pimpl,这意味着内存分配,可能会失败。
@JerryCoffin 我没有,但这个问题并不是关于套接字(我犹豫是否添加那个标签),而是关于 RAII。
【参考方案1】:
暂时先不管sockets,你通常想做的就是把事情分成两个阶段。
在第一阶段,您会做一些可能会抛出的事情,但如果他们这样做了,您可以将系统恢复到正常状态(最好是好像什么都没发生过的状态)。
在第二阶段,你做了一些你可能无法撤消,但你知道肯定永远不会扔掉的事情。
为此,您需要对可以/将要抛出的内容有一些保证,并且(特别是)对至少一些根本不会抛出的相当具体的操作(例如,交换两个项目)有一些保证。
为了促进这一点,您通常希望在 dtor 中恢复到正常状态,因此如果抛出异常,析构函数将自动清理。
不幸的是,很难说更多关于您的具体代码/情况的信息,因为我们对您正在使用的类了解不够多。
【讨论】:
我认为您实际上让我以正确的方式思考,所以我想我应该接受这一点。我将创建一个 fd_guard 类并首先将 fd 插入其中,以一种不会失败的方式。然后我就可以无忧无虑地创建连接对象,让它继承fd或fd_guard。【参考方案2】:首先,要使用 RAII,您必须以面向对象的方式进行思考。所以我看到您尝试使用 C++ 实现服务器类。在这种情况下,您将仅将 RAII 用于服务器初始化,这意味着您将编写代码并在服务器开始侦听端口时完成它。构造函数中的最后一个函数调用必须是监听或启动连接线程。之后,您必须实现将处理客户端连接的第二个线程。第二个线程将调用 accept 以接受客户端,并将在服务器工作时进行迭代。在您的析构函数中,您只需将侦听标志设置为 false 并等待接受线程终止,然后关闭所有套接字。
【讨论】:
以上是关于如何使用 RAII 对套接字进行建模的主要内容,如果未能解决你的问题,请参考以下文章