Страницы

Поиск по вопросам

вторник, 10 декабря 2019 г.

Многопоточный прием данных

#c_sharp #многопоточность #smtp


Есть СМТП-сервис. Он слушает 25ый порт:

SMTP_Listener = new TcpListener(IPAddress.Any, 25); 
SMTP_Listener.Start();


Получается, что когда к нему шлют команды 2 и более клиентов, то они "сливаются"
и получается такое (пример):


  
  Клиент1: команда "HELLO 11111" 
  Клиент2: команда "HELLO 22222"
  Клиент3: команда "HELLO 33333"
  


Я получаю "HELLO 11" от первого клиента, от второго- "LO 22", для третьего - "LO
3333" (например).

Подскажите, как правильно разделить чтение от нескольких клиентов?

У меня каждый клиент работает в отдельном потоке.

КОД приема сообщений от клиентов:

/// 
/// Считываем команду клиента
/// 
/// 
private string ReadLine() {
    try {
        long lastDataTime = DateTime.Now.Ticks;
        ArrayList lineBuf = new ArrayList();
        byte prevByte = 0;

        while (true) {
            if (clientSocket == null) l.Write(" !!! clientSocket is NULL");

            if (!clientSocket.Connected) l.Write(" !!! сокет не включен!");

            if (clientSocket.Available > 0) {
                // Read next byte
                byte[] currByte = new byte[1];
                int countRecieved = clientSocket.Receive(currByte, 1, SocketFlags.None);

                // Count must be equal. Eg. some computers won't give byte at first
read attempt
                if (countRecieved == 1) {
                    lineBuf.Add(currByte[0]);

                    // Line found
                    if ((prevByte == (byte)
                    '\r' && currByte[0] == (byte)
                    '\n')) {
                        byte[] retVal = new byte[lineBuf.Count - 2]; // Remove  
                        lineBuf.CopyTo(0, retVal, 0, lineBuf.Count - 2);

                        return System.Text.Encoding.Default.GetString(retVal).Trim();
                    }

                    // Store byte
                    prevByte = currByte[0];
                    //l.Write("prevByte = " + prevByte);
                    //// Check if maximum length is exceeded
                    //if (lineBuf.Count > maxLen)
                    //{
                    //    throw new Exception( "Maximum line length exceeded");
                    //}

                    // reset last data time
                    lastDataTime = DateTime.Now.Ticks;
                    //l.Write("lastDataTime = " + lastDataTime);
                }
            } else {
                // l.Write("clientSocket.Available: " + clientSocket.Available.ToString());
// тут всегда 0
                //---- Time out stuff -----------------------//
                if (DateTime.Now.Ticks > lastDataTime + ((long)(60000)) * 10000) {
                    l.Write("ERROR: Read timeout: " + (((long)(60000)) * 10000).ToString());

                    //clientSocket.Close();
                    l.Write("Сокет закрыт из-за простоя в " + (((long)(60000)) *
10000).ToString() + " м-сек.");

                    // throw new Exception("Read timeout");
                    return ""; // ?????????????????????????????????????????????????????????????????????
                }
                System.Threading.Thread.Sleep(100);
                //------------------------------------------//
            }
        }

    } catch (Exception x) {
        l.Write("EROR ReadLine(): " + x.ToString());
        throw new Exception(x.Message);
    }
}



  UPD:


 public void Listen()
        {
            int iter = 0;

            try
            {
                l.Write("***************START*******************");
                l.Write("SMTP server started " + DateTime.Now.ToString());

                SMTP_Listener = new TcpListener(IPAddress.Any, port); 
                SMTP_Listener.Start();


                while (true)
                {

//тут я определяю что у нас есть клиент
                    clientSocket = SMTP_Listener.AcceptSocket();                

                    _sessionId = clientSocket.GetHashCode().ToString();

                    _email.sessionId = Convert.ToInt32(_sessionId);

                    l.Write("New session: " + _sessionId);

//и в отдельный поток его
                    Thread newClient = new Thread(StartProcessing);

                    l.Write("Создан поток для обработки клиента " + _sessionId);
                    UserSessionController.AddSession( Convert.ToInt32(_sessionId ) );

                    newClient.Name = _sessionId;
                    newClient.IsBackground = true; // ???
                    newClient.Start();          
                } 

            }
            catch (Exception ex)
            {
                l.Write("SMTP Listen Error: " + ex.ToString());
                throw;
            }
        }

    


Ответы

Ответ 1



private Socket clientSocket у вас один общий экземпляр сокета для всех клиентов. когда подключается новый клиент clientSocket = SMTP_Listener.AcceptSocket(); вы выбрасываете сокет, который был открыт при подключении предыдущего клиента. В результате все потоки у вас читают из this.clientSocket, ссылающегося на сокет, открытый для последнего подключенного клиента. Т.е. все, что прислали предыдущие клиенты, но что не успели вычитать - уходит вникуда. А все, что присылает последний клиент, рвут между собой все читающие потоки. Вам стоит перенести код, связанный с обработкой клиента, в отдельный класс, чтобы получить отдельный экземпляр на каждого клиента: class ClientProcessor() { private Socket clientSocket; public ClientProcessor(Socket clientSocket) { this.clientSocket = clientSocket; } string ReadLine() {....} void StartProcessing(); } и создавать новый экземпляр для каждого подключившегося клиента: var processor = new ClientProcessor(SMTP_Listener.AcceptSocket()); Thread newClient = new Thread(processor.StartProcessing);

Комментариев нет:

Отправить комментарий