Как один поток сервера может обрабатывать несколько клиентов - C#
Формулировка задачи:
Есть клиент-серверное приложение, на данный момент 4х потоковое. Моя роль в нем это написание хорошего сервера.
Сейчас сервер работает как:
1)Ждёт подключения.
2)Ждёт пока клиент отправит данные.
3)Получает данные.
4)Отправляет ответ и ждёт пока ответ будет получен.
5)Переходит в ожидание следующего подключения клиента (те на пункт 1).
Те всё работает в синхронном режиме.
В виде кода выглядит так (есть мой класс оболочка для обработки записи и чтения):
Как работает мой класс-обёртка для чтения и записи:
Преподавателю это не нравиться, мол фигня это всё что сервер в потоке ждет пока клиент ему что-то пришлет и надо мол чтобы это всё асинхронно работало и чтобы поток обрабатывал другого клиента пока этот не хочет отправлять данные например (ну вот для случая что клиент подключился и висит 30 секунд, а потом уже только данные начинает слать). Вот только саму идею асинхронности я не могу понять. Сейчас я пишу и читаю байтики в/из потока через синхронные функции Read и Write. Я вижу что есть также асинхронные Read и Write, но
private static void ServerThread(object data)
{
NamedPipeServerStream pipeServer =
new NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
//пара логин-пароль для получения инфо от клиента
Tuple<string, string> logpass;
while (true)
{
pipeServer.WaitForConnection();
try
{
StreamRW srw = new StreamRW(pipeServer);
logpass = srw.ReadData();
srw.WriteInt(1);
}
catch (IOException e)
{
Console.WriteLine("ERROR: {0}", e.Message);
}
pipeServer.Disconnect();
}
pipeServer.Close();
}/класс для работы с потоком
//Позволяет писать пары логин-пароль и считывать результаты их обработки
class StreamRW
{
private Stream ioStream;//поток куда писать данные
public StreamRW(Stream stream)
{ ioStream = stream; }
//запись пары логин-пароль в поток
public void WriteData(Tuple<string, string> data)
{
byte[] outBuffer;
using (MemoryStream m = new MemoryStream())
using (BinaryWriter writer = new BinaryWriter(m))
{
writer.Write(data.Item1);
writer.Write(data.Item2);
outBuffer = m.ToArray();
}
int len = outBuffer.Length;
ioStream.WriteByte((byte)(len / 256));
ioStream.WriteByte((byte)(len & 255));
ioStream.Write(outBuffer, 0, len);
ioStream.Flush();
}
//чтение пары логин-пароль из потока
public Tuple<string, string> ReadData()
{
Tuple<string, string> result;
int len;
len = ioStream.ReadByte() * 256;
len += ioStream.ReadByte();
byte[] inBuffer = new byte[len];
ioStream.Read(inBuffer, 0, len);
using (MemoryStream m = new MemoryStream(inBuffer))
using (BinaryReader reader = new BinaryReader(m))
{
try
{
result = new Tuple<String, String>(reader.ReadString(), reader.ReadString());
}
catch (IOException ex)
{
Console.WriteLine("Error has occured :(\n" +
"If you'd like to know more, you can google it:" + ex.Message);
result = new Tuple<String, String>("Error", "Error");
}
}
return result;
}
//запись кода результата обработки
public void WriteInt(int answer)
{
byte[] outBuffer = new byte[1] { Convert.ToByte(answer) };
ioStream.Write(outBuffer, 0, 1);
ioStream.Flush();
}
//чтение кода результата обработки (если соединение разорвано на стороне сервера то вернёт -1).
//Исключение выброшено не будет даже если поток закрыт!
public int ReadInt()
{
return Convert.ToInt32(ioStream.ReadByte());
}
}я не могу понять саму идею: как один поток сервера может обрабатывать несколько клиентов? Те как это он может пока один клиент висит получить данные от другого и ответить ему?
Объясните пожалуйста, с кодом было бы вообще идеально, но хотя бы на словах.Решение задачи: «Как один поток сервера может обрабатывать несколько клиентов»
textual
Листинг программы
namespace Server
{
public class PipeServer
{
//количество потоков, пока что рассмотрим ситуацию когда = 1
private int numThreads;
//класс который хранит указатель на открытый канал и байты считанные с него
private class ConnectionInfo
{
public NamedPipeServerStream pipeServer;
public byte[] buffer;
}
public PipeServer(int numThreads)
{
this.numThreads = numThreads;
}
//запуск треда на выполнение
public void Start()
{
//создаём экземпляр
NamedPipeServerStream pipeServer =
new NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads,
PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
//асинхроонное ожидание подключения, при подключении будет вызвано AcceptCallback
pipeServer.BeginWaitForConnection(new AsyncCallback(AcceptCallback), pipeServer);
Console.WriteLine("\n*** Named pipe server stream started ***\n");
Console.WriteLine("Waiting for client connect...\n");
}
//вызывается при подключении к серверу клиента
/// <param name="result">указатель на открытый канал сервера</param>
private void AcceptCallback(IAsyncResult result)
{
ConnectionInfo connection = new ConnectionInfo();
connection.pipeServer = (NamedPipeServerStream)result.AsyncState;
//объявляем об окончании ожидания (вроде так нужно тут сделать же?)
connection.pipeServer.EndWaitForConnection(result);
connection.buffer = new byte[1];//выделим место под информацию для получения от клиента
//когда клиент подключился выведем уведомление об этом
Console.WriteLine("{0} connected on thread[{1}].",
connection.pipeServer.GetImpersonationUserName(), Thread.CurrentThread.ManagedThreadId);
//начать АСИНХРОННОЕ чтение с потока
connection.pipeServer.BeginRead(connection.buffer, 0, connection.buffer.Length,
new AsyncCallback(ReceiveCallback), connection);
//НАЧАТЬ АСИНХРОННОЕ ожидание след. подключеня не даёт - вываливается исключение что
//канал уже находится в подключ. состоянии
//connection.pipeServer.BeginWaitForConnection(new AsyncCallback(AcceptCallback), connection.pipeServer);
}
//вызывается при чтении данных с потока после подключения
private void ReceiveCallback(IAsyncResult result)
{
ConnectionInfo connection = (ConnectionInfo)result.AsyncState;
//считать данные
StreamRW stream = new StreamRW(connection.pipeServer);
Tuple<string, string> tuple = stream.ReadTuple(connection.buffer[0]);
Console.WriteLine(tuple.Item1+" "+tuple.Item2);
//записать АСИНХРОННО ответ
connection.buffer = new byte[1] { 1 };
connection.pipeServer.BeginWrite(connection.buffer, 0,
connection.buffer.Length, SendCallback, connection);
}
//отправка ответа клиенту. пишеться АСИНХРОННО
private void SendCallback(IAsyncResult result)
{
ConnectionInfo connection = (ConnectionInfo)result.AsyncState;
connection.pipeServer.EndWrite(result);
connection.pipeServer.Disconnect();
connection.pipeServer.BeginWaitForConnection(new AsyncCallback(AcceptCallback),
connection.pipeServer);
}
}
class Test
{
static void Main(string[] args)
{
PipeServer pipe = new PipeServer(1);
pipe.Start();
Console.ReadLine();
}
}
}