如何同步读/写到Stream?
问候, 我有 C# 中的自定义 Telnet 客户端。我用它来与 IMAP 服务器通信。它的工作原理只是写入命令,然后得到响应。测试代码:
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();
调用上述代码的输出为:
- USER [email protected]
- +确定 欢迎使用 Atmail POP3 服务器 - 使用 user@domain 登录。
- +确定需要密码。
- PASS 密码
- QUIT
- +OK 已登录。
- +OK 再见。
这是一个简单的示例,但它显示了竞争条件问题。第 nr.6 行的输出应出现在 nr.6 行之前。 5
问:如何处理?
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:
- USER [email protected]
- +OK Welcome to the Atmail POP3 server - Login with user@domain.
- +OK Password required.
- PASS password
- QUIT
- +OK logged in.
- +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 ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我过去手动编写过自己的 telnet 客户端。我处理命令的方式是为每个命令编写一个流程,其中包含输入和预期响应(我期望的答复的一部分,在您的情况下+确定)。这将允许我发送命令并等待响应。如果未收到响应或不匹配,则抛出异常。
Telnet 可以为每个命令发送许多响应(空白、页面更改、登录流程等)。因此,等待您的预期回复是必要的。
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.
首先,IMAP 邮件服务器不支持 Telnet。 Telnet 是 TCP 之上的协议。请参阅 RFC 854。然而,Telnet 客户端的优点在于,由于 Telnet 是原始 TCP 套接字之上的一个非常小的协议,因此您可以连接到许多也在 TCP 之上使用文本命令/响应样式协议(POP3、IMAP、 SMTP、HTTP 等)。
最终您可能应该只使用“现成的 IMAP 客户端”。通过 Google 随机搜索,我找到了这个。我不知道这是否好,但它可能会给你带来快速的胜利。
然而,如果您想学习如何正确进行 TCP/IP 网络,则必须了解一些有关双向 IO 的基础知识。
您在这里遇到的情况很正常。 TCP 套接字上有两个通道。一种用于阅读,一种用于写作。这些通道是独立的并且可能被缓冲。
缓冲意味着仅仅因为您在客户端代码中向发送套接字写入了一些数据(一行或其他内容)并不意味着另一端已收到该数据。
因此,在写入后立即阻塞读取可能意味着您的命令永远不会发送,并且您将永远阻塞读取。
此外,您可以按照操作系统和带宽允许的速度向发送套接字写入命令。每当服务器开始处理回复时,回复就会返回。这可能很快,也可能永远不会。从本质上讲,套接字编程是异步的并且容易失败(毕竟您是通过不可靠的网络处理远程系统)。
处理异步双向 IO 的常用方法是使用两个线程。一个用于阅读,另一个用于写作。当您必须协调请求和请求的“聊天”时,“乐趣”就开始了。两个线程之间的响应。像 AutoResetEvent 和 ManualResetEvent 这样的线程原语可以在这里提供帮助。尽管您可能最好将 C# Reactive Extensions 视为他们可以使工作变得更加简单。
总的来说,我建议阅读该主题。即使对于像 SMTP 和 SMTP 这样的“简单”协议,编写良好的无错误网络代码也并非易事。 POP3。
如果您的解决方案不限于使用 C#,而只是尝试自动化一些简单的 Telnet 风格的服务交互,那么您应该查看 Linux/Unix 实用程序 expect,它可以节省你很多时间。
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.