views:

55

answers:

2

Greetings, I've got custom Telnet client in C#. I am using it to communicate with IMAP servers. It works as just write command, then get response. Test code:

            Telnet tc = new Telnet();
            // setup server credentials
            const string server = "pop.nexlink.net";
            const int port = 140;
            const int timeout = 70;
            // establish server connection
            tc.Setup(server, port, timeout);
            // test initial server response
            Console.WriteLine(tc.output);

            // set-up a few commands to send
            var array = new[] { "USER [email protected]", "PASS password", "QUIT" };

            // while connected to server
            if (tc.IsConnected)
            {
                foreach (string str in array)
                {
                    // Show command on console
                    Console.WriteLine(str);
                    // Write to Stream
                    tc.WriteLine(str);
                    // Read from Stream
                    tc.Read();
                    // Test the Stream output
                    Console.Write(tc.output);
                }


            }
            // close connection
            tc.Disconnect();

Output from calling above code is:

  1. USER [email protected]
  2. +OK Welcome to the Atmail POP3 server - Login with user@domain.
  3. +OK Password required.
  4. PASS password
  5. QUIT
  6. +OK logged in.
  7. +OK Bye-bye.

This is a simplistic example, however it shows a problem of race condition. Output from line nr.6 should appear before nr. 5

Q: How to handle that ?

A: 

I've manually written my own telnet clients in the past. The way I handled commands was to write a flow for each command, with inputs and expected responses (part of the reply that I was expecting, in your case +OK). This would allow me to send commands and wait for the response. If response was not received or did not match, then throw exception.

Telnet can send many responses per command (blank, page changes, logon flow, etc.). So waiting for your expected response is necessary.

CkH
That will not work here. There are many servers that this program must work for (i am in ISP business). More general solution is welcomed. I was thinking about multithreading and locking the resources but no idea how to do that.
Protos
Not seeing how multi server environment will not work with this approach. Please explain... You are still only connected to one Telnet Instance at a time. Right? After I encapsulate all commands I need, I expose the Telnet facade as a WCF service and manage a pool of connections to the Telenet server.
CkH
+1  A: 

Firstly, an IMAP mail server doesn't speak Telnet. Telnet is a protocol on top of TCP. See RFC 854. The virtue of Telnet clients however is that it as Telnet is a very minimal protocol on top of raw TCP sockets you can therefore connect to and interact with many services that also use text command/response style protocols on top of TCP (POP3, IMAP, SMTP, HTTP, etc).

Ultimately you should probably just use a "off the shelf IMAP client". Doing some random Google searching lead me to this one. I have no idea if it is good but it may lead to quick wins on your part.

If however you want to learn how to do TCP/IP networking properly you have to realise some fundamentals about bidirectional IO.

What you are experiencing here is quite normal. There are two channels on a TCP socket. One for reading, one for writing. These channels are independent and potentially buffered.

Buffering means that just because you wrote some data (a line, or whatever) to the sending socket in your client code doesn't mean it has been received at the other end.

Therefore immediately blocking on a read after a write may mean that your command is never sent and you will block on the read forever.

Also, you can write commands to the sending socket as fast as your OS and bandwidth will let you. The replies will come back whenever the server gets to processing them. Which might be soon, or maybe never. By its very nature socket programming is asynchronous and prone to failure (you are dealing with a remote system over an unreliable network afterall).

The usual approach with dealing with async bidirectional IO is to have two threads. One for reading, and another for writing. The "fun" starts when you have to coordinate the "chat" of request & response between the two threads. Threading primitives like AutoResetEvent and ManualResetEvent can help here. Though you would probably do best to look at the C# Reactive Extensions as they could make the job a lot simpler.

Overall I'd suggest reading up on the topic. Writing good error free network code is non trivial even for "simple" protocols like SMTP & POP3.

If you're not tied to using C# for your solution and you are just trying to automate some simple Telnet style service interactions then you should look at the Linux/Unix utility expect, it could save you a lot of time.

orj
Thank you, I will definitely check all of the above.
Protos