C# async SocketException while binding - c#

I am working on a client-server application on C# using async sockets. As I listen for connections, I keep getting this error
An unhandled exception of type 'System.Net.Sockets.SocketException'
occurred in System.dll
Additional information: Only one usage of each socket address (protocol/network address/port) is normally permitted
This is the line where this exception happens:
_listener.Bind(new IPEndPoint(0, port));
The code is under Listener.Start(int port). In the main program, here's how I call the method:
void btnListen_Click(object sender, EventArgs e)
{
_listener = new Listener();
_listener.Accepted += new Listener.SocketAcceptedHandler(listener_Accepted);
_listener.Start(8192);
}
I am testing both the client and server applications in my PC.
Here's the Listener class.
namespace serverPC
{
class Listener
{
public delegate void SocketAcceptedHandler(Socket e);
public event SocketAcceptedHandler Accepted;
Socket _listener;
public int Port;
public bool Running
{
get;
private set;
}
public Listener() {Port = 0;}
public void Start(int port)
{
if (Running)
return;
_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_listener.Bind(new IPEndPoint(0, port)); // This is where the SocketException error occurs
_listener.Listen(0);
_listener.BeginAccept(acceptedCallback, null);
Running = true;
}
public void Stop()
{
if (!Running)
return;
_listener.Close();
Running = false;
}
void acceptedCallback(IAsyncResult ar)
{
try
{
Socket s = _listener.EndAccept(ar);
if (Accepted != null)
{
Accepted(s);
}
}
catch
{
}
if (Running)
{
try
{
_listener.BeginAccept(acceptedCallback, null);
}
catch
{
}
}
}
}
}
Here's the Client class.
namespace serverPC
{
struct ReceiveBuffer
{
public const int BUFFER_SIZE = 1024;
public byte[] Buffer;
public int ToReceive;
public MemoryStream BufStream;
public ReceiveBuffer(int toRec)
{
Buffer = new byte[BUFFER_SIZE];
ToReceive = toRec;
BufStream = new MemoryStream(toRec);
}
public void Dispose()
{
Buffer = null;
ToReceive = 0;
Close();
if (BufStream != null)
BufStream.Dispose();
}
public void Close()
{
if (BufStream != null && BufStream.CanWrite)
{
BufStream.Close();
}
}
}
class Client
{
byte[] lenBuffer;
ReceiveBuffer buffer;
Socket _socket;
public IPEndPoint EndPoint
{
get
{
if (_socket != null && _socket.Connected)
{
return (IPEndPoint)_socket.RemoteEndPoint;
}
return new IPEndPoint(IPAddress.None, 0);
}
}
public delegate void DisconnectedEventHandler(Client sender);
public event DisconnectedEventHandler Disconnected;
public delegate void DataReceivedEventHandler(Client sender, ReceiveBuffer e);
public event DataReceivedEventHandler DataReceived;
public Client(Socket s)
{
_socket = s;
lenBuffer = new byte[4];
}
public void Close()
{
if (_socket != null)
{
_socket.Disconnect(false);
_socket.Close();
}
buffer.Dispose();
_socket = null;
lenBuffer = null;
Disconnected = null;
DataReceived = null;
}
public void ReceiveAsync()
{
_socket.BeginReceive(lenBuffer, 0, lenBuffer.Length, SocketFlags.None, receiveCallback, null);
}
void receiveCallback(IAsyncResult ar)
{
try
{
int rec = _socket.EndReceive(ar);
if (rec == 0)
{
if (Disconnected != null)
{
Disconnected(this);
return;
}
if (rec != 4)
{
throw new Exception();
}
}
}
catch (SocketException se)
{
switch (se.SocketErrorCode)
{
case SocketError.ConnectionAborted:
case SocketError.ConnectionReset:
if (Disconnected != null)
{
Disconnected(this);
return;
}
break;
}
}
catch (ObjectDisposedException)
{
return;
}
catch (NullReferenceException)
{
return;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return;
}
buffer = new ReceiveBuffer(BitConverter.ToInt32(lenBuffer, 0));
_socket.BeginReceive(buffer.Buffer, 0, buffer.Buffer.Length, SocketFlags.None, receivePacketCallback, null);
}
void receivePacketCallback(IAsyncResult ar)
{
int rec = _socket.EndReceive(ar);
if (rec <= 0)
{
return;
}
buffer.BufStream.Write(buffer.Buffer, 0, rec);
buffer.ToReceive -= rec;
if(buffer.ToReceive > 0)
{
Array.Clear(buffer.Buffer, 0, buffer.Buffer.Length);
_socket.BeginReceive(buffer.Buffer, 0, buffer.Buffer.Length, SocketFlags.None, receivePacketCallback, null);
return;
}
if (DataReceived != null)
{
buffer.BufStream.Position = 0;
DataReceived(this, buffer);
}
buffer.Dispose();
ReceiveAsync();
}
}
}
Why do I get the SocketException error? Any help is appreciated. Thanks!

So the obvious things to check - Something is already listening on port 8192 or there's some other reason why your machine would reject your program opening that port (firewalls, etc).
The other problem is while you are blocking binding twice with the "Running" check, you're only doing that for the instance of listener - hitting the button will create a whole new listener and attempt to have it listen on port 8192 - which since you already have a listener bound there, will fail with that exception.

Related

TCP socket timeout

I wrote C# program that server sends data from Server to Client only.
Problem is that time between it sends data is changable ( max 5min)
and that causes sometimes connection to timeout.
When I send data every 3sec then none of them timeouts.
But if message send after 5min then there is problem on Client to recieve it.
I made timeout feature that both Client and Server has. After timeout every reconnects:
public class TimerControl
{
private System.Timers.Timer timeoutTimer = null;
public void initTimeout(int timeMS, System.Timers.ElapsedEventHandler funct)
{
timeoutTimer = new System.Timers.Timer();
timeoutTimer.Interval = timeMS; //MS
timeoutTimer.Elapsed += funct;
timeoutTimer.AutoReset = true;
setTimeoutTimer(false);
}
public void setTimeoutTimer(bool state)
{
if (timeoutTimer != null)
{
timeoutTimer.Stop();
timeoutTimer.Enabled = state;
if (state) timeoutTimer.Start();
}
}
public void resetTimeoutTimer()
{
if (timeoutTimer != null && timeoutTimer.Enabled)
{
timeoutTimer.Stop();
timeoutTimer.Start();
}
}
}
that has not solved the trouble.
What should I do to make it work correct and not timeout after some time?
Server:
public class TCPserver :TCPunit
{
private int TIMEOUT_MS = 5000;
Socket serverListener = null;
Queue<string> dataQueued = null;
bool isConnectedForced = false;
public TCPserver()
{
dataQueued = new Queue<string>();
initTimeout(TIMEOUT_MS, reconnect);
}
public void sendDataToClient(string message)
{
dataQueued.Enqueue(message + Environment.NewLine);
if(isConnectedForced) startListening();
if (dataQueued.Count > 0) setTimeoutTimer(true);
}
public bool connect(string adress)
{
this.thisUnitAdress = adress;
isConnectedForced = true;
loopedConnect();
startListening();
return true;
}
public bool disconnect()
{
isConnectedForced = false;
loopedDisconnect();
return true;
}
private bool loopedConnect()
{
try
{
IPAddress ipAddress = IPAddress.Parse(this.thisUnitAdress);
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
if (serverListener != null) loopedDisconnect();
serverListener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
serverListener.Bind(localEndPoint);
Console.WriteLine("SERVER connected to: " + this.thisUnitAdress + " port : " + port.ToString());
return true;
}
catch (Exception ex)
{
Console.WriteLine("!!! SERVER connect");
setTimeoutTimer(true);
return false;
}
}
private bool loopedDisconnect()
{
setTimeoutTimer(false);
if (serverListener != null)
{
if (serverListener.Connected) serverListener.Shutdown(SocketShutdown.Both);
serverListener.Close();
Console.WriteLine("SERVER CLOSED!");
serverListener = null;
}
return true;
}
private void reconnect(Object source, System.Timers.ElapsedEventArgs e)
{
if (isConnectedForced)
{
Console.WriteLine("SERVER RECONNECT!!!");
loopedDisconnect();
loopedConnect();
if (dataQueued.Count > 0) setTimeoutTimer(true);
else setTimeoutTimer(false);
}
else
{
setTimeoutTimer(false);
}
}
private void startListening()
{
try
{
serverListener.Listen(100);
Console.WriteLine("SERVER Waiting for a connection...");
serverListener.BeginAccept(new AsyncCallback(AcceptCallback), serverListener);
setTimeoutTimer(true);
}
catch (Exception ex)
{
Console.WriteLine("!!! SERVER sendingLOOP");
setTimeoutTimer(true);
}
}
private void AcceptCallback(IAsyncResult ar)
{
try
{
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
//HERE SEND
while (dataQueued.Count > 0)
{
string data = dataQueued.Dequeue();
byte[] byteData = Encoding.ASCII.GetBytes(data);
handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler);
}
//handler.Shutdown(SocketShutdown.Both);
//handler.Close();
setTimeoutTimer(false);
}
catch (Exception ex)
{
Console.WriteLine("!!! SERVER AcceptCallback");
setTimeoutTimer(true);
}
}
private void SendCallback(IAsyncResult ar)
{
try
{
((Socket)ar.AsyncState).EndSend(ar);
}
catch(Exception ex)
{
Console.WriteLine("!!! SERVER SendCallback");
setTimeoutTimer(true);
}
}
}
Client:
public class TCPclient : TCPunit
{
private int TIMEOUT_MS = 5000;
Socket client;
IPEndPoint remoteEP;
bool isConnecting = false;
bool isRecieving = false; // TELS IF PROGRAM SHOULD LOOK FOR SERVER ALL TIME
Action<string> afterRecieveAction = null ; // To print to GUI
public TCPclient()
{
initTimeout(TIMEOUT_MS, reconnect);
}
public void assignAfterRecieveAction(Action<string> action)
{
this.afterRecieveAction = action;
}
public bool connect(string adress)
{
thisUnitAdress = adress;
loopedConnect();
return true;
}
public bool disconnect()
{
isRecieving = false;
isConnecting = false;
loopedDisconnect();
return true;
}
private bool loopedConnect()
{
IPAddress ipAddress = IPAddress.Parse(this.thisUnitAdress);
remoteEP = new IPEndPoint(ipAddress, port);
client = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
setTimeoutTimer(true);
isRecieving = true;
StartClientListening();
return true;
}
private bool loopedDisconnect()
{
if (client != null)
{
if (client.Connected) client.Shutdown(SocketShutdown.Both);
client.Close();
Console.WriteLine("CLIENT CLOSED!");
client = null;
}
return true;
}
private void reconnect(Object source, System.Timers.ElapsedEventArgs e)
{
if (isRecieving)
{
Console.WriteLine("CLIENT RECONNECT!!!");
if (isConnecting) loopedDisconnect();
isRecieving = true;
loopedConnect();
}
}
private void StartClientListening()
{
try
{
if (isRecieving)
{
client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback) , client);
isConnecting = true;
Console.WriteLine("CLIENT listens to: " + thisUnitAdress + " port : " + port.ToString());
}
}
catch (System.Net.Sockets.SocketException ex)
{
Console.WriteLine("CLIENT StartClientListening");
}
catch (Exception ex)
{
Console.WriteLine("!!! CLIENT StartClientListening2");
if (isRecieving) setTimeoutTimer(true);
}
}
private void ConnectCallback(IAsyncResult ar)
{
try
{
client.EndConnect(ar);
Console.WriteLine("CLIENT connected to {0}", client.RemoteEndPoint.ToString());
StateObject state = new StateObject();
state.workSocket = client;
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state); ;
}
catch (Exception e)
{
Console.WriteLine("!!! CLIENT ConnectCallback");
if (isRecieving) setTimeoutTimer(true);
}
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
String response = Encoding.ASCII.GetString(state.buffer);
if (afterRecieveAction != null) afterRecieveAction(response);
resetTimeoutTimer();
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
}
catch(System.Net.Sockets.SocketException ex)
{
Console.WriteLine("!!! CLIENT ReceiveCallback");
if (isRecieving) setTimeoutTimer(true);
}
catch(Exception ex)
{
Console.WriteLine("!!! CLIENT ReceiveCallback2");
if (isRecieving) setTimeoutTimer(true);
}
}
}
How to make async Server-Client to work without timeouts?
Best regards,
Chris
You should use socket_set_option to parameter this

Client disconnected, but server don't see it

so i was making a Tcp Server, when i got a problem while a client is disconnecting. Exception is being throwed, but it looks like that server don't see it. And it don't remove the disconnected client from list. Here is code:
public void Read(IAsyncResult ar, TcpClient PL)
{
try
{
{
BytesRead[PL] = PL.GetStream().EndRead(ar);
}
if (BytesRead[PL] < 1)
{
throw new SocketException();
}
else
{
while (true)
{
packets++; break;
}
}
}
catch (Exception x)
{
if ((x.Message.Contains("Client") && x.Message.Contains("Disconnected")) || x is SocketException || x is EndOfStreamException || x is IOException) //|| x.InnerException.GetType() == typeof(IOException))
{
throw;
}
else
{
System.Console.WriteLine(x.ToString());
}
}
}
It occurs when client closes program or when it's killed by task manager
Here is where method Read has been used.
private void HandleClient(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream ns = tcpClient.GetStream();
while (true)
{
try
{
while ((true))
{
{
try
{
}
catch (SocketException ex)
{
if ((ex.Message.Contains("Client") && ex.Message.Contains("Disconnected")) || ex is SocketException)
{
throw;
}
}
try
{
{
}
}
catch (Exception ex)
{
}
}
}
}
catch (SocketException ex)
{
if (((ex.Message.Contains("Client") && ex.Message.Contains("Disconnected"))) || ex is SocketException )
{
clients.Remove(tcpClient);
}
else
{
System.Console.WriteLine(ex.ToString());
}
}
}
}
Here is the TcpListener Initialization
public void Server1()
{
timer = new Timer(new TimerCallback(MultipleCheck), null, 0, 500);
listener = new TcpListener(IPAddress.Parse("25.75.22.56"), 29339);
listenThread = new Thread(new ThreadStart(ListenForClients));
listenThread.Start();
}
And here is method where it accepts the TcpClient
private void ListenForClients()
{
listener.Start();
while (true)
{
System.Console.Title = "Server : " + clients.Count.ToString();
if (!LineStarted)
{
System.Console.WriteLine("Server started on port 29339");
LineStarted = true;
}
System.Console.WriteLine("Connection Request");
TcpClient client = listener.AcceptTcpClient();
if (client.Connected)
{
clients.Add(client);
System.Console.WriteLine("New Client Connected");
//if (threads.ContainsKey(client))
{
threads[client].Start(client);
}
counter++;
}
}
}
Before the threads[tcpClient].Abort(); put try instruction
try
{
//Removing Client from list and notifying connected users about disconnected
//client from list
}
catch
{
}
//And after Abort the thread to avoid the exception of thread aborting being
//thrown too early
threads[tcpClient].Abort();
threads.Remove(tcpClient);

Socket disconnections and gracefully reconnect C#

I am building a tcp port forwarding application. The client connects to the server on a particular port and internally the request is routed in the server to a remote port and response is passed back to the client. Below is my code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace BBGRouter
{
class Router
{
#region log4net
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
#endregion //log4net
bool isShutdown = false;
private Socket router = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public event EventHandler OnRestartNeeded;
//only one time
Thread thRouterTh;
public Router()
{
thRouterTh = new Thread(new ParameterizedThreadStart(RouterProc));
}
IPEndPoint local, remote;
public void Start(IPEndPoint local, IPEndPoint remote)
{
if (log.IsInfoEnabled) { log.Info("Listening on " + local); }
this.local = local;
this.remote = remote;
router.Bind(local);
router.Listen(10);
thRouterTh.Name = "router thread";
thRouterTh.Start();
}
void RouterProc(object obj)
{
while (!isShutdown)
{
try
{
var source = router.Accept();
if (log.IsInfoEnabled) { log.Info("Creating new session...."); }
var destination = new Router();
var state = new State(source, destination.router);
destination.Connect(remote, source);
source.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
catch (Exception ex)
{
if (log.IsErrorEnabled) { log.Error("Exception in Router thread", ex); }
if (isShutdown) { if (log.IsInfoEnabled) { log.Info("Shutting down..."); } }
}
}
}
public void Join()
{
if (thRouterTh != null)
thRouterTh.Join();
}
private void StopAndFireRestart(EventArgs e)
{
Stop();
log.Info("Stopped router");
EventHandler handler = OnRestartNeeded;
if (handler != null)
{
log.Info("Firing the restart event now");
handler(this, e);
}
}
public void Stop()
{
try
{
isShutdown = true;
if (log.IsInfoEnabled) { log.Info("Stopping router thread"); }
router.Shutdown(SocketShutdown.Both);
//router.Shutdown(SocketShutdown.Receive);
}
catch (Exception ex)
{
if (log.IsErrorEnabled) { log.Error("Exception while stopping", ex); }
}
finally
{
thRouterTh.Interrupt();
router.Dispose();
}
}
private void Connect(EndPoint remoteEndpoint, Socket destination)
{
if (log.IsInfoEnabled) { log.InfoFormat("connecting session at {0}", remoteEndpoint.ToString()); }
var state = new State(router, destination);
try
{
router.Connect(remoteEndpoint);
router.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, OnDataReceive, state);
}
catch (SocketException e)
{
if (log.IsErrorEnabled) { log.Error(string.Format("SocketException while connect: Exception: {0}, ErrorCode: {1}", e, e.ErrorCode)); }
// Stop the service
StopAndFireRestart(new EventArgs());
}
catch (Exception ex)
{
if (log.IsErrorEnabled) { log.Error("exception while connect {0}", ex); }
}
}
private void OnDataReceive(IAsyncResult result)
{
var state = (State)result.AsyncState;
try
{
var bytesRead = state.SourceSocket.EndReceive(result);
if (bytesRead > 0)
{
log.Info(string.Format("Bytes read: {0}", bytesRead));
state.DestinationSocket.Send(state.Buffer, bytesRead, SocketFlags.None);
state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
catch (Exception e)
{
if (log.IsErrorEnabled) { log.Error("Exception receiving the data, Closing source/destination sockets", e); }
//state.DestinationSocket.Close();
//state.SourceSocket.Close();
//StopAndFireRestart(new EventArgs());
}
}
private class State
{
public Socket SourceSocket { get; private set; }
public Socket DestinationSocket { get; private set; }
public byte[] Buffer { get; private set; }
public State(Socket source, Socket destination)
{
SourceSocket = source;
DestinationSocket = destination;
Buffer = new byte[8192];
}
}
}
}
The problem happens when suddenly the client gets disconnected from the remote port randomly after 4-5 hrs. How can I reconnect the client gracefully so that there is no change in the client code ?
Why don't you solve this issue with netsh?
netsh interface portproxy add v4tov4 listenport=LOCALPORT listenaddress=localhost connectport=REMOTEPORT connectaddress=REMOTEADDRESS
or
netsh interface portproxy add v4tov4 listenport=LOCALPORT connectport=REMOTEPORT connectaddress=REMOTEADDRESS
More information on netsh available on MSDN.

Multiple TcpListeners not working

I made an "Acceptor" class which in the cunstroctor it accepts an amount of TcpListeners. Starting from port 8484 and above.
class Acceptor
{
private List<TcpListener> Listeners;
private static int clientCount = 0;
private static int portStart = 8484;
public Acceptor(int capacity)
{
Listeners = new List<TcpListener>(capacity);
for (int i = 0; i < capacity; i++)
{
Listeners.Add(new TcpListener(IPAddress.Any, portStart));
portStart++;
}
foreach (TcpListener listener in Listeners)
{
try
{
listener.Start();
listener.BeginAcceptSocket(new AsyncCallback(EndAccept), null);
}
catch (SocketException ex)
{
Debug.WriteLine("Failed to start TcpListener, Error {0}.", ex.Message);
}
}
Debug.WriteLine(string.Format("Initiated {0} Listeners from 8484 - {1}.", capacity, portStart));
}
public void EndAccept(IAsyncResult IAR)
{
TcpListener Listener = (TcpListener)IAR.AsyncState;
Socket socket = Listener.EndAcceptSocket(IAR);
frmMain.Clients.Add(clientCount, new Client(socket));
Listener.Stop();
Listener = null;
clientCount++;
frmMain.Instance.UpdateClients();
}
}
However, that doesn't work. The program crashes when accepting a new connection, why's that?
You're passing null as the "state" in BeginAcceptSocket, thus the error when you attempt to cast it back and then use it.
Try changing this line:
listener.BeginAcceptSocket(new AsyncCallback(EndAccept), null);
To:
listener.BeginAcceptSocket(new AsyncCallback(EndAccept), listener);
This is how i implement a Listener:
public class EasySocketListener : IDisposable
{
private Socket _socket;
public void Start(int port)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Bind(new IPEndPoint(IPAddress.Any, port));
_socket.Listen(1);
StartAccepting();
}
private void StartAccepting()
{
try
{
_socket.BeginAccept((asyncResult) =>
{
try
{
Socket clientSocket = _socket.EndAccept(asyncResult);
if (OnSocketAccept != null)
OnSocketAccept(this, new SocketEventArgs(clientSocket));
StartAccepting();
}
catch { }
}, null);
}
catch { }
}
public void Dispose()
{
if (_socket != null)
{
_socket.Dispose();
_socket = null;
}
}
public event EventHandler<SocketEventArgs> OnSocketAccept;
}
This can accept multiple client sockets. When a client connects, the OnSocketAccept triggers.
Needs the SocketEventArgs:
public class SocketEventArgs : EventArgs
{
public Socket Socket { get; private set; }
public SocketEventArgs(Socket socket)
{
Socket = socket;
}
}
example:
private void Init()
{
_listener = new EasySocketListener();
_listener.OnSocketAccept += Listener_OnSocketAccept;
_listener.Start(port);
}
private void Listener_OnSocketAccept(object sender, SocketEventArgs e)
{
Debug.WriteLine( e.Socket.RemoteEndPoint );
}
This will come on my blog http://csharp.vanlangen.biz but haven't wrote it there yet. You can find some methods for reading asynchronous from a socket.

Asynchronous socket, forced disconnection and reuse

Im writing an application which is going to act as a tcp listener for a single client. The client is a java applet and will periodically connect to the listener, send some data and then wait for a response.
The code for the TcpServer class below has been largely lifted from an example provided by a more knowledgeable stackoverflow member in response to a different question.
Everything was going great until I found something in testing which wasnt mentioned in any of the interface documents I have. After the server has responded to the client it must then disconnect the client and start listening again.
My first thought was to call Disconnect() from inside SendData() but this results in a call to ReceiveCompleted() from somewhere and a nasty exception about the socket already being disposed.
Is this requirement easily achievable with the code design I have, and will I run into any problems in reusing the socket for subsequent connections?
sealed class TcpServer : IDisposable
{
#region Fields
private const int SocketBufferSize = 1024;
private readonly TcpListener tcpListener;
private Socket connectedSocket;
private bool disposed = false;
#endregion Fields
#region Constructors
public TcpServer(int port)
{
tcpListener = new TcpListener(IPAddress.Any, port);
tcpListener.Start();
tcpListener.BeginAcceptSocket(EndAcceptSocket, tcpListener);
}
~TcpServer()
{
Dispose(false);
}
#endregion Constructors
#region Events
public event EventHandler<DataReceivedEventArgs> DataReceived;
public event EventHandler<IPEndPointEventArgs> SocketConnected;
public event EventHandler<IPEndPointEventArgs> SocketDisconnected;
#endregion Events
#region Methods
public void Dispose()
{
Dispose(true);
}
public void SendData(byte[] data)
{
if (connectedSocket == null)
{
return;
}
connectedSocket.Send(data);
}
private void BeginReceiveAsync(Socket sock, SocketAsyncEventArgs e)
{
if (!sock.ReceiveAsync(e))
{
ReceiveCompleted(sock, e);
}
}
private void Connected(Socket socket)
{
var endPoint = (IPEndPoint)socket.RemoteEndPoint;
connectedSocket = socket;
OnSocketConnected(endPoint);
}
private void Disconnect(Socket socket)
{
var endPoint = (IPEndPoint)socket.RemoteEndPoint;
socket.Close();
connectedSocket = null;
OnSocketDisconnected(endPoint);
tcpListener.BeginAcceptSocket(EndAcceptSocket, tcpListener);
}
private void Dispose(bool disposing)
{
if (this.disposed == false)
{
if (disposing)
{
try
{
if (tcpListener != null)
{
this.disposed = true;
tcpListener.Stop();
}
}
catch (Exception ex)
{
TraceLog.Error("TcpServer: tcpListener.Stop(): {0}", ex.Message);
}
try
{
if (connectedSocket != null)
{
connectedSocket.Close();
connectedSocket = null;
}
}
catch (SocketException ex)
{
TraceLog.Error("TcpServer: connectedSocket.Close(): {0}", ex);
}
}
this.disposed = true;
}
}
private void EndAcceptSocket(IAsyncResult asyncResult)
{
var listener = (TcpListener)asyncResult.AsyncState;
if (disposed)
{
return;
}
try
{
Socket sock = listener.EndAcceptSocket(asyncResult);
Connected(sock);
var e = new SocketAsyncEventArgs();
e.Completed += ReceiveCompleted;
e.SetBuffer(new byte[SocketBufferSize], 0, SocketBufferSize);
BeginReceiveAsync(sock, e);
}
catch (SocketException ex)
{
TraceLog.Error("TcpServer.EndAcceptSocket: {0}", ex.Message);
}
catch (Exception ex)
{
TraceLog.Error("TcpServer.EndAcceptSocket: {0}", ex.Message);
}
}
private void OnDataReceived(byte[] data, IPEndPoint ipEndPoint)
{
if (DataReceived != null)
{
DataReceived(this, new DataReceivedEventArgs(data, ipEndPoint));
}
}
private void OnSocketConnected(IPEndPoint ipEndPoint)
{
if (SocketConnected != null)
{
SocketConnected(this, new IPEndPointEventArgs(ipEndPoint));
}
}
private void OnSocketDisconnected(IPEndPoint ipEndPoint)
{
if (SocketDisconnected != null)
{
SocketDisconnected(this, new IPEndPointEventArgs(ipEndPoint));
}
}
private void ReceiveCompleted(object sender, SocketAsyncEventArgs e)
{
var sock = (Socket)sender;
if (!sock.Connected)
{
Disconnect(sock);
}
try
{
int size = e.BytesTransferred;
if (size == 0)
{
Disconnect(sock);
}
else
{
var buf = new byte[size];
Array.Copy(e.Buffer, buf, size);
ReceiveData(buf, (IPEndPoint)sock.RemoteEndPoint);
BeginReceiveAsync(sock, e);
}
}
catch (SocketException ex)
{
TraceLog.Error("TcpServer: ReceiveCompleted: {0}", ex.Message);
}
catch (Exception ex)
{
TraceLog.Error("TcpServer: ReceiveCompleted: {0}", ex.Message);
}
}
private void ReceiveData(byte[] data, IPEndPoint endPoint)
{
OnDataReceived(data, endPoint);
}
#endregion Methods
}
Whenever I'm writing code that wraps around System.Net.Sockets.Socket, I find myself constantly adding try/catch clauses for SocketException and ObjectDisposedException. In most cases, ObjectDisposedException can simply be ignored, as it, in 99% of cases, indicates that the client has simply disconnected.
At least that's my impression of how the Socket API in .NET works. Try adding some exception handlers here and there, and see how it goes. In any case, your Disconnect method should not do more than something like this:
public void Disconnect()
{
try
{
connectedSocket.Shutdown(SocketShutdown.Both);
}
catch (Exception)
{
// Ignore the exception. The client probably already disconnected.
}
connectedSocket.Dispose(); // This is safe; a double dispose will simply be ignored.
}
I hope that sheds some light on the issue...

Categories

Resources