|
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;
using System.Xml.Serialization;
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.xml", Watch = true)]
namespace Utils
{
public class SocketSender<D, R> : IDisposable
{
public delegate void ClientConnect(String remoteAddress);
public delegate void ClientRemove(String remoteAddress);
public delegate void ResponseRecieved(String remoteAddress, R data);
public delegate Boolean CanSend(String remoteAddress, D data);
private log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
// max incoming connection on socket.Listen
private const int MAX_CONN = 10;
// timeout increment
private const int TIMEOUT_INC = 2000;
private int port;
private Socket sock;
private Thread acceptThread;
private Timer lifeTickTimer;
private ClientConnect onClientConnect;
public event ClientConnect OnClientConnect
{
add
{
onClientConnect = (ClientConnect)System.Delegate.Combine(onClientConnect, value);
}
remove
{
onClientConnect = (ClientConnect)System.Delegate.Remove(onClientConnect, value);
}
}
private ClientRemove onClientRemove;
public event ClientRemove OnClientRemove
{
add
{
onClientRemove = (ClientRemove)System.Delegate.Combine(onClientRemove, value);
}
remove
{
onClientRemove = (ClientRemove)System.Delegate.Remove(onClientRemove, value);
}
}
private ResponseRecieved onResponseRecieved;
public event ResponseRecieved OnResponseRecieved
{
add
{
onResponseRecieved = (ResponseRecieved)System.Delegate.Combine(onResponseRecieved, value);
}
remove
{
onResponseRecieved = (ResponseRecieved)System.Delegate.Remove(onResponseRecieved, value);
}
}
private CanSend onCanSend;
public event CanSend OnCanSend
{
add
{
onCanSend = (CanSend)System.Delegate.Combine(onCanSend, value);
}
remove
{
onCanSend = (CanSend)System.Delegate.Remove(onCanSend, value);
}
}
private XmlSerializer responseSerializer = new XmlSerializer(typeof(R));
private List<ComClass<D>> clients = new List<ComClass<D>>();
private int lifeTickTimeout;
/// <summary>
/// SockerSender server
/// </summary>
/// <param name="port">tcpip listen port</param>
/// <param name="lifeTickTimeout">timeout between lifetick packet in ms</param>
/// <param name="sendLifeTicks">send or not send lifetick events</param>
public SocketSender(int port, int lifeTickTimeout, Boolean sendLifeTicks)
{
this.port = port;
this.lifeTickTimeout = lifeTickTimeout;
if (sendLifeTicks)
this.lifeTickTimer = new Timer(lifeTick_callback, null, 0, lifeTickTimeout);
}
/// <summary>
/// Start listening
/// </summary>
public void Start()
{
acceptThread = new Thread(run);
// start listening in new thread
acceptThread.Start();
}
private void run()
{
try
{
// prepare the socket
sock = new Socket(
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint ipe = new IPEndPoint(
IPAddress.Parse("0.0.0.0"), port);
sock.Bind(ipe);
sock.Listen(MAX_CONN);
log.Debug("Start listening");
while (true)
{
Socket newConnection = sock.Accept();
// new socket is stored in a list of clients
ComClass<D> newClient = new ComClass<D>(newConnection, clientResponse);
newClient.SendTimeOut(lifeTickTimeout + TIMEOUT_INC);
lock (clients)
{
clients.Add(newClient);
log.Debug("Clients: " + clients.Count.ToString());
}
newClient.StartRead();
if (onClientConnect != null)
onClientConnect.Invoke(newClient.RemoteAddress);
}
}
catch (Exception ex)
{
log.Debug("Exception: " + ex.Message);
}
}
/// <summary>
/// Send data to all connected clients
/// </summary>
/// <param name="data"></param>
public void SendData(D data)
{
lock (clients)
{
// first we remove dead clients
int i = 0;
while (i < clients.Count)
{
if (clients[i].IsDead)
{
if (onClientRemove != null)
this.onClientRemove.Invoke(clients[i].RemoteAddress);
clients.RemoveAt(i);
}
else
i++;
}
log.Debug("Clients: " + clients.Count.ToString());
// after that we send data to all clients
foreach (ComClass<D> com in clients)
{
Boolean sendData = true;
// if we have a handler we check with the handler before sending
if (this.onCanSend != null)
sendData = this.onCanSend.Invoke(com.RemoteAddress, data);
// send
if (sendData)
com.SendData(data);
}
}
}
private void lifeTick_callback(Object state)
{
log.Debug("Send LifeTick");
SendData(default(D));
}
private void clientResponse(String remoteAddress, String xmlData)
{
StringReader sReader = new StringReader(xmlData);
R sd = (R)responseSerializer.Deserialize(sReader);
if ((sd != null) && (onResponseRecieved != null))
{
// response recieved
onResponseRecieved.Invoke(remoteAddress, sd);
}
}
public void Dispose()
{
if (sock != null)
{
// when closing no erros
try
{
// we close all client sockets
foreach (ComClass<D> com in clients)
{
com.Dispose();
}
// than we close the listening socket
sock.Close();
}
catch (Exception) { }
}
if (acceptThread != null)
{
acceptThread.Abort();
acceptThread.Join();
}
}
}
public class ComClass<D> : IDisposable
{
private log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
public delegate void ClientResponse(String remoteAddress, String xmlMsg);
private Socket socket;
private StreamReader m_reader;
private StreamWriter m_writer;
private Boolean isDead = false;
public Boolean IsDead
{
get
{
return this.isDead;
}
}
private Thread readThread;
private volatile Boolean reading;
private XmlSerializer dataSerializer = new XmlSerializer(typeof(D));
private ClientResponse onClientResponse;
private String remoteAddress;
public String RemoteAddress
{
get { return this.remoteAddress; }
}
public ComClass(Socket socket)
{
// we store the remote addres of client
this.remoteAddress = ((IPEndPoint)socket.RemoteEndPoint).ToString();
log.Debug("Connected to: " + remoteAddress);
this.socket = socket;
this.m_reader = new StreamReader(new NetworkStream(socket), Encoding.Unicode);
this.m_writer = new StreamWriter(new NetworkStream(socket), Encoding.Unicode);
}
public ComClass(Socket socket, ClientResponse onClientResponse)
{
// we store the remote addres of client
this.remoteAddress = ((IPEndPoint)socket.RemoteEndPoint).ToString();
log.Debug("Connected to: " + remoteAddress);
this.socket = socket;
this.m_reader = new StreamReader(new NetworkStream(socket), Encoding.Unicode);
this.m_writer = new StreamWriter(new NetworkStream(socket), Encoding.Unicode);
this.onClientResponse = onClientResponse;
}
public void SendData(D data)
{
// sending of data in custom thread
Thread senderThread = new Thread(sendInThread);
senderThread.Start(data);
}
public void SendTimeOut(int timeout)
{
// sending of data in custom thread
Thread senderThread = new Thread(sendInThread);
senderThread.Start(timeout);
}
public void StartRead()
{
this.readThread = new Thread(readSocket);
this.readThread.Start();
}
private void readSocket()
{
this.reading = true;
while (reading)
{
try
{
// reading socket for responses
readData();
}
catch (System.Threading.ThreadAbortException)
{
log.Debug("ThreadAbort exception");
// končamo zanko
break;
}
catch (Exception)
{
}
}
}
private void sendInThread(Object data)
{
lock (m_writer)
{
try
{
// we are sending timeout
if (data is Int32)
{
// send TIMEOUT=0000000
m_writer.WriteLine(Commons.CreateTimeoutString((Int32)data));
m_writer.Flush();
}
else
if (data == null)
{
// sending lifetick just size line
m_writer.WriteLine(Commons.CreateSizeString(0));
m_writer.Flush();
}
else
{
StringBuilder sBuilder = new StringBuilder();
StringWriter sWriter = new StringWriter(sBuilder);
dataSerializer.Serialize(sWriter, (D)data);
// size tag on begining
m_writer.WriteLine(Commons.CreateSizeString(sBuilder.Length));
m_writer.WriteLine(sBuilder.ToString());
m_writer.Flush();
}
}
catch (Exception ex)
{
log.Debug(ex.Message);
this.isDead = true;
this.Dispose();
}
}
}
private void readData()
{
// read the data - sync with size message
int size = -1;
do
{
String message = m_reader.ReadLine();
size = Commons.ParseSizeMessage(message);
}
while (size < 0);
char[] buf = new char[size];
StringBuilder sb = new StringBuilder();
while (sb.Length < size)
{
int len = m_reader.Read(buf, 0, size - sb.Length);
sb.Append(new string(buf, 0, len));
}
if (onClientResponse != null)
onClientResponse.Invoke(this.remoteAddress, sb.ToString());
}
#region IDisposable Members
public void Dispose()
{
// stop the reading thread
this.reading = false;
if (readThread != null)
readThread.Abort();
if (socket != null)
{
log.Debug("Drop connection.");
try
{
m_writer.Close();
socket.Close();
}
catch (Exception) { }
}
}
#endregion
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.