Как один поток сервера может обрабатывать несколько клиентов - C#

Узнай цену своей работы

Формулировка задачи:

Есть клиент-серверное приложение, на данный момент 4х потоковое. Моя роль в нем это написание хорошего сервера. Сейчас сервер работает как: 1)Ждёт подключения. 2)Ждёт пока клиент отправит данные. 3)Получает данные. 4)Отправляет ответ и ждёт пока ответ будет получен. 5)Переходит в ожидание следующего подключения клиента (те на пункт 1). Те всё работает в синхронном режиме. В виде кода выглядит так (есть мой класс оболочка для обработки записи и чтения):
        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());
    }
}
Преподавателю это не нравиться, мол фигня это всё что сервер в потоке ждет пока клиент ему что-то пришлет и надо мол чтобы это всё асинхронно работало и чтобы поток обрабатывал другого клиента пока этот не хочет отправлять данные например (ну вот для случая что клиент подключился и висит 30 секунд, а потом уже только данные начинает слать). Вот только саму идею асинхронности я не могу понять. Сейчас я пишу и читаю байтики в/из потока через синхронные функции Read и Write. Я вижу что есть также асинхронные Read и Write, но

я не могу понять саму идею: как один поток сервера может обрабатывать несколько клиентов? Те как это он может пока один клиент висит получить данные от другого и ответить ему?

Объясните пожалуйста, с кодом было бы вообще идеально, но хотя бы на словах.

Решение задачи: «Как один поток сервера может обрабатывать несколько клиентов»

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();
        }
    }
}

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

10   голосов , оценка 4.3 из 5
Похожие ответы