04.03.2009

Многопоточный сервер на C#

Сервер может быть организован по следующему принципу: отдельный поток на прослушивание порта и каждый поток на клиентское подключение.



/// Ассинхронный сервер
public class AsyncServer
{
/// Сокет сервера
private Socket _serverSocket;

/// Элемент для синхронизации ожидания подключений
private static ManualResetEvent _connectionMutex =
new ManualResetEvent(false);

/// Менеджер для обработки клиентов
private ClientManager _clientManager;

public AsyncServer(string ipAddrees, int port)
{
try
{
// создание сокета для сервера
this._serverSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);

// прикрепление сервера к ip адресу и порту
this._serverSocket.Bind(
new IPEndPoint(IPAddress.Parse(ipAddrees), port));

}
catch (Exception ex)
{
throw new Exception("Ошибка инициализации сервера.", ex);
}
}

private BackgroundWorker _listenThread = new BackgroundWorker();

/// Начать работу сервера
public void Start()
{
this._clientManager = new ClientManager(this._clientConnections);

this._listenThread.WorkerReportsProgress = true;
this._listenThread.WorkerSupportsCancellation = true;
this._listenThread.DoWork +=
new DoWorkEventHandler(ListenThread_DoWork);

this._listenThread.RunWorkerAsync(this._serverSocket);
}

/// Поток для прослушки порта
private void ListenThread_DoWork(object sender, DoWorkEventArgs e)
{
Socket serverSocket = (Socket)e.Argument;

// прослушивание сокета
serverSocket.Listen(100);

while (true)
{
// сбросить мьютекс
_connectionMutex.Reset();

serverSocket.BeginAccept(
new AsyncCallback(this.AcceptCallback), this._serverSocket);

// ожидание следующего подключения
_connectionMutex.WaitOne();
}
}

/// Клиентские подключения
private List _clientConnections = new List();

/// Количество клиентских подключений
public int ConnectionsCount
{
get { return this._clientConnections.Count; }
}

/// Callback метод для обработки входящих соединений
private void AcceptCallback(IAsyncResult asyncResult)
{
// уведомить о том, что подключение произошло
_connectionMutex.Set();

Socket serverSocket = (Socket)asyncResult.AsyncState;
Socket clientSocket = (Socket)serverSocket.EndAccept(asyncResult);
this._clientConnections.Add(clientSocket);

// передача управления клиентом менеджеру клиентов
this._clientManager.HandleClient(clientSocket);
}

}


Метод AcceptCallback(IAsyncResult asyncResult) срабатывает при новом входящем соединение и передает сокет клиента специальному менеджеру (ClientManager), который создаст для него отдельный поток на обслуживание. Ниже приведен код менеджера клиентов.



/// Менеджер для работы с клиентским подключениями
public class ClientManager
{
/// Коллекция процессоров клиентов
private List<BackgroundWorker> _clientProcessors = new List<BackgroundWorker>();

/// Коллекция соединений
private List<Socket> _connections;

/// <param name="connections">Список соединений</param>
public ClientManager(List<Socket> connections)
{
this._connections = connections;
}

/// Обработка клиентского подключения
public void HandleClient(Socket clientSocket)
{
BackgroundWorker clientProcessor = new BackgroundWorker();
clientProcessor.DoWork += new DoWorkEventHandler(ClientProcessing);

// добавление процессора в коллекцию процессоров
this._clientProcessors.Add(clientProcessor);

List<object> args = new List<object>();
// добавление аргументов для потока
// args.Add(...);

// запустить процессор для обработки клиента
clientProcessor.RunWorkerAsync(args);
}

/// Метод для работы с клиентом в другом потоке
private void ClientProcessing(object sender, DoWorkEventArgs e)
{
// чтение аргументов
List<object> args = (List<object>)e.Argument;

ProtocolSerializer serializer = new ProtocolSerializer();

try
{
while (socket.Connected)
{
// получение или отправка данных

}
}
catch (SocketException)
{
// обработка исключений
}
catch (Exception)
{
// обработка исключений
}
}
}

1 комментарий:

Unknown комментирует...

Последний метод вообще нифига не канает))

У меня прекрасно заработало так:
/// Метод для работы с клиентом в другом потоке
private void ClientProcessing(объект sender, DoWorkEventArgs e)
{
byte[] bytes;
string data = null;

// Перебор объектов из e.Argument
foreach (объект obj in (List)e.Argument)
{
// Если объект является сокетом
if (obj is Socket)
{
Socket socket = (Socket)obj;
try
{
while (socket.Connected)
{
// получение или отправка данных

bytes = new byte[1024];
int bytesRec = socket.Receive(bytes);
// итд...


}
}
catch (SocketException)
{
// обработка исключений
}
catch (Exception)
{
// обработка исключений
}
}
}
}
Но опять же, э то только моё мнение), я новичёк в csharp, приводил в рабочее состояние этот метод 2 дня).