У меня есть служба Windows сервера TCP, разработанная в.net 4.0 с асинхронными серверными сокетами.
Он работает, но примерно в 90% случаев я просто не могу его остановить: после нажатия кнопки останова в консоли Windows Service он зависает и останавливается примерно через минуту, но его процесс продолжается, и связь по протоколу 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);
}
}