c# - 服务停止后后台线程仍在侦听 TCP 端口
问题描述
我正在编写一个侦听 TCP 端口的 C# 服务(.NET 4.0)。我在后台线程(使用任务并行库)上启动 TcpListener,因此该服务不会对 Windows 无响应。每当客户端连接时,我也会使用 TPL,因为每个客户端都会做一些数据库工作,我不想阻止其他客户端。
我在 Windows Server 2012 R2 Standard 上使用 InstallUtil.exe 安装和卸载该服务。每当我停止服务并卸载它时,使用 netstat -abo 我可以看到 [System] 进程仍在监听该端口。它有一个 PID,但是我在任务管理器或任务列表中看不到具有此 PID 的进程,也不能用 taskkill 杀死它。它只是说找不到进程,但是当我运行 netstat -abo 时它总是在那里。如果我尝试使用相同的端口再次启动服务,则会收到套接字异常:
Only one usage of each socket address (protocol/network address/port) is normally permitted
Stacktrace:
at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.Sockets.Socket.Bind(EndPoint localEP)
at System.Net.Sockets.TcpListener.Start(Int32 backlog)
我猜我的后台线程在我停止服务后仍然存在,但我不知道现在如何杀死它们,以及如何防止这种情况发生。以下是我希望的相关代码(已删除异常处理和日志记录以便于阅读):
public partial class MyService : ServiceBase
{
private static TCPServer server = null;
protected override void OnStart(string[] args)
{
server = new TCPServer();
}
protected override void OnStop()
{
if (server != null)
{
server.StopServer();
}
}
}
class TCPServer
{
public static TcpListener listener = null;
private static Task listenerTask = null;
private static List<Task> clientTasks = new List<Task>();
public TCPServer()
{
listenerTask = new Task(() => StartServer());
listenerTask.Start();
}
public void StopServer()
{
foreach(Task task in clientTasks)
{
task.Dispose();
}
listenerTask.Dispose();
if (listener != null)
{
listener.Stop();
listener = null;
}
}
private void StartServer()
{
Int32 port = 51987;
IPAddress localAddr = GetLocalIP();
listener = new TcpListener(localAddr, port);
listener.Start();
while (listener != null)
{
if (listener.Pending())
{
TcpClient client = listener.AcceptTcpClient();
Task task = new Task((obj) => ProcessClient(obj), client);
task.Start();
clientTasks.Add(task);
}
}
}
private void ProcessClient(object obj)
{
using (TcpClient client = obj as TcpClient)
{
Byte[] bytes = new Byte[2048];
String data = null;
NetworkStream stream = client.GetStream();
int i;
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
data = Encoding.ASCII.GetString(bytes, 0, i);
}
// do some stuff with data
// If an exception is thrown here, the rogue thread issue happens when I stop the service.
// Otherwise, everything is good - I stop the service and no rogue thread, I can reuse the listener port.
}
}
}
编辑: 用建议的更改更新了代码。我还发现,只有在我的一个客户端线程中引发异常时,才会出现这种流氓线程问题。如果一切正常,当我停止服务时就没有恶意线程。
解决方案
在这种情况下,“停止”方法意味着停止当前连接并开始监听新连接,因此请确保您的客户端已正确关闭,以免创建新连接。
Stop 方法还关闭底层 Socket 并为 TcpListener 创建一个新的 Socket。如果在调用 Stop 方法之前在基础 Socket 上设置了任何属性,这些属性将不会传递到新的 Socket。
https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.stop(v=vs.110).aspx#
此外,请确保您using
与客户端一起使用
using(TcpClient client = obj as TcpClient){//DO SOMETHING}
推荐阅读
- amazon-web-services - 如何管理认知令牌生命周期
- php - Wordpress Cron 作业未运行
- python - python 参数解析器。可执行文件和源代码文件作为参数
- php - 使用旧版本数据库的新版本 Laravel 项目
- wireshark - 将wireshark与ns3一起使用
- libgdx - 在 libgdx 中设置文本 alpha 的最佳方法
- python - 试图找到这种特殊情况的正则表达式?我也可以在不创建组的情况下解析它吗?
- php - 如何在(高级)自定义字段更新上添加订单注释?
- ip-camera - 在霍尼韦尔 HBL6GR2 上查找实时快照的 URL
- php - 如何在函数中使用动态变量与 PHP 和 MySQL 中的查询