Как добавить задержки потокобезопасным способом (TcpClient)

Я создаю проект, который будет читать список электронных писем и использую внешние библиотеки, буду искать записи MX для этого домена и использовать telnet, чтобы определить, существует ли это электронное письмо.

До сих пор мне удавалось заставить его работать немного - однако из-за того, как быстро выполняется код, я полагаю, что пытаюсь прочитать ответ сервера быстрее, чем он отправляется. (Я предполагаю это, так как код будет возвращать пустые строки при запуске без отладки, с точками останова он возвращает все нормально.)

Чтобы противодействовать этому, я добавил Thread.Sleep() в области вокруг кода, чтобы я мог дождаться ответа, это работает, но он блокирует поток и перестает отвечать на несколько минут.

Как бы я мог выполнить эту задачу без блокировки пользовательского интерфейса?
Вот код из класса, где проблема -

class TelnetConnection
{
    TcpClient tcpClient;
    List<char> reply;
    public TelnetConnection(string hostname, int port)
    {
        tcpClient = new TcpClient(); 
        tcpClient.BeginConnect(hostname, port, null, null);
        Thread.Sleep(2000);
    }

    public List<char> GetReply()
    {
        reply = new List<char>();
        StringBuilder sb = new StringBuilder();
        while (tcpClient.Available > 0)
        {
            ParseTelnet(sb);
        }
        for (int i = 0; i < sb.Length; i++)
        {
            reply.Add(sb[i]);
        }
        return reply;
    }
    public List<char> Greet(string greeting)
    {
        WriteLine(greeting);
        Thread.Sleep(2000);
        return GetReply();
    }
    public List<char> MailFrom(string mailFrom)
    {
        WriteLine(string.Concat("MAIL FROM: <", mailFrom, ">"));
        Thread.Sleep(2000);
        return GetReply();
    }
    public List<char> RcptTo(string rcptTo)
    {
        WriteLine(string.Concat("RCPT TO: <", rcptTo, ">"));
        Thread.Sleep(2000);
        return GetReply();
    }

    public void WriteLine(string cmd)
    {
        Write(cmd + "\n");
    }

    public void Write(string cmd)
    {
        if (!tcpClient.Connected) return;
        byte[] buf = System.Text.ASCIIEncoding.ASCII.GetBytes(cmd.Replace("\0xFF", "\0xFF\0xFF"));
        tcpClient.GetStream().Write(buf, 0, buf.Length);
    }

    public bool IsConnected
    {
        get { return tcpClient.Connected; }
    }

    // Method from external library
    void ParseTelnet(StringBuilder sb)
    {
            try
            {
                int input = tcpClient.GetStream().ReadByte();
                switch (input)
                {
                    case -1:
                        break;
                    case (int)Verbs.IAC:
                        // interpret as command
                        int inputverb = tcpClient.GetStream().ReadByte();
                        if (inputverb == -1) break;
                        switch (inputverb)
                        {
                            case (int)Verbs.IAC:
                                //literal IAC = 255 escaped, so append char 255 to string
                                sb.Append(inputverb);
                                break;
                            case (int)Verbs.DO:
                            case (int)Verbs.DONT:
                            case (int)Verbs.WILL:
                            case (int)Verbs.WONT:
                                // reply to all commands with "WONT", unless it is SGA (suppres go ahead)
                                int inputoption = tcpClient.GetStream().ReadByte();
                                if (inputoption == -1) break;
                                tcpClient.GetStream().WriteByte((byte)Verbs.IAC);
                                if (inputoption == (int)Options.SGA)
                                    tcpClient.GetStream().WriteByte(inputverb == (int)Verbs.DO ? (byte)Verbs.WILL : (byte)Verbs.DO);
                                else
                                    tcpClient.GetStream().WriteByte(inputverb == (int)Verbs.DO ? (byte)Verbs.WONT : (byte)Verbs.DONT);
                                tcpClient.GetStream().WriteByte((byte)inputoption);
                                break;
                            default:
                                break;
                        }
                        break;
                    default:
                        sb.Append((char)input);
                        break;
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
    }
}

1 ответ

Решение

Удалить все сны. Вы просто не можете знать, сколько времени займет ответ сервера.

Вместо этого согласуйте маркер в данных, отправляемых с сервера клиенту, и отметьте конец ответа.

В GetReplay читайте из потока, пока не встретите маркер, а затем вернитесь.

РЕДАКТИРОВАТЬ, похоже, что метод ParseTelnet пытается сделать что-то вроде этого. Убедитесь, что сервер отправляет то, что вы ожидаете.

Альтернатива состоит в том, чтобы сервер отправлял длину ответа первым, поэтому все, что нужно сделать клиенту, это прочитать это количество байтов.

Такое соглашение называется протоколом.

Чтобы предотвратить блокирование пользовательского интерфейса, выполните отправку и чтение асинхронно, используя методы *Async NetworkStream.

Другие вопросы по тегам