无法停止异步 TCP 服务器 Windows 服务
Can not stop async TCP sever Windows service
我有一个在 .net 4.0 中开发的带有异步服务器套接字的 TCP 服务器 windows 服务。
它有效,但大约 90% 的时间我根本无法停止它:在 Windows 服务控制台中按下停止按钮后,它挂起并在大约一分钟后停止,但它的过程继续并TCP 通信继续。 它挂在 _listener.Close()。 我唯一能做的就是重新启动 Windows 来关闭通信。可能有关闭套接字的东西。我试图弄清楚,但我根本找不到问题的根源。
客户端不受我控制,它们是大约 100 个通过 TCP 发送数据的小工具。
这是我的代码(已更新)。
非常感谢,非常感谢任何建议!
public class DeviceTCPServer
{
public readonly IPAddress IPAddress;
public readonly int Port;
public readonly int InputBufferSize;
public readonly ConcurrentDictionary<Guid, StateObject> Connections;
public event EventHandler OnStarted;
public event EventHandler OnStopped;
public event ServerEventHandler OnConnected;
public event ServerEventHandler OnDisconnected;
public event RecievedEventHandler OnRecieved;
public event DroppedEventHandler OnDropped;
public event ExceptionEventHandler OnException;
public event ServerLogEventHandler ServerLog;
private volatile bool _iAmListening;
private Socket _listener;
private Thread _listenerThread;
private readonly ManualResetEvent _allDone = new ManualResetEvent(false);
public bool Listening
{
get { return _iAmListening; }
}
public DeviceTCPServer(IPAddress ipAddress,
int port,
int inputBufferSize)
{
IPAddress = ipAddress;
Port = port;
InputBufferSize = inputBufferSize;
Connections = new ConcurrentDictionary<Guid, StateObject>();
}
public void ThreadedStart()
{
_listenerThread = new Thread(Start)
{
CurrentUICulture = Thread.CurrentThread.CurrentUICulture,
IsBackground = true
};
_listenerThread.Start();
}
private void Start()
{
try
{
var localEP = new IPEndPoint(IPAddress, Port);
_listener = new Socket(localEP.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_listener.Bind(localEP);
_listener.Listen(10000);
if (OnStarted != null)
OnStarted(this, new EventArgs());
_iAmListening = true;
var listenerWithCultureInfo = new Tuple<Socket, CultureInfo>(_listener,
Thread.CurrentThread.CurrentUICulture);
while (_iAmListening)
{
_allDone.Reset();
_listener.BeginAccept(AcceptCallback, listenerWithCultureInfo);
_allDone.WaitOne();
}
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, "Start"));
}
}
public void StopListening()
{
try
{
_iAmListening = false;
_allDone.Set();
}
catch(Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, "StopListening"));
}
}
public void Stop()
{
try
{
_listener.Close(0);
CloseAllConnections();
_listenerThread.Abort();
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, "Stop"));
}
}
private void AcceptCallback(IAsyncResult ar)
{
var arTuple = (Tuple<Socket, CultureInfo>)ar.AsyncState;
var state = new StateObject(arTuple.Item2, InputBufferSize);
try
{
Connections.AddOrUpdate(state.Guid,
state,
(k, v) => v);
Thread.CurrentThread.CurrentUICulture = state.CurrentUICulture;
var listener = arTuple.Item1;
var handler = listener.EndAccept(ar);
_allDone.Set();
if (!_iAmListening)
return;
state.WorkSocket = handler;
handler.BeginReceive(state.Buffer, 0, state.InputBufferSize, 0,
RecieveCallBack, state);
if (OnConnected != null)
OnConnected(this, new ServerEventArgs(state));
}
catch(ObjectDisposedException)
{
_allDone.Set();
return;
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, state, "AcceptCallback"));
}
}
public void RecieveCallBack(IAsyncResult ar)
{
var state = (StateObject)ar.AsyncState;
try
{
Thread.CurrentThread.CurrentUICulture = state.CurrentUICulture;
var handler = state.WorkSocket;
var read = handler.EndReceive(ar);
var pBinayDataPocketCodecStore = new BinayDataPocketCodecStore();
if (read > 0)
{
state.LastDataReceive = DateTime.Now;
var data = new byte[read];
Array.Copy(state.Buffer, 0, data, 0, read);
state.AddBytesToInputDataCollector(data);
//check, if pocket is complete
var allData = state.InputDataCollector.ToArray();
var codecInitRes = pBinayDataPocketCodecStore.Check(allData);
if (codecInitRes.Generic.Complete)
{
if (!codecInitRes.Generic.Drop)
{
if (OnRecieved != null)
OnRecieved(this, new RecievedEventArgs(state, allData));
}
else
{
if (OnDropped != null)
OnDropped(this, new DroppedEventArgs(state, codecInitRes.Generic));
//get new data
state.ResetInputDataCollector();
handler.BeginReceive(state.Buffer, 0, state.InputBufferSize, 0,
RecieveCallBack, state);
}
}
else
{
//get more data
handler.BeginReceive(state.Buffer, 0, state.InputBufferSize, 0,
RecieveCallBack, state);
}
}
else
{
if ((handler.Connected == false) || (handler.Available == 0))
{
Close(state);
}
}
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, state, "RecieveCallBack"));
}
}
public void Send(StateObject state, byte[] data)
{
try
{
var handler = state.WorkSocket;
handler.BeginSend(data, 0, data.Length, 0,
SendCallback, state);
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, state, "Send"));
}
}
private void SendCallback(IAsyncResult ar)
{
var state = (StateObject)ar.AsyncState;
try
{
Thread.CurrentThread.CurrentUICulture = state.CurrentUICulture;
var handler = state.WorkSocket;
handler.EndSend(ar);
handler.BeginReceive(state.Buffer, 0, state.InputBufferSize, 0,
RecieveCallBack, state);
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, state, "SendCallback"));
}
}
public void Close(StateObject state)
{
try
{
if (state == null)
return;
var handler = state.WorkSocket;
if (handler == null)
return;
if (!handler.Connected)
return;
if (handler.Available > 0)
{
var data = new byte[handler.Available];
handler.Receive(data);
}
handler.Shutdown(SocketShutdown.Both);
handler.Close(0);
if (OnDisconnected != null)
OnDisconnected(this, new ServerEventArgs(state));
StateObject removed;
var removeResult = Connections.TryRemove(state.Guid, out removed);
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, "Close"));
}
}
private void CloseAllConnections()
{
try
{
var connections = Connections.Select(c => c.Value);
foreach(var connection in connections)
{
Close(connection);
}
}
catch(Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, "CloseAllConnections"));
}
}
public override string ToString()
{
return string.Format("{0}:{1}", IPAddress, Port);
}
}
由于这是一项服务,通常的 "wait for the non-background threads to exit" 规则不适用,您的工作是杀死工作人员(这可能包括挂起的异步操作)。现在;目前,您正在杀死 listener,但这只会阻止 new 套接字连接。理想情况下,您应该在某个地方跟踪您的客户,这样您也可以杀死所有 client 套接字。当您完成 那个 后,您应该没问题。请记住同步访问任何客户端集合,或使用线程安全的集合类型。
我认为您应该在关闭套接字之前使用 Socket.Shutdown 方法。正如文档中所述:
When using a connection-oriented Socket, always call the Shutdown method before closing the Socket. This ensures that all data is sent and received on the connected socket before it is closed.
此外,也许我错了,但我看不到对 StopListening() 方法的任何调用。此后 _iAmListening 字段未设置为 false,并且回调 (AcceptCallback(IAsyncResult ar)) 仍在响应来电。
所以,问题终于解决了,我的Windows服务没有任何问题!
这是由 Microsoft Windows 更新引起的:
Microsoft KB4338815,它导致 closesocket 在 Intel Xeon 处理器上永远挂起:https://forum.filezilla-project.org/viewtopic.php?t=49308
感谢您为帮助我所做的一切努力!
我有一个在 .net 4.0 中开发的带有异步服务器套接字的 TCP 服务器 windows 服务。
它有效,但大约 90% 的时间我根本无法停止它:在 Windows 服务控制台中按下停止按钮后,它挂起并在大约一分钟后停止,但它的过程继续并TCP 通信继续。 它挂在 _listener.Close()。 我唯一能做的就是重新启动 Windows 来关闭通信。可能有关闭套接字的东西。我试图弄清楚,但我根本找不到问题的根源。
客户端不受我控制,它们是大约 100 个通过 TCP 发送数据的小工具。
这是我的代码(已更新)。
非常感谢,非常感谢任何建议!
public class DeviceTCPServer
{
public readonly IPAddress IPAddress;
public readonly int Port;
public readonly int InputBufferSize;
public readonly ConcurrentDictionary<Guid, StateObject> Connections;
public event EventHandler OnStarted;
public event EventHandler OnStopped;
public event ServerEventHandler OnConnected;
public event ServerEventHandler OnDisconnected;
public event RecievedEventHandler OnRecieved;
public event DroppedEventHandler OnDropped;
public event ExceptionEventHandler OnException;
public event ServerLogEventHandler ServerLog;
private volatile bool _iAmListening;
private Socket _listener;
private Thread _listenerThread;
private readonly ManualResetEvent _allDone = new ManualResetEvent(false);
public bool Listening
{
get { return _iAmListening; }
}
public DeviceTCPServer(IPAddress ipAddress,
int port,
int inputBufferSize)
{
IPAddress = ipAddress;
Port = port;
InputBufferSize = inputBufferSize;
Connections = new ConcurrentDictionary<Guid, StateObject>();
}
public void ThreadedStart()
{
_listenerThread = new Thread(Start)
{
CurrentUICulture = Thread.CurrentThread.CurrentUICulture,
IsBackground = true
};
_listenerThread.Start();
}
private void Start()
{
try
{
var localEP = new IPEndPoint(IPAddress, Port);
_listener = new Socket(localEP.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_listener.Bind(localEP);
_listener.Listen(10000);
if (OnStarted != null)
OnStarted(this, new EventArgs());
_iAmListening = true;
var listenerWithCultureInfo = new Tuple<Socket, CultureInfo>(_listener,
Thread.CurrentThread.CurrentUICulture);
while (_iAmListening)
{
_allDone.Reset();
_listener.BeginAccept(AcceptCallback, listenerWithCultureInfo);
_allDone.WaitOne();
}
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, "Start"));
}
}
public void StopListening()
{
try
{
_iAmListening = false;
_allDone.Set();
}
catch(Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, "StopListening"));
}
}
public void Stop()
{
try
{
_listener.Close(0);
CloseAllConnections();
_listenerThread.Abort();
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, "Stop"));
}
}
private void AcceptCallback(IAsyncResult ar)
{
var arTuple = (Tuple<Socket, CultureInfo>)ar.AsyncState;
var state = new StateObject(arTuple.Item2, InputBufferSize);
try
{
Connections.AddOrUpdate(state.Guid,
state,
(k, v) => v);
Thread.CurrentThread.CurrentUICulture = state.CurrentUICulture;
var listener = arTuple.Item1;
var handler = listener.EndAccept(ar);
_allDone.Set();
if (!_iAmListening)
return;
state.WorkSocket = handler;
handler.BeginReceive(state.Buffer, 0, state.InputBufferSize, 0,
RecieveCallBack, state);
if (OnConnected != null)
OnConnected(this, new ServerEventArgs(state));
}
catch(ObjectDisposedException)
{
_allDone.Set();
return;
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, state, "AcceptCallback"));
}
}
public void RecieveCallBack(IAsyncResult ar)
{
var state = (StateObject)ar.AsyncState;
try
{
Thread.CurrentThread.CurrentUICulture = state.CurrentUICulture;
var handler = state.WorkSocket;
var read = handler.EndReceive(ar);
var pBinayDataPocketCodecStore = new BinayDataPocketCodecStore();
if (read > 0)
{
state.LastDataReceive = DateTime.Now;
var data = new byte[read];
Array.Copy(state.Buffer, 0, data, 0, read);
state.AddBytesToInputDataCollector(data);
//check, if pocket is complete
var allData = state.InputDataCollector.ToArray();
var codecInitRes = pBinayDataPocketCodecStore.Check(allData);
if (codecInitRes.Generic.Complete)
{
if (!codecInitRes.Generic.Drop)
{
if (OnRecieved != null)
OnRecieved(this, new RecievedEventArgs(state, allData));
}
else
{
if (OnDropped != null)
OnDropped(this, new DroppedEventArgs(state, codecInitRes.Generic));
//get new data
state.ResetInputDataCollector();
handler.BeginReceive(state.Buffer, 0, state.InputBufferSize, 0,
RecieveCallBack, state);
}
}
else
{
//get more data
handler.BeginReceive(state.Buffer, 0, state.InputBufferSize, 0,
RecieveCallBack, state);
}
}
else
{
if ((handler.Connected == false) || (handler.Available == 0))
{
Close(state);
}
}
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, state, "RecieveCallBack"));
}
}
public void Send(StateObject state, byte[] data)
{
try
{
var handler = state.WorkSocket;
handler.BeginSend(data, 0, data.Length, 0,
SendCallback, state);
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, state, "Send"));
}
}
private void SendCallback(IAsyncResult ar)
{
var state = (StateObject)ar.AsyncState;
try
{
Thread.CurrentThread.CurrentUICulture = state.CurrentUICulture;
var handler = state.WorkSocket;
handler.EndSend(ar);
handler.BeginReceive(state.Buffer, 0, state.InputBufferSize, 0,
RecieveCallBack, state);
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, state, "SendCallback"));
}
}
public void Close(StateObject state)
{
try
{
if (state == null)
return;
var handler = state.WorkSocket;
if (handler == null)
return;
if (!handler.Connected)
return;
if (handler.Available > 0)
{
var data = new byte[handler.Available];
handler.Receive(data);
}
handler.Shutdown(SocketShutdown.Both);
handler.Close(0);
if (OnDisconnected != null)
OnDisconnected(this, new ServerEventArgs(state));
StateObject removed;
var removeResult = Connections.TryRemove(state.Guid, out removed);
}
catch (Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, "Close"));
}
}
private void CloseAllConnections()
{
try
{
var connections = Connections.Select(c => c.Value);
foreach(var connection in connections)
{
Close(connection);
}
}
catch(Exception exc)
{
Debug.Assert(false, exc.Message);
if (OnException != null)
OnException(this, new ExceptionEventArgs(exc, "CloseAllConnections"));
}
}
public override string ToString()
{
return string.Format("{0}:{1}", IPAddress, Port);
}
}
由于这是一项服务,通常的 "wait for the non-background threads to exit" 规则不适用,您的工作是杀死工作人员(这可能包括挂起的异步操作)。现在;目前,您正在杀死 listener,但这只会阻止 new 套接字连接。理想情况下,您应该在某个地方跟踪您的客户,这样您也可以杀死所有 client 套接字。当您完成 那个 后,您应该没问题。请记住同步访问任何客户端集合,或使用线程安全的集合类型。
我认为您应该在关闭套接字之前使用 Socket.Shutdown 方法。正如文档中所述:
When using a connection-oriented Socket, always call the Shutdown method before closing the Socket. This ensures that all data is sent and received on the connected socket before it is closed.
此外,也许我错了,但我看不到对 StopListening() 方法的任何调用。此后 _iAmListening 字段未设置为 false,并且回调 (AcceptCallback(IAsyncResult ar)) 仍在响应来电。
所以,问题终于解决了,我的Windows服务没有任何问题!
这是由 Microsoft Windows 更新引起的:
Microsoft KB4338815,它导致 closesocket 在 Intel Xeon 处理器上永远挂起:https://forum.filezilla-project.org/viewtopic.php?t=49308
感谢您为帮助我所做的一切努力!