Conexão TELNET no LATROMI

Boa noite a todos.

Atualmente na minha empresa trabalhamos com um sistema legado desenvolvido em IBM S400.

Gostaria de saber se é possível se conectar com esse sistema via TELNET.

Poderia me tirar essa dúvida?

Olá @Framos

Essa comunicação é bem difícil de implementar, pois não é possível manter uma conexão aberta de TELNET entre as requisições do Formulário (request / response / request …)

Você teria que implementar a toda a lógica de comunicação de forma atômica, ou seja, quando ocorrer um evento do Formulário, você precisa abrir a conexão Telnet, fazer tudo que precisa e fechar a conexão depois.

Este processo pode envolver vários comandos e respostas. Por exemplo, se o servidor exige autenticação, poderia ser algo assim:

  1. Conecta no servidor.

  2. Aguarda o servidor pedir “username”

  3. Informa o nome de usuário

  4. Aguarda o servidor pedir “password”

  5. Informa a senha

  6. Aguarda o servidor pedir um comando (prompt)

  7. Envia um comando

  8. Lê a resposta

Além disso, é preciso levar em consideração que cada servidor poderá ter uma codificação de texto diferente (UTF8, ASCII…) e formas diferentes de solicitar os comandos (poder ser qualquer caractere ou palavra chave definida pelo servidor).


Eu implementei um exemplo com o servidor Telehack, para testar a comunicação. O exemplo pode ser conferido neste link.

Neste servidor, o prompt para comandos é um “.” (ponto) e a codificação é ASCII.

OBS.: Eu recomendo que você teste o seu código em um projeto Console do Visual Studio antes de colocá-lo em uso no LATROMI. Assim você pode depurar e testar com mais facilidade.

Para implementar este exemplo basta criar um Formulário com os campos txtCommand e txtResult do tipo TextBox, um botão. No evento Click do botão, executar o código C# a seguir:

using System.IO;
using System.Net.Sockets;
using System.Text;

var hostname = "telehack.com";
var port = 23;
var returnKey = "\r\n";
var command = (string)Fields["txtCommand"].Value;

StringBuilder output = new StringBuilder();

string readedLine;

// Faz a leitura da linha de retorno  telnet.
Func<StreamReader, string> ReadTelnetLine = sr =>
{
    int n;
    StringBuilder line = null;
    while ((n = sr.Read()) > 0)
    {
        if (line == null) line = new StringBuilder();

        // Consome as quebras de linhas
        if (System.Environment.NewLine[0] == (char)n)
        {
            if (System.Environment.NewLine.Length == 2)
            {
                if (System.Environment.NewLine[1] == sr.Peek())
                    sr.Read();
                break;
            }
            else
            {
                break;
            }
        }

        line.Append((char)n);

        if (sr.Peek() == -1)
            break;
    }
    return line?.ToString();
};

// Use o encoding correspondente ao servidor telnet
Encoding encoding = Encoding.ASCII;

using (var client = new TcpClient())
{
    client.Connect(hostname, port);

    using (NetworkStream networkStream = client.GetStream())
    {
        // IMPORTANTE: Sempre defina um Timetout para evitar que aplicação trave
        networkStream.ReadTimeout = 5000;
        
        bool quit = false;
        using (var writer = new StreamWriter(networkStream, encoding) { AutoFlush = true })
        using (var reader = new StreamReader(networkStream, encoding, true))
        {
            while ((readedLine = ReadTelnetLine(reader)) != null)
            {
                // Detecta linha de prompt
                if (readedLine.StartsWith(((char)27).ToString()) && readedLine.EndsWith("."))
                {
                    readedLine = ".";
                }
                
                output.AppendLine(readedLine);

                switch (readedLine.Trim())
                {
                    // Prompt
                    case ".":
                        if (string.IsNullOrEmpty(command))
                            quit = true;
                        else
                        {
                            writer.Write(command + returnKey);
                            System.Threading.Thread.Sleep(500);
                            command = "";
                        }
                        break;

                    
                    default:
                        break;
                }

                if (quit)
                {
                    writer.Write("quit" + returnKey);
                    break;
                }
            }
        }
    }
}

Fields["txtResult"].Value = output.ToString();

Considerações Finais

  • Não deixe de definir o ReadTimeout do NetworkStream. Isso evita que o site fique travado caso ocorra algum erro. Informe um valor em milisegundos.

  • Se executar StreamReader.Read() após chegar ao fim da resposta que o servidor tem para oferecer, ocorrera um erro do tipo System.IO.IOException com a mensagem:

    ‘Não é possível ler os dados da conexão de transporte: Uma tentativa de conexão falhou porque o componente conectado não respondeu
    corretamente após um período de tempo ou a conexão estabelecida falhou
    porque o host conectado não respondeu.’

    Por isso é importante interceptar no momento certo as “perguntas” que o servidor irá fazer, e não deixar que a leitura prossiga a partir deste ponto. Por exemplo, se o servidor pedir por um comando com um “.” (ponto) e depois disso ocorrer uma leitura de StreamReader.Read(), vai ocorrer o erro, pois o servidor está esperando um comando (que deve ser enviado com StreamWriter.Write(...)).

    Este erro também irá ocorrer se o servidor exceder o limite de tempo estipulado do ReadTimeout.

2 curtidas

@daniel.giacomelli muito obrigado por responder, vou fazer o teste no visual Studio para ver se consigo.