具有 recv/send 命令和 request/response 设计的 C++ 服务器
C++ server with recv/send commands & request/response design
我正在尝试创建一个带有阻塞套接字的服务器(每个新客户端一个新线程)。该线程应该能够接收来自客户端的命令(并发回结果)并定期向客户端发送命令(并请求返回结果)。
我的想法是为每个客户端创建两个线程,一个用于 recv
,第二个用于 send
。然而:
- 它是普通线程开销的两倍。
- 由于 request/response 设计,我在第一个线程(等待客户端命令)中执行的 recv 可能是我在第二个线程(客户端发送的结果)中查找的请求,反之亦然。使其全部正确同步可能是一个地狱故事。所以现在我正在考虑以这种方式从单个线程执行此操作:
循环中:
setsockopt(SO_RCVTIMEO, &small_timeout);
// 设置接收超时(例如 1000 毫秒)。
recv();
// 首先检查客户端的请求。如果 returns WSAETIMEDOUT 比我假设没有请求数据并且什么都不做。如果我收到正常请求,我会处理它。
if (clientbufferToSend != nullptr) send(clientbufferToSend);
// 现在当客户端的请求被处理后,我们检查我们必须发送给客户端的命令列表。如果队列中有命令,我们发送它们。 SO_SNDTIMEO 可以将超时设置为较大的值,这样我们就不会在客户端断开连接时出现死锁。
setsockopt(SO_RCVTIMEO, &large_timeout);
// 设置 recv 的超时时间(最大 SO_SNDTIMEO,只是为了不死锁)。
recv();
// 现在我们等待客户端的响应。
这是做我想做的事情的合法方式吗?或者有更好的选择(最好是阻塞套接字和线程)?
P.S。 recv()
with timeout returns WSAETIMEDOUT
只有在没有数据可用的情况下吗?如果有数据,它会 return 这个错误,但是 recv()
没有足够快地处理所有数据,因此 return 正在处理部分数据吗?
一种方法是只创建一个后台线程来从该套接字中读取数据。在任何随机线程上写下你主动提出的事件。
您需要以下内容。
每个套接字的关键部分或互斥锁用于序列化写入,例如当后台线程正在发送对客户端发起的消息的响应,而其他线程想要向同一客户端发送消息时。
一些其他同步原语,例如客户端线程在等待响应时休眠的条件变量。
接收消息的后台线程需要区分客户端发起的消息(需要由同一个后台线程响应)和服务器发起的消息的响应。如果您的网络协议没有该数据,您将不得不更改协议。
如果您的服务器启动的事件只发生在单个线程上,这将正常工作,例如它们来自一些序列化源,例如设备或 OS 接口。
但是,如果事件源也是多线程的,并且您想要良好的性能,那么您将需要非平凡的复杂性来将响应分派到正确的服务器线程,例如每个客户端线程 1 个条件变量,也许是一些队列,等等
我正在尝试创建一个带有阻塞套接字的服务器(每个新客户端一个新线程)。该线程应该能够接收来自客户端的命令(并发回结果)并定期向客户端发送命令(并请求返回结果)。
我的想法是为每个客户端创建两个线程,一个用于 recv
,第二个用于 send
。然而:
- 它是普通线程开销的两倍。
- 由于 request/response 设计,我在第一个线程(等待客户端命令)中执行的 recv 可能是我在第二个线程(客户端发送的结果)中查找的请求,反之亦然。使其全部正确同步可能是一个地狱故事。所以现在我正在考虑以这种方式从单个线程执行此操作:
循环中:
setsockopt(SO_RCVTIMEO, &small_timeout);
// 设置接收超时(例如 1000 毫秒)。recv();
// 首先检查客户端的请求。如果 returns WSAETIMEDOUT 比我假设没有请求数据并且什么都不做。如果我收到正常请求,我会处理它。if (clientbufferToSend != nullptr) send(clientbufferToSend);
// 现在当客户端的请求被处理后,我们检查我们必须发送给客户端的命令列表。如果队列中有命令,我们发送它们。 SO_SNDTIMEO 可以将超时设置为较大的值,这样我们就不会在客户端断开连接时出现死锁。setsockopt(SO_RCVTIMEO, &large_timeout);
// 设置 recv 的超时时间(最大 SO_SNDTIMEO,只是为了不死锁)。recv();
// 现在我们等待客户端的响应。
这是做我想做的事情的合法方式吗?或者有更好的选择(最好是阻塞套接字和线程)?
P.S。 recv()
with timeout returns WSAETIMEDOUT
只有在没有数据可用的情况下吗?如果有数据,它会 return 这个错误,但是 recv()
没有足够快地处理所有数据,因此 return 正在处理部分数据吗?
一种方法是只创建一个后台线程来从该套接字中读取数据。在任何随机线程上写下你主动提出的事件。
您需要以下内容。
每个套接字的关键部分或互斥锁用于序列化写入,例如当后台线程正在发送对客户端发起的消息的响应,而其他线程想要向同一客户端发送消息时。
一些其他同步原语,例如客户端线程在等待响应时休眠的条件变量。
接收消息的后台线程需要区分客户端发起的消息(需要由同一个后台线程响应)和服务器发起的消息的响应。如果您的网络协议没有该数据,您将不得不更改协议。
如果您的服务器启动的事件只发生在单个线程上,这将正常工作,例如它们来自一些序列化源,例如设备或 OS 接口。
但是,如果事件源也是多线程的,并且您想要良好的性能,那么您将需要非平凡的复杂性来将响应分派到正确的服务器线程,例如每个客户端线程 1 个条件变量,也许是一些队列,等等