如何核心关闭套接字线程 [英] How to close a socket thread corectly

查看:87
本文介绍了如何核心关闭套接字线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家好。

我创建了一个许多客户端连接到它的服务器应用程序。每个连接都有一个线程,没有线程池。客户端的管理员,比如客户端A,想要断开客户端B.

目前,我通过关闭与客户端B相关联的线程B的套接字断开客户端B.

Hello guys.
I have created a server application that many clients connect to it. every connection has a thread, no thread pooling. The admin at a client, say client A, wants to disconnect client B.
Currently, I disconnect client B by closing the socket of thread B, which is associated to client B.

//this code is executed in thread A
closesocket(socketB);

我希望这段代码会导致recv函数立即以值-1或0返回,打破循环,最后终止线程B.它运行良好很多次,崩溃有时候,停止服务。这是断开客户端的正确方法吗?如果没有,那么正确的方法是什么?如果线程A在套接字B未等待时关闭套接字B会发生什么情况,例如,当线程B在此代码中执行myFunc1时:

I expect this code will cause recv function to immediately return with value -1 or 0, breaking the loop, and finally terminating thread B. It works fine many times, and crashes some times, stopping the service. Is this a correct way to disconnect a client? If not, what is the correct way? What happens if thread A closes socket B when socket B is not waiting, for example, When thread B is executing myFunc1 in this code:

while (1)
{
  int iResult = recv(socketB, buf, len, flags);
  if (iResult <= 0) break;
  myFunc1(buf, iResult);
}

提前致谢

mr.abzadeh

Thanks in advance
mr.abzadeh

推荐答案

使用以下一组功能: WSAEventSelect WSACreateEvent WSAWaitForMultipleEvents 。请注意 WSAEventSelect 会自动将套接字置于非阻塞模式,但这不是问题,因为您将阻止使用 WSAWaitForMultipleEvents 并且您可以通过使用您使用 WSACreateEvent 创建的事件来唤醒它(您已经正确地找到了它)。由于你的套接字是非阻塞的,当你对套接字做一些事情(比如recv / send)时总是处理 WSAEWOULDBLOCK 错误,因为 WSAEWOULDBLOCK 实际上并不是一个错误,在非阻塞套接字的情况下接收此错误很自然。如果使用 WSAEventSelect + WSACreateEvent + WSAWaitForMultipleEvents,你可以提交另一个非常大的错误:关于套接字变为可写事件的这些函数的信令是边沿触发的(而其余事件是在电平触发模式下处理的)。如果您不知道触发的级别/边缘意味着什么:



如果您想编写套接字但其发送缓冲区已满,那么您将开始等待套接字以上api。当套接字的写缓冲区中有一些可用空间可用时,系统会向您发送有关此事件的事件。但是因为套接字可写事件是边缘触发事件,如果你没有写入套接字直到它的发送缓冲区已满,那么即使你开始等待另一个套接字变成可写的事件,你将永远不会收到它,因为它仅当套接字的发送缓冲区填满(从非完整转换为完整)时,系统才会发送。因此,您应该以这种方式处理套接字可写事件:在某处保留socket_writablebool并将其设置为true。当您要写入数据时,请检查此bool值:如果为true,则只需开始将数据写入套接字,否则开始等待写入事件。如果您正在编写套接字并且其发送缓冲区已满(您收到 WSAEWOULDBLOCK )将剩余数据存储在某处并将套接字的socket_writable标志设置为false并开始等待对于套接字可写事件。



总结:你能做的最好的事情是假设套接字是可写的,如果你收到 WSAEWOULDBLOCK 在尝试编写套接字时,它的时间开始等待套接字可写事件。我们刚刚使用bool值来保存一些系统调用,如果我们知道套接字当前不在可写状态,那么向它发送数据肯定会返回 WSAEWOULDBLOCK 。一个天真的非优化实现总是会调用套接字上的send,如果当前的数据以 WSAEWOULDBLOCK 失败,它将存储数据供以后发送。
Use the following set of functions: WSAEventSelect, WSACreateEvent, WSAWaitForMultipleEvents. Note that WSAEventSelect automatically puts your socket into nonblocking mode but this isnt a problem as you will block using WSAWaitForMultipleEvents and you can wake it up by using an event you create using WSACreateEvent (you have found that out correctly). Since your sockets are nonblocking always handle WSAEWOULDBLOCK errors when you do something with the socket (like recv/send) because WSAEWOULDBLOCK isn't really an error, its natural to receive this error in case of nonblocking sockets. There is another very big mistake you can commit in case of using WSAEventSelect + WSACreateEvent + WSAWaitForMultipleEvents: The signaling of these functions about socket-became-writable event is edge triggered (while the rest of the events are handled in level triggered mode). If you don't know what level/edge triggered means:

If you want to write the socket but its send buffer is full then you start waiting for the socket with the above apis. When some free space becomes available in the write buffer of the socket the system sends you an event about this. BUT since the socket-became-writable event is an edge triggered event if you don't write to the socket until its send buffer is full then even if you start waiting for another socket-became-writable event you will never receive it because it is sent by the system only when the send buffer of the socket fills up (transitions from non-full to full). For this reason you should handle the socket-writable event this way: Keep a "socket_writable" bool somewhere and set it to true initially. When you have data to write check this bool value: if its true then simply start writing the data to the socket otherwise start waiting for a write event. If you are writing the socket and its send buffer fills up (you receive WSAEWOULDBLOCK) store the remaining data somewhere and set the "socket_writable" flag of the socket to false and start waiting for a socket-writable event.

Summing it up: The best you can do is assuming that the socket is writable and if you receive WSAEWOULDBLOCK while trying to write the socket then its time to start waiting for the socket-writable event. We have just used a bool value to save some system calls if we know that the socket isn't currently in a writable state so sending data to it would return WSAEWOULDBLOCK for sure. A naive non-optimized implementation would always call send on the socket and it would store the data for later send if the current one failed with WSAEWOULDBLOCK.

这实际上应该可以正常工作(至少是套接字部分)......崩溃可能是由于与线程同步性有关的其他事情。



有一件事是肯定的,但是在一个阻塞套接字上,关闭套接字将始终从recv()调用返回(至少在Windows中,Wine实现'这样做,但如果你只是Windows,那应该不是问题。)



来自MSDN的recv()描述:

This actually should work fine (at least the socket portion)... the crashing may be due to something else related to synchronicity of the threads.

One thing is for sure though, on a blocking socket, closing the socket WILL always return from the recv() call (at least in Windows, Wine implementations don't do this, but if you're Windows only, it shouldn't be a problem).

From the recv() description from MSDN:
Quote:

如果套接字是面向连接的,并且远程端已正常关闭连接,并且已收到所有数据, recv将立即完成,并收到零字节。如果连接已重置,则recv将失败,并显示错误WSAECONNRESET。

If the socket is connection oriented and the remote side has shut down the connection gracefully, and all data has been received, a recv will complete immediately with zero bytes received. If the connection has been reset, a recv will fail with the error WSAECONNRESET.


这篇关于如何核心关闭套接字线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆