Шаблон многопоточного TCP сервера
Для собственных нужд понадобилась программка которая будет слушать указанные порты, принимать/отсылать по ним данные.
Каждый созданный объект в шарпе имеет собственный контекст исполнения, используя данный контекст мы можем вызывать методы данного объекта, чем я и воспользуюсь.
Итак, у нас будет основной процесс, из которого мы можем назначать диспетчеру какие порты хотим слушать, и в котором будем принимать данные на обработку и при необходимости отсылать результат. Есть диспетчер, который следит за тем, чтобы прослушка портов не прерывалась. И есть дочерние потоки в которых собственно прослушка и висит.
Создаем два класса которые позволят передать в дочерний поток список методов для вызова при отлове сообщения на порту.
Тут все просто, в ContextMethod храним контекст объекта чей метод вызовем, ссылку на метод и вызов метода объекта. Ну а в ListContextMethod храним список вызываемых методов нужных нам объектов.
Теперь напишем класс отвечающий за прослушку конкретного порта:
И наконец диспетчер запускающий прослушку в потоки, и отслеживающий запуск прослушки после принятия данных:
Можно использовать. Поставить на прослушку нужный порт можно следующим образом:
Где ReceiveData метод принимающий данные. Например так:
Ну и не стоит забывать вызвать TCPReaderChain.EndAllListener(); при закрытии программы.
В аттаче пример, с возможностью тестировать работу.
Еще большая просьба, если кто знает, можно ли определить жив ли еще объект зная только его контекст синхронизации, напишите как это сделать.
Каждый созданный объект в шарпе имеет собственный контекст исполнения, используя данный контекст мы можем вызывать методы данного объекта, чем я и воспользуюсь.
Итак, у нас будет основной процесс, из которого мы можем назначать диспетчеру какие порты хотим слушать, и в котором будем принимать данные на обработку и при необходимости отсылать результат. Есть диспетчер, который следит за тем, чтобы прослушка портов не прерывалась. И есть дочерние потоки в которых собственно прослушка и висит.
Создаем два класса которые позволят передать в дочерний поток список методов для вызова при отлове сообщения на порту.
public class ContextMethod
{
private SynchronizationContext context = null;
/// <summary>
/// Контекст
/// </summary>
public SynchronizationContext Context
{
get
{
return context;
}
set
{
context = value.CreateCopy();
}
}
public ContextCallback method;
/// <summary>
/// Запуск на выполнение метода в указанном контексте
/// </summary>
/// <param name="state"></param>
public void Run(object state)
{
if (context != null)
context.Send(delegate(object obj) { method(state); }, null);
}
}
/// <summary>
/// Класс выполняющий метод указанного контекста
/// </summary>
public class ListContextMethod
{
/// <summary>
/// Метод
/// </summary>
public List<ContextMethod> async_events = new List<ContextMethod>();
/// <summary>
/// Вызов методов в указанном контексте
/// </summary>
/// <param name="state">Параметр метода</param>
public void Run(object state)
{
foreach (ContextMethod call in async_events)
call.Run(state);
}
}
* This source code was highlighted with Source Code Highlighter.Тут все просто, в ContextMethod храним контекст объекта чей метод вызовем, ссылку на метод и вызов метода объекта. Ну а в ListContextMethod храним список вызываемых методов нужных нам объектов.
Теперь напишем класс отвечающий за прослушку конкретного порта:
/// <summary>
/// Класс для прослушки указанного порта
/// </summary>
public class TCPReader
{
Socket sock;
/// <summary>
/// Прослушиваемый порт
/// </summary>
private int port;
/// <summary>
/// Прослушивалка
/// </summary>
private TcpListener listener;
/// <summary>
/// Набор методов исполняющихся после получения данных на порт
/// </summary>
private ListContextMethod OnRead = null;
/// <summary>
/// Завершение работы
/// </summary>
public void End()
{
if (sock != null)
sock.Close();
if (listener != null)
listener.Stop();
}
/// <summary>
/// Конструктор с параметрами
/// </summary>
public TCPReader(int _port, ListContextMethod onRead)
{
port = _port;
OnRead = onRead;
}
/// <summary>
/// Запуск прослушки порта
/// </summary>
public void Start()
{
listener = new TcpListener(IPAddress.Any, port);
listener.Start();
object[] ret = new object[2];
try
{
sock = listener.AcceptSocket();
ret[0] = port;
ret[1] = sock;
listener.Stop();
}
catch
{ }
if (OnRead != null)
OnRead.Run(ret);
}
}
* This source code was highlighted with Source Code Highlighter.И наконец диспетчер запускающий прослушку в потоки, и отслеживающий запуск прослушки после принятия данных:
/// <summary>
/// Список прослушиваемых портов
/// </summary>
public static class TCPReaderChain
{
//Список потоков прослушивающих порты
private static Dictionary<int, TCPReader> readers = new Dictionary<int, TCPReader>();
//Перезапуск прослушки порта
private static void RestartListen(object data)
{
object[] arr = (object[])data;
if (arr != null && arr.Length > 0)
if (readers.ContainsKey((int)arr[0]))
new Thread(readers[(int)arr[0]].Start).Start();
}
//Запуск прослушки указанного порта
public static bool StartListen(int port, ListContextMethod onRead)
{
if (!readers.ContainsKey(port))
{
onRead.async_events.Add(new ContextMethod
{
Context = SynchronizationContext.Current,
method = RestartListen
});
readers.Add(port, new TCPReader(port, onRead));
Thread th = new Thread(readers[port].Start);
th.IsBackground = true;
th.Priority = ThreadPriority.Lowest;
th.Start();
return true;
}
else return false;
}
/// <summary>
/// Завершение прослушивания портов
/// </summary>
public static void EndAllListener()
{
foreach (int key in readers.Keys)
{
readers[key].End();
}
Thread.Sleep(1000);
readers.Clear();
}
* This source code was highlighted with Source Code Highlighter.Можно использовать. Поставить на прослушку нужный порт можно следующим образом:
private void AddPort(int port)
{
ListContextMethod meth = new ListContextMethod();
meth.async_events.Add(new ContextMethod
{
Context = SynchronizationContext.Current,
method = RecieveData
});
TCPReaderChain.StartListen(port, meth);
}
* This source code was highlighted with Source Code Highlighter.Где ReceiveData метод принимающий данные. Например так:
private void RecieveData(object data)
{
object[] arr = (object[])data;
if (arr != null && arr.Length == 2)
{
string output = "";
if (arr[1] is Socket)
{
Socket sock = (Socket)arr[1];
byte[] buf = new byte[sock.ReceiveBufferSize];
sock.Receive(buf);
output = Encoding.Default.GetString(buf);
sock.Send(Encoding.Default.GetBytes("SUCCESS"));
sock.Close();
}
lock (listBox1)
listBox1.Items.Add(arr[0].ToString() + ":" + output);
}
}
* This source code was highlighted with Source Code Highlighter.Ну и не стоит забывать вызвать TCPReaderChain.EndAllListener(); при закрытии программы.
В аттаче пример, с возможностью тестировать работу.
Еще большая просьба, если кто знает, можно ли определить жив ли еще объект зная только его контекст синхронизации, напишите как это сделать.



комментарии (15)