I am very new to sockets with C# and I have been working on trying to get a socket to work with localhost. Here is the main UDP code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class NetUDP
{
private UdpClient udp;
private IPEndPoint endPont;
public NetUDP(int port_local, int port_global, string ip_global, string ip_local)
{
Console.WriteLine(IPAddress.Any.ToString());
udp = new UdpClient(IPAddress.Any.ToString(), port_global);
endPont = new IPEndPoint(IPAddress.Parse(ip_global), port_global);
udp.Connect(ip_global, port_global);
}
public NetUDP()
{
}
public void UDPinit(int port_local, int port_global, string ip_global, string ip_local)
{
Console.WriteLine();
udp = new UdpClient("127.0.0.1", port_global);
endPont = new IPEndPoint(IPAddress.Parse("127.0.0.1"), port_global);
udp.Connect("127.0.0.1", port_global);
}
public void sendDataUDP(string info)
{
Byte[] SendByte = Encoding.ASCII.GetBytes(info);
udp.Send(SendByte, SendByte.Length);
}
public string getDataUDP()
{
Byte[] get = udp.Receive(ref endPont);
return Encoding.ASCII.GetString(get);
}
public void closeUDP()
{
udp.Close();
}
}
}
The problem is every time I do this code when I try to retrieve info from another UDP C# program (on the same machine) it freezes. I know it keeps on looking for info coming it's way, but id there a way to put a timeout on the receive command or do I have to make an event handler to prevent this freezing? The ports connect just fine and do not show any form of error or exception. Thank you for your help in advance!
class Program
{
static void Main(string[] args)
{
var serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8500);
NetUDP udp = new NetUDP(serverEndPoint);
UdpClient localClient = new UdpClient();
Task.Delay(20000);
var dgram = Encoding.ASCII.GetBytes("Hello Server");
localClient.Send(dgram, dgram.Length, serverEndPoint); // Send "Hello Server" to Server
Console.Read();
}
}
class NetUDP
{
private UdpClient udp;
private IPEndPoint endPont;
public NetUDP() { }
public NetUDP(IPEndPoint serverEndPoint)
{
udp = new UdpClient(serverEndPoint); // Bind the EndPoint
// Receive any IPAddress send to the 8555 port
IPEndPoint receiveEndPoint = new IPEndPoint(IPAddress.Any, 8555);
Task.Run(() =>
{
try
{
while (true)
{
// This Receive will block until receive an UdpClient
// So, run in another thread in thread pool
var bytes = udp.Receive(ref receiveEndPoint);
Console.WriteLine(Encoding.ASCII.GetString(bytes));
}
}
catch (Exception ex)
{
// TODO
}
finally
{
udp.Close();
}
});
}
}
Here is and UdpChat demo in github: UdpChat
Related
im trying to implement a quick thread safe socket for udp. for this Im creating a while true loop for both send and receive that will constantly send/receive information as needed.
I created this in a few minutes just for a rough idea of how it'll work. this is from the belief (that I'm not sure of) that send and receive methods do not affect each other or the object.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections.Concurrent;
namespace ConsoleApp1
{
class ThreadSafeSocket
{
private UdpClient udpclient;
private ConcurrentQueue<SendInfo> sendData;
private SendInfo data;
private object recieveLock;
private int port;
public ThreadSafeSocket(int port)
{
this.udpclient = new UdpClient(port);
this.sendData = new ConcurrentQueue<SendInfo>();
this.port = port;
Thread receiveThread = new Thread(RecieveLoop);
Thread sendThread = new Thread(SendLoop);
receiveThread.Start();
sendThread.Start();
}
public void Send(SendInfo data)
{
sendData.Enqueue(data);
}
public SendInfo Receive()
{
lock(recieveLock)
{
byte[] receivedata = new byte[data.Data.Length];
Array.Copy(receivedata, data.Data, data.Data.Length);
IPEndPoint point = new IPEndPoint(data.Point.Address, data.Point.Port);
return new SendInfo(receivedata, point);
}
}
private void SendLoop()
{
while (true)
{
if (sendData.TryDequeue(out SendInfo result))
{
udpclient.Send(result.Data, result.Data.Length,this.data.Point);
}
else
{
Thread.Sleep(1);
}
}
}
private void RecieveLoop()
{
while(true)
{
IPEndPoint point = new IPEndPoint(IPAddress.Any, this.port);
byte[] data = this.udpclient.Receive(ref point);
lock(recieveLock)
{
this.data = new SendInfo(data, point);
}
}
}
}
}
sendinfo consists of a byte array called data, and an ipendpoint called point.
I've heard that thread safety is very hard to test so if the idea itself is wrong id asked before testing. thanks in advance.
Well I got my answer, sockets by themselves already can't receive and send at the same time most likely so this implementation is wrong. It's better instead to just look an instance of udpClient and not the methods.
I have these two classes that is part of my Server application (desktop) and need send a command back to Client after connection was established.
When i try make this way:
clients[i].Send("info");
the Send() routine (of Listener.cs) is accessible, but i have this following sintaxe error:
How solve this?
Listener.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
class Listener
{
Socket s;
string IP;
public List<Info> clients;
public delegate void ReceivedEventHandler(Listener l, Info i, string received);
public event ReceivedEventHandler Received;
public delegate void DisconnectedEventHandler(Listener l, Info i);
public event DisconnectedEventHandler Disconnected;
bool listening = false;
public Listener()
{
clients = new List<Info>();
s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public bool Running
{
get { return listening; }
}
public void BeginListen(int port)
{
s.Bind(new IPEndPoint(IPAddress.Any, port));
s.Listen(100);
s.BeginAccept(new AsyncCallback(AcceptCallback), s);
listening = true;
}
public void StopListen()
{
if (listening == true)
{
s.Close();
}
}
void AcceptCallback(IAsyncResult ar)
{
Socket handler = (Socket)ar.AsyncState;
Socket sock = handler.EndAccept(ar);
Info i = new Info(sock);
clients.Add(i);
Console.WriteLine("New Connection: " + i.ID.ToString());
clients[i].Send("info");
sock.BeginReceive(i.buffer, 0, i.buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), i);
handler.BeginAccept(new AsyncCallback(AcceptCallback), handler);
}
void ReadCallback(IAsyncResult ar)
{
Info i = (Info)ar.AsyncState;
try
{
int rec = i.sock.EndReceive(ar);
if (rec != 0)
{
string data = Encoding.ASCII.GetString(i.buffer, 0, rec);
Received(this, i, data);
}
else
{
Disconnected(this, i);
return;
}
i.sock.BeginReceive(i.buffer, 0, i.buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), i);
}
catch
{
Disconnected(this, i);
i.sock.Close();
clients.Remove(i);
}
}
}
Info.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
public class Info
{
public Socket sock;
public Guid ID;
public string RemoteAddress;
public byte[] buffer = new byte[8192];
public Info(Socket sock)
{
this.sock = sock;
ID = Guid.NewGuid();
RemoteAddress = sock.RemoteEndPoint.ToString();
}
public void Send(string data)
{
byte[] buffer = Encoding.ASCII.GetBytes(data);
sock.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback((ar) =>
{
sock.EndSend(ar);
}), buffer);
}
}
Project of reference
clients[i].Send("info");
This statement is incorrect. The variable 'i' refers to an instance of the Info class. It is not an integer index into the list. The correct call would be as below.
i.Send("info");
I am attempting to connect multiple clients to a server and monitor their connection...
I am trying to get a better understanding of TcpListener and TcpClient while creating these programs.
I found my server code from another stackoverflow answer as I am looking to get a connection from multiple clients, I have edited it abit:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace socket_server
{
class connect
{
public class State
{
public Socket workSocket = null;
public const int bufferSize = 1024;
public byte[] buffer = new byte[bufferSize];
public StringBuilder sb = new StringBuilder();
}
public class Server
{
public static ManualResetEvent allDone = new ManualResetEvent(false);
private static IPEndPoint findMe()
{
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 5505);
return localEndPoint;
}
public static void start()
{
try
{
TcpListener listener = new TcpListener(findMe());
TcpClient client;
listener.Start();
Console.WriteLine("Server IP: {0}\n", findMe());
Console.WriteLine("Waiting for Connection...");
while (true)
{
client = listener.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(threadProc, client);
}
Console.ReadKey();
} catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void threadProc(object obj)
{
try
{
var client = (TcpClient)obj;
} catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
}
You call this like connect.Server.start()
This is my current client code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace socket_client
{
class connect
{
public class State
{
public Socket workSocket = null;
public const int bufferSize = 256;
public byte[] buffer = new byte[bufferSize];
public StringBuilder sb = new StringBuilder();
}
public class Client
{
private const int port = 5505;
private static String response = String.Empty;
private static IPEndPoint findServer()
{
IPHostEntry ipHostInfo = Dns.Resolve("10.1.2.30");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
return remoteEP;
}
public static void start()
{
TcpClient client = new TcpClient();
Console.WriteLine("Server IP: {0}", findServer().ToString());
Console.WriteLine("Client IP: {0}\n", software.getIPAddress());
connect(client);
if (client.Connected)
Console.WriteLine("\nConnected to: {0}\n", findServer().ToString());
}
private static void connect(TcpClient client)
{
try
{
client.Connect(findServer());
} catch(Exception e)
{
Console.WriteLine("Attempting to connect to: {0}", findServer().ToString());
connect(client);
}
}
}
}
}
You call this like connect.Client.start();
My classes such as hardware and software simply just get system information.
I would like to know how to check if a connection is dropped and respond with a Console.WriteLine for both Client and Server.
Edit-
This is what I have working for checking to see if a client drops from the server:
...
private static void threadProc(object obj)
{
try
{
var client = (TcpClient)obj;
Byte[] bytes = new Byte[256];
string data = null;
try {
NetworkStream stream = client.GetStream();
int i;
while((i = stream.Read(bytes,0,bytes.Length)) != 0)
{
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine(">{0}: {1}", ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString(), data);
}
} catch(Exception e)
{
Console.WriteLine(">>{0} Lost Connection...", ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString());
}
} catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
...
You will only be able to detect if a connection was closed when reading on the stream fails, since there is no event which would notify you.
You might raise your own event in the connection-handling class.
But for some reasons, it is possible, that a TCP-Connection drops without a pending Read()-call to fail.
So, there is no perfect method, like handling an event, which always notifies you, when the connection broke. So you have to implement some keep-alive mechanism. E.g. send a ping/pong every once in a while and wait for the answer. If there is no answer in a defined time, you can close the connection.
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
}
}
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();
}
}