半关闭 TcpClient
Half-closing TcpClient
我在半关闭 TcpClient 时遇到严重问题。我想做的是:
在客户端:
- 发送消息
- 关闭用于发送的底层套接字
- 收到回复
- 关闭用于读取的底层套接字(或者,此时,直接关闭它)
在服务器上:
- 收到消息
- 关闭底层套接字进行读取
- 发送回复
- 关闭用于写入的底层套接字(或者,此时,直接关闭它)
但是,在客户端或服务器上执行第 2 步之后,我无法使用 TcpClient 的流。
这是我的代码的一个非常简化的版本(没有异步调用、处理和清理,还使用 StreamReader 和 StreamWriter 而不是 XmlSerializer):
//initialize the connection between the server and the client
var listener = new TcpListener(IPAddress.Any, 13546);
listener.Start();
var client = new TcpClient("127.0.0.1", 13546);
var server = listener.AcceptTcpClient();
listener.Stop();
//CLIENT: send the message
var cwriter = new StreamWriter(client.GetStream());
cwriter.Write("client's message");
cwriter.Flush();
client.Client.Shutdown(SocketShutdown.Send);
//SERVER: receive the message
string msg;
var sreader = new StreamReader(server.GetStream());
msg = sreader.ReadToEnd();
server.Client.Shutdown(SocketShutdown.Receive);
//SERVER: send a response
//Here the code fails on server.GetStream() -
//InvalidOperationException, apparently the whole connection is closed now
var swriter = new StreamWriter(server.GetStream());
swriter.Write(msg + " with server's response");
swriter.Flush();
server.Client.Shutdown(SocketShutdown.Send);
//CLIENT: receive the message
var creader = new StreamReader(client.GetStream());
var response = creader.ReadToEnd();
client.Client.Shutdown(SocketShutdown.Receive);
有什么方法可以不使用原始套接字来做到这一点吗?有什么我弄错了吗?
问题是 ReadToEnd
读取数据直到流结束。通过发出 Client.Shutdown
你实际上关闭了套接字使得它不可能被重用(至少在 TCPClient
的情况下)。这是 GetStream()
的代码
public NetworkStream GetStream() {
if(Logging.On)Logging.Enter(Logging.Sockets, this, "GetStream", "");
if (m_CleanedUp){
throw new ObjectDisposedException(this.GetType().FullName);
}
if (!Client.Connected) {
throw new InvalidOperationException(SR.GetString(SR.net_notconnected));
}
if (m_DataStream==null) {
m_DataStream = new NetworkStream(Client, true);
}
if(Logging.On)Logging.Exit(Logging.Sockets, this, "GetStream", m_DataStream);
return m_DataStream;
}
如您所见,错误是由于套接字关闭造成的。
编辑:这很奇怪,但我想我找到了它不能正常工作的原因。原因是 Shutdown
总是将整个套接字的标志设置为断开连接。即使我们实际上并没有那样关闭它!如果我们在方法的开头保留流,我们将不会遇到这个问题,因为问题出在检查套接字状态的 GetStream
方法中。但是当其他代码检查套接字的状态时,我们可能会遇到一些错误。
我在半关闭 TcpClient 时遇到严重问题。我想做的是:
在客户端:
- 发送消息
- 关闭用于发送的底层套接字
- 收到回复
- 关闭用于读取的底层套接字(或者,此时,直接关闭它)
在服务器上:
- 收到消息
- 关闭底层套接字进行读取
- 发送回复
- 关闭用于写入的底层套接字(或者,此时,直接关闭它)
但是,在客户端或服务器上执行第 2 步之后,我无法使用 TcpClient 的流。 这是我的代码的一个非常简化的版本(没有异步调用、处理和清理,还使用 StreamReader 和 StreamWriter 而不是 XmlSerializer):
//initialize the connection between the server and the client
var listener = new TcpListener(IPAddress.Any, 13546);
listener.Start();
var client = new TcpClient("127.0.0.1", 13546);
var server = listener.AcceptTcpClient();
listener.Stop();
//CLIENT: send the message
var cwriter = new StreamWriter(client.GetStream());
cwriter.Write("client's message");
cwriter.Flush();
client.Client.Shutdown(SocketShutdown.Send);
//SERVER: receive the message
string msg;
var sreader = new StreamReader(server.GetStream());
msg = sreader.ReadToEnd();
server.Client.Shutdown(SocketShutdown.Receive);
//SERVER: send a response
//Here the code fails on server.GetStream() -
//InvalidOperationException, apparently the whole connection is closed now
var swriter = new StreamWriter(server.GetStream());
swriter.Write(msg + " with server's response");
swriter.Flush();
server.Client.Shutdown(SocketShutdown.Send);
//CLIENT: receive the message
var creader = new StreamReader(client.GetStream());
var response = creader.ReadToEnd();
client.Client.Shutdown(SocketShutdown.Receive);
有什么方法可以不使用原始套接字来做到这一点吗?有什么我弄错了吗?
问题是 ReadToEnd
读取数据直到流结束。通过发出 Client.Shutdown
你实际上关闭了套接字使得它不可能被重用(至少在 TCPClient
的情况下)。这是 GetStream()
public NetworkStream GetStream() {
if(Logging.On)Logging.Enter(Logging.Sockets, this, "GetStream", "");
if (m_CleanedUp){
throw new ObjectDisposedException(this.GetType().FullName);
}
if (!Client.Connected) {
throw new InvalidOperationException(SR.GetString(SR.net_notconnected));
}
if (m_DataStream==null) {
m_DataStream = new NetworkStream(Client, true);
}
if(Logging.On)Logging.Exit(Logging.Sockets, this, "GetStream", m_DataStream);
return m_DataStream;
}
如您所见,错误是由于套接字关闭造成的。
编辑:这很奇怪,但我想我找到了它不能正常工作的原因。原因是 Shutdown
总是将整个套接字的标志设置为断开连接。即使我们实际上并没有那样关闭它!如果我们在方法的开头保留流,我们将不会遇到这个问题,因为问题出在检查套接字状态的 GetStream
方法中。但是当其他代码检查套接字的状态时,我们可能会遇到一些错误。