C# Server Socket stops accepting new connections - c#

I’ve written TCP socket catcher based on server socket and events for transferring caught connections to high level.
public class TcpCatcher
{
private static readonly Exception IsStartedException = new Exception("Catcher has already been started.");
private int _port;
private int _openTimeout;
private Socket _serverSocket;
private SocketAsyncEventArgs _acceptAsyncArg;
public int Port
{
get => _port;
set
{
if (IsStarted)
throw IsStartedException;
_port = value;
}
}
public int OpenTimeout
{
get => _openTimeout;
set
{
if (IsStarted)
throw IsStartedException;
_openTimeout = value;
}
}
public bool IsStarted { get; protected set; }
public event Action<Socket> ConnectionCatched;
public event Action<string> Log;
public TcpCatcher()
{
_acceptAsyncArg = new SocketAsyncEventArgs();
_acceptAsyncArg.SetBuffer(null, 0, 0);
_acceptAsyncArg.Completed += OnAcceptSocket;
}
private void OnLog(string message)
{
if (Log != null)
Log(message);
}
private void AcceptSocket()
{
OnLog("AcceptSocket is executing");
bool isAsync;
do
{
isAsync = _serverSocket.AcceptAsync(_acceptAsyncArg);
if (!isAsync)
{
OnLog($"AcceptAsync finished Sync.");
if (_acceptAsyncArg.SocketError == SocketError.Success)
ConnectionCatched(_acceptAsyncArg.AcceptSocket);
}
} while (!isAsync);
OnLog("AcceptSocket finished");
}
private void OnAcceptSocket(object sender, SocketAsyncEventArgs asyncArg)
{
OnLog("Caught new socket!");
if (asyncArg.SocketError != SocketError.Success)
{
OnLog($"Socket has bad status:${asyncArg.SocketError}");
asyncArg.AcceptSocket?.Close();
}
else
{
ConnectionCatched(asyncArg.AcceptSocket);
}
OnLog($"Caught socket was processed");
asyncArg.AcceptSocket = null;
if (IsStarted)
AcceptSocket();
}
public void Start()
{
OnLog($"Starting");
IsStarted = true;
_serverSocket?.Close();
_serverSocket = new Socket(SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
_serverSocket.Bind(new IPEndPoint(IPAddress.Any, Port));
_serverSocket.Listen((int)SocketOptionName.MaxConnections);
AcceptSocket();
}
public void Finish()
{
OnLog($"Stopping");
_serverSocket.Disconnect(false);
_serverSocket.Close();
_serverSocket.Dispose();
_serverSocket = null;
IsStarted = false;
}
}
Catchers are used in a Windows Service and initialized when the service is started. They work about 2-4 days and stop for unknown reasons accepting sockets one by one. Can you explain why it may be and how this problem can be solved?

Related

c# Socket.AcceptAsync where is Socket.StopAcceptAsync?

There is AcceptAsync for Socket.
Code looks like this:
private Socket _listeningSocket;
private SocketAsyncEventArgs _socketEvent;
private Init()
{
_socketEvent = new SocketAsyncEventArgs();
_listeningSocket = ...; // Set up ip, port etc.
_listeningSocket.Bind(...);
_listeningSocket.Listen(...);
}
public void StartAccept()
{
if (_socketEvent.AcceptSocket is not null)
{
_socketEvent.AcceptSocket = null;
}
if (!_listeningSocket.AcceptAsync(_socketEvent))
{
ProcessAccept(_socketEvent);
}
}
public void StopAccept()
{
// _listeningSocket.Stop or what ?
}
private void ProcessAccept(SocketAsyncEventArgs socketAsyncEvent)
{
if (socketAsyncEvent.SocketError == SocketError.Success)
{
OnClientAccepted?.Invoke(this, socketAsyncEvent);
StartAccept();
}
}
How can I stop accepting and start again? I would love to have something like StopAcceptAsync... Or could you point me some example how to stop accepting with SocketAsyncEventArgs ?

can not connect to server after some time

Client :
public class TCPClientWrapper : IDisposable
{
private TcpClient tcpClient;
private readonly string address;
private readonly int port;
public TCPClientWrapper(string address, int port)
{
tcpClient = new TcpClient();
this.address = address;
this.port = port;
}
private void TryConnect()
{
tcpClient = new TcpClient();
tcpClient.SendTimeout = 15;
bool isConnected = false;
while (true)
{
try
{
Log.Info("TcpClient, Trying Connect");
tcpClient.Connect(IPAddress.Parse(address), port);
if (SocketConnected(tcpClient.Client))
{
Log.Info("TcpClient, Connected");
isConnected = true;
break;
}
}
catch (Exception e)
{
Log.Info("TcpClient, connection failed. Try to reconnect after 30 seconds, {0}", e.Message);
}
finally
{
if (!isConnected)
Thread.Sleep(30000);
}
}
}
public void SendMessage(string msg)
{
if (!SocketConnected(tcpClient.Client))
{
TryConnect();
}
byte[] buffer = Encoding.UTF8.GetBytes(msg);
tcpClient.Client.Send(buffer);
}
private bool SocketConnected(Socket s)
{
if (!s.Connected)
return false;
bool part1 = s.Poll(1000, SelectMode.SelectRead);
bool part2 = s.Available == 0;
return !(part1 && part2);
}
public void Dispose()
{
tcpClient.Close();
}
}
Server running as windows service:
public class TcpServer
{
private bool started;
private bool stopped;
private TcpListener tcpListener;
private static ManualResetEvent allDone = new ManualResetEvent(false);
public TcpServer(string url, int port)
{
tcpListener = new TcpListener(IPAddress.Parse(url), port);
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void Run()
{
if (started) return;
stopped = false;
tcpListener.Start();
Task.Run(() =>
{
Log.Info("Server running");
while (!stopped)
{
allDone.Reset();
tcpListener.BeginAcceptSocket(AcceptCallback, tcpListener);
Log.Info("Accepting socket");
allDone.WaitOne();
}
});
Log.Info("Ping server started");
started = true;
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void Stop()
{
if (!started) return;
stopped = true;
tcpListener.Stop();
started = false;
Log.Info("Ping server stopped");
}
private void AcceptCallback(IAsyncResult result)
{
try
{
allDone.Set();
if (stopped) return;
Log.Info("Socket accepted");
var listener = (TcpListener)result.AsyncState;
var socket = listener.EndAcceptSocket(result);
Log.Info("Process socket");
ProcessSocket(socket);
}
catch (Exception e)
{
Log.Info("Error accepting callback. {0}", e.Message);
}
}
private void ProcessSocket(Socket socket)
{
try
{
byte[] buffer = new byte[256];
while (!stopped && socket.Receive(buffer) != 0)
{
var msg = Encoding.UTF8.GetString(buffer);
Console.WriteLine(msg);
}
}
catch (Exception e)
{
socket.Close();
Log.Info("Socket closed:{0}", !socket.Connected);
}
}
}
The server is configured in such a way that request to server processed on one IP xxx.xx.xxx.135:5050 and response from server given from xxx.xx.xxx.134:5050
The client works fine for some period of time, but after i get the following error on client side:
A connection attempt failed because the connected party did not
properly respond after a period of time, or established connection
failed because connected host has failed to respond
xxx.xx.xxx.135:5050
What is the reason that the client can't connect to server ?
Check firewall settings on production server
Check whether IP white listing is required
Antivirus might be blocking the request
If no luck, install Advanced REST Client tool on Chrome and manually
test the request
https://forums.asp.net/t/2138734.aspx?A+connection+attempt+failed+because+the+connected+party+did+not+properly+respond+after+a+period+of+time

Is there an advantage/disadvantage to open a different tcp port for every client?

I build a middle ware server.
I have 5(or 6) clients(different computer/IP address), and every client can have up to 6 connection -> max 36 connection to the server.
It is better to use only 1 port for all (e.g. 9000) or one different port for every client(e.g. 9000-9005) ?
I write me a simple test server(see code).
It worked like expected but maybe there is some room for optimization.
Thanks
My TCP server
public class TCPServerMulti : IDisposable
{
private static TcpListener _tcpListener;
private string _ipAdress = "";
private int _port = 9050;
private readonly Thread _listener;
private readonly bool _running;
private int cc = 0; //Test Counter
public string IPAdress
{
get { return _ipAdress; }
set { _ipAdress = value; }
}
public TCPServerMulti(int port)
{
if(port>0)
_port = port;
_running = true;
string sHostName = Dns.GetHostName();
IPAdress = "";
//Only use the first address -> need to change
IPAddress[] localAddress = Dns.GetHostAddresses(sHostName);
foreach (IPAddress ipAddress in localAddress)
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{
_tcpListener = new TcpListener(ipAddress, _port);
IPAdress = ipAddress.ToString();
break;
}
}
if (IPAdress == "")
{
Console.Writeline("TCP Error");
return;
}
_tcpListener.Start();
_listener = new Thread(Listen) { IsBackground = true };
_listener.Start();
}
public TCPServerMulti():this(-1)
{}
//listening for clients
public void Listen()
{
try
{
while(_running)
{
cc++;
//create a own client object for every connection
TCPClientData cd = new TCPClientData(this, ""+cc, _tcpListener.AcceptTcpClient());
cd.StartClient();
if (ClientConnected != null) ClientConnected("Connected: " + cc);
}
_tcpListener.Stop();
}
catch (Exception)
{
}
}
public void Dispose()
{
_listener.Abort();
_tcpListener.Stop();
}
//Handle for Mainthread
internal void ReceiveClient(TCPClientData sender, string msg)
{
if (ReceiveMsg != null)
ReceiveMsg(sender, msg);
}
public delegate void ReceiveEventHandler(TCPClientData sender, string msg);
public event ReceiveEventHandler ReceiveMsg;
public delegate void ConnectedEventHandler(string msg);
public event ConnectedEventHandler ClientConnected;
}
Clientdata object
public class TCPClientData : IDisposable
{
private TCPServerMulti _master;
private byte[] _data;
private NetworkStream _networkStream;
private int _received;
private TcpClient _tcpClient;
private Thread _worker;
private bool _wrunning;
private string _id;
public TCPClientData(TCPServerMulti master,string id,TcpClient client)
{
_data = new byte[1024];
_master = master;
_id = id;
_tcpClient = client;
}
public string ID
{
get { return _id; }
}
private string ByteArrayToString(byte[] arr)
{
UTF8Encoding enc = new UTF8Encoding();
return enc.GetString(arr);
}
private byte[] StringToByteArray(string data)
{
UTF8Encoding enc = new UTF8Encoding();
return enc.GetBytes(data);
}
// Reading Data
private void Worker()
{
try
{
while (_wrunning)
{
if (_networkStream.DataAvailable)
{
_received = _networkStream.Read(_data, 0, _data.Length);
string s = ByteArrayToString(_data);
_master.ReceiveClient(this, s.Substring(0, _received));
}
else
{
Thread.Sleep(20);
}
}
_networkStream.Close();
_tcpClient.Close();
}
catch (Exception)
{ }
}
public void Dispose()
{
_worker.Abort();
_networkStream.Close();
_tcpClient.Close();
}
public void StartClient()
{
_networkStream = _tcpClient.GetStream();
_networkStream.ReadTimeout = 200;
_wrunning = true;
_worker = new Thread(Worker) { IsBackground = true };
_worker.Start();
}
public void StopClient()
{
_wrunning = false;
Thread.Sleep(700);
}
//Sending Data
public void WriteData(string msg)
{
byte[] b = StringToByteArray(msg);
_networkStream.Write(b,0,b.Length);
_networkStream.Flush();
}
}
Main Thread
static void Main(string[] args)
{
//Create Server
TCPServerPlugin.TCPServerMulti server=new TCPServerMulti(12345);
server.ClientConnected += Server_ClientConnected;
server.ReceiveMsg += Server_ReceiveMsg;
while (true)
{
}
}
//Callbacks
private static void Server_ClientConnected(string msg)
{
Console.WriteLine(msg);
}
private static void Server_ReceiveMsg(TCPClientData sender, string msg)
{
Console.WriteLine(string.Format("Sender: {0} -> MSG:{1}",sender.ID,msg));
if (msg == "!!X")
{
sender.Dispose();
sender = null;
}
else if(msg=="DATE")
sender.WriteData(DateTime.Now.ToLongDateString());
else if(msg=="TIME")
sender.WriteData(DateTime.Now.ToShortTimeString());
}

Couldn't establish connection to TcpListener

For the last 2 weeks I've been trying to solve a problem, apparently unsuccessfully.
I have a server, using TcpListener class, and a client, using TcpClient class.
My tests were using 2 computers in my internal home network (level 1 router).
Trying to connect, client-computer got "connection request timeout" exception.
What I've already tried:
Running the client and the server on the same computer - worked
Listening on 0.0.0.0 port 6666 (check with netstat to make sure that the server is listening)
Disable both client and server firewalls
Trying to connect my remote application with telnet (to reject "client code bug" possibilty)
Installing telnet on both computers, and connecting successfully.
Still my server does not seem to get any connection. Client computer is Windows XP and server computer is Windows 7.
Please help, I'm really stuck on this one.
Edit - Server code ->
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Utils
{
public class ClientConnection
{
public ClientConnection(IPHostEntry hostName, TcpClient client)
{
HostName = hostName;
Client = client;
}
public IPHostEntry HostName { get; set; }
public TcpClient Client { get; set; }
}
public class SocketServer : IDisposable
{
private const int DefaultRecieveSize = 1024;
protected const int ReceiveTimeoutBeforeQuiting = 5000;
private readonly Encoding _defaultEncoding = Encoding.ASCII;
private readonly TcpListener _socket;
protected Dictionary<IPAddress, ClientConnection> Clients;
private static object _locker;
private readonly AutoResetEvent _listeningStopEvent;
private readonly Thread _listeningThread;
private readonly AutoResetEvent _acceptNewClientEvent;
public delegate void ClientsListChangedEvent();
public event ClientsListChangedEvent ClientsChangedEvent;
private Timer _refreshClientsTimer;
private static int _updateClientListInterval;
#region Ctors
protected SocketServer(int port)
{
_socket = new TcpListener(IPAddress.Parse("0.0.0.0"),port);
Clients = new Dictionary<IPAddress, ClientConnection>();
_updateClientListInterval = Convert.ToInt32(ConfigurationManager.AppSettings["UpdateClientListInterval"]);
_listeningThread = new Thread(AcceptNewConnections);
_listeningStopEvent = new AutoResetEvent(false);
_acceptNewClientEvent = new AutoResetEvent(false);
_locker = new object();
_socket.Start();
}
#endregion
#region Send methods
protected void SendString(IPAddress client, string data)
{
// implementation here
}
#endregion
#region Receieve methods
protected MemoryStream GetInputDataAsMemoryStream(IPAddress client)
{
// implementation here
}
protected string GetInputDataAsMemoryString(IPAddress client)
{
// implementation here
}
protected string GetInputDataAsMemoryString(IPAddress client, int timeout)
{
// implementation here
}
#endregion
#region Listening methons
public void StopListening()
{
_listeningStopEvent.Set();
// todo : check if works
_refreshClientsTimer.Dispose();
}
public bool StartListening()
{
if (_listeningThread.ThreadState == ThreadState.Unstarted)
{
_listeningThread.Start();
// start refreshing clients list too. update clients list every 10 seconds
_refreshClientsTimer = new Timer(UpdateList, null, 0, _updateClientListInterval * 1000);
return true;
}
return false;
}
private void AddClientCallback(IAsyncResult ar)
{
// get all needed data before entering lock
if (_socket != null)
{
TcpClient client;
try
{
client = _socket.EndAcceptTcpClient(ar);
}
catch (ObjectDisposedException)
{
// probebly server has been closed. exit
return;
}
var clientEndPoint = client.Client.RemoteEndPoint;
var ip = SocketFunctions.ExtractIpFromEndPoint(clientEndPoint);
IPHostEntry hostName;
try
{
hostName = SocketFunctions.ExtractHostnameFromEndPoint(clientEndPoint);
}
catch(SocketException)
{
// error while parsing from ip to host name. Put default
hostName = new IPHostEntry();
}
// check if given ip already connected
if (!Clients.ContainsKey(ip))
{
lock (_locker)
{
Clients.Add(ip, new ClientConnection(hostName, client));
}
// notify that a new clients has been added
ClientsChangedEvent.Invoke();
}
}
// a new client has accepted
_acceptNewClientEvent.Set();
}
private void AcceptNewConnections(object obj)
{
var shouldStop = false;
while (!shouldStop)
{
_socket.BeginAcceptTcpClient(AddClientCallback, null);
AutoResetEvent[] events = { _listeningStopEvent, _acceptNewClientEvent };
var result = WaitHandle.WaitAny(events);
if (result == 0)
{
shouldStop = true;
}
}
}
#endregion
public List<KeyValuePair<string, string>> GetConnectedClients()
{
var connectedClients = new List<KeyValuePair<string, string>>();
lock (_locker)
{
foreach (var client in Clients)
{
// get only connected clients. Don`t remove - next Timer round will do it anyway and I don`t want to lock _clients
if (client.Value.Client.Connected)
{
connectedClients.Add(new KeyValuePair<string, string>(client.Key.ToString(), client.Value.HostName.HostName));
}
}
}
return connectedClients;
}
private static void CheckConnectionStatus(TcpClient client)
{
if (client.Client.Poll(0, SelectMode.SelectRead))
{
var checkConn = new byte[1];
checkConn[0] = 0;
try
{
if (client.Client.Receive(checkConn, SocketFlags.Peek) == 0)
{
throw new IOException();
}
}
catch (SocketException)
{
// for me, both exceptions mean that there was a conncetion error. Throw IOException for this case too
throw new IOException();
}
}
}
public void UpdateList(object obj)
{
// iterate each client and check its state. Remove if not connected
var tempClients = new Dictionary<IPAddress, ClientConnection>(Clients);
var changedOccurred = false;
lock (_locker)
{
foreach (var client in Clients)
{
try
{
CheckConnectionStatus(client.Value.Client);
}
catch (IOException)
{
// client is not connected anymore
tempClients.Remove(client.Key);
changedOccurred = true;
}
}
}
if (changedOccurred)
{
lock (_locker)
{
Clients = tempClients;
}
// notify that a new clients has been added
ClientsChangedEvent.Invoke();
}
}
#region IDisposble
public void Dispose()
{
if (_socket != null)
{
StopListening();
_socket.Stop();
}
}
#endregion
}
}

C# Asynchronous Sockets: Is this thread-safe and correctly done?

I need to implement a TCP client application. The client and the server send messages to each other. I want to make this program scalable enough to handle connections to multiple servers at the same time. It seems like asynchronous sockets is the way to go for this. I'm new to C# so I'm pretty sure I don't know what I'm doing here. I wrote some classes and a simple console program to get started with. Eventually, I want to create a Windows Forms application but I want to start small and simple first. The Client class runs in its own thread. Is this all thread-safe and correctly done? It's a lot of code and I tried to cut out some fat.
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace FastEyeClient
{
class Program
{
static void Main(string[] args)
{
Client client = new Client();
client.ConnectEvent += new ConnectEventHandler(OnConnect);
client.SetLiveStatusEvent += new SetLiveStatusEventHandler(OnSetLiveStatus);
client.Connect("hostname", 1987);
Thread.Sleep(1000);
client.SetLiveStatus("hostname", true);
}
private static void OnConnect(object sender, ConnectEventArgs e)
{
Console.WriteLine(e.Message);
}
private static void OnSetLiveStatus(object sender, SetLiveStatusEventArgs e)
{
Console.WriteLine(e.Message);
}
}
}
Client.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace FastEyeClient
{
public delegate void ConnectEventHandler(object sender, ConnectEventArgs e);
public delegate void SetLiveStatusEventHandler(object sender, SetLiveStatusEventArgs e);
public class Client : IDisposable
{
public event ConnectEventHandler ConnectEvent;
public event SetLiveStatusEventHandler SetLiveStatusEvent;
ServerManager m_Manager;
EventWaitHandle m_WaitHandle;
readonly object m_Locker;
Queue<Event> m_Tasks;
Thread m_Thread;
public Client()
{
m_Manager = new ServerManager(this);
m_WaitHandle = new AutoResetEvent(false);
m_Locker = new object();
m_Tasks = new Queue<Event>();
m_Thread = new Thread(Run);
m_Thread.Start();
}
public void EnqueueTask(Event task)
{
lock (m_Locker)
{
m_Tasks.Enqueue(task);
}
m_WaitHandle.Set();
}
public void Dispose()
{
EnqueueTask(null);
m_Thread.Join();
m_WaitHandle.Close();
}
private void Run()
{
while (true)
{
Event task = null;
lock (m_Locker)
{
if (m_Tasks.Count > 0)
{
task = m_Tasks.Dequeue();
if (task == null)
{
return;
}
}
}
if (task != null)
{
task.DoTask(m_Manager);
}
else
{
m_WaitHandle.WaitOne();
}
}
}
public void Connect(string hostname, int port)
{
EnqueueTask(new ConnectEvent(hostname, port));
}
public void SetLiveStatus(string hostname, bool status)
{
EnqueueTask(new SetLiveEvent(hostname, status));
}
public void OnConnect(bool isConnected, string message)
{
if (ConnectEvent != null)
{
ConnectEvent(this, new ConnectEventArgs(isConnected, message));
}
}
public void OnSetLiveStatus(string hostname, string message)
{
if (SetLiveStatusEvent != null)
{
SetLiveStatusEvent(this, new SetLiveStatusEventArgs(hostname, message));
}
}
}
}
Server.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace FastEyeClient
{
public class Server
{
private ServerManager m_Manager;
private string m_Hostname;
private bool m_IsLive;
private class StateObject
{
public Socket AsyncSocket = null;
public const int BufferSize = 1024;
public byte[] Buffer = new byte[BufferSize];
public StringBuilder Builder = new StringBuilder();
}
public Server(ServerManager manager, Socket socket)
{
try
{
m_Manager = manager;
IPEndPoint endPoint = (IPEndPoint)socket.RemoteEndPoint;
IPAddress ipAddress = endPoint.Address;
IPHostEntry hostEntry = Dns.GetHostEntry(ipAddress);
Hostname = hostEntry.HostName;
IsLive = false;
StateObject state = new StateObject();
state.AsyncSocket = socket;
socket.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
catch (Exception)
{
}
}
public string Hostname
{
get
{
return m_Hostname;
}
set
{
m_Hostname = value;
}
}
public bool IsLive
{
get
{
return m_IsLive;
}
set
{
m_IsLive = value;
}
}
private void ReceiveCallback(IAsyncResult result)
{
try
{
StateObject state = (StateObject)result.AsyncState;
Socket socket = state.AsyncSocket;
int read = socket.EndReceive(result);
if (read > 0)
{
state.Builder.Append(Encoding.ASCII.GetString(state.Buffer, 0, read));
if (state.Builder.Length > 1)
{
string messages = state.Builder.ToString();
ParseMessages(messages);
}
}
StateObject newState = new StateObject();
newState.AsyncSocket = socket;
socket.BeginReceive(newState.Buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), newState);
}
catch (Exception)
{
}
}
private void ParseMessages(string messages)
{
string[] messagesArray = messages.Split('\n');
foreach (string message in messagesArray)
{
string[] tokens = message.Split(',');
if (tokens[0].Contains("#"))
{
ParseServerMessage(tokens);
}
}
}
private void ParseServerMessage(string[] tokens)
{
tokens[0].Remove(0, 1);
if (tokens[0] == "4")
{
bool status;
if (tokens[1] == "0")
{
status = false;
m_Manager.SetLiveStatus(m_Hostname, status);
}
else if (tokens[1] == "1")
{
status = true;
m_Manager.SetLiveStatus(m_Hostname, status);
}
}
}
}
}
ServerManager.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace FastEyeClient
{
public class ServerManager
{
private Client m_Client;
private Dictionary<string, Server> m_Servers;
private object m_Locker;
public ServerManager(Client client)
{
m_Client = client;
m_Servers = new Dictionary<string, Server>();
m_Locker = new object();
}
public void AddServer(string hostname, int port)
{
try
{
IPAddress[] IPs = Dns.GetHostAddresses(hostname);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.BeginConnect(IPs, port, new AsyncCallback(ConnectCallback), socket);
}
catch (Exception)
{
bool isConnected = false;
string message = "Could not connect to server.";
m_Client.OnConnect(isConnected, message);
}
}
private void ConnectCallback(IAsyncResult ar)
{
bool isConnected;
string message;
try
{
Socket socket = (Socket)ar.AsyncState;
socket.EndConnect(ar);
IPEndPoint endPoint = (IPEndPoint)socket.RemoteEndPoint;
IPAddress ipAddress = endPoint.Address;
IPHostEntry hostEntry = Dns.GetHostEntry(ipAddress);
string hostname = hostEntry.HostName;
lock (m_Servers)
{
if (m_Servers.ContainsKey(hostname))
{
isConnected = false;
message = "Client is already connected to server";
}
else
{
m_Servers.Add(hostname, new Server(this, socket));
isConnected = true;
message = "Successfully connected.";
}
}
m_Client.OnConnect(isConnected, message);
}
catch (Exception)
{
isConnected = false;
message = "Could not connect to server.";
m_Client.OnConnect(isConnected, message);
}
}
public void SetLiveStatus(string hostname, bool newStatus)
{
string message;
lock (m_Locker)
{
if (m_Servers.ContainsKey(hostname))
{
if (m_Servers[hostname].IsLive == newStatus)
{
message = "Server is already set to this status.";
}
else
{
m_Servers[hostname].IsLive = newStatus;
message = "Successfully set new status.";
}
}
else
{
message = "Server not found.";
}
}
m_Client.OnSetLiveStatus(hostname, message);
}
}
}
Does it Run?
Does it throw an exception(s)?
Pitfall in trying to run server code in multiple threads:
AVOID attempting to manipulate, read or write a socket in different threads. Have one thread accept connections from the server socket and spawn a thread to handle transactions. If you get too many threads going on at once, you're going to have have 1 thread handle several sockets.
No it is not thread safe.
A subscriber can have unsubscribe before between the check and the invocation:
if (ConnectEvent != null)
{
ConnectEvent(this, new ConnectEventArgs(isConnected, message));
}
Define the event as:
public event ConnectEventHandler ConnectEvent = delegate{};
and remove the event to get thread safety.
I would reduce the run loop to:
private void Run()
{
while (true)
{
m_WaitHandle.WaitOne();
Event task = null;
lock (m_Locker)
{
if (m_Tasks.Count == 0)
{
m_WaitHandle.Reset();
continue;
}
task = m_Tasks.Dequeue();
}
task.DoTask(m_Manager);
}
}
The loop will continue to run until the event is reset.
Make sure that no null items are inserted into the queue instead of checking for null.
You could simplify the producer-consumer pattern in the Client class by using BlockingCollection instead of a combination of an AutoResetEvent and plain old Queue.
The EnqueueTask method would look like:
public void EnqueueTask(Event task)
{
m_Queue.Add(task);
}
The Run method would look like:
public void Run()
{
while (true)
{
Event task = m_Queue.Take();
if (task == null)
{
return;
}
task.DoTask();
}
}

Categories