C# 半关闭TCP客户端
我对关闭TCP客户端有严重的问题。我想做的是: 在客户机上:C# 半关闭TCP客户端,c#,.net,tcpclient,C#,.net,Tcpclient,我对关闭TCP客户端有严重的问题。我想做的是: 在客户机上: 发送消息 关闭基础套接字以进行发送 收到答复 关闭底层套接字进行读取(或者,此时,只需关闭它) 在服务器上: 收到消息 关闭底层套接字以进行读取 发送响应 关闭底层套接字以进行写入(或者,此时,只需关闭它) 但是,在客户端或服务器上执行步骤2之后,我无法使用TcpClient的流。 下面是我的代码的一个非常简化的版本(没有异步调用、处理和清理,也没有使用StreamReader和StreamWriter而不是XmlSerializer
有没有办法不用原始套接字就可以做到这一点?有什么地方我弄错了吗?问题是
ReadToEnd
读取数据直到流的末尾。通过发出Client.Shutdown
实际上关闭了套接字,使其无法重用(至少在TCPClient
的情况下)。下面是GetStream()的代码
如您所见,错误是由于套接字关闭而发生的
编辑:这很奇怪,但我想我找到了它不能正常工作的原因。原因是Shutdown
总是将整个套接字的标志设置为断开连接。即使我们实际上并没有这样结束它!如果我们在方法开始时保留流,我们将不会遇到这个问题,因为问题在于检查套接字状态的GetStream
方法。但当其他代码检查套接字的状态时,我们可能会遇到一些错误。失败是什么意思?跳转到原始套接字似乎是不成熟和武断的。更确切地说,修复错误,您就可以了。StreamWriter
拥有传递给它的流的所有权。当它被垃圾收集时,底层流也会被处理。如果您按照预期的方式处理了StreamWriter
(因为它是IDisposable
),您就会清楚地看到这一点。您可以使用使流保持打开状态的构造函数重载。或者,不必为此费心——如果您需要访问TcpClient
的底层套接字,那么您通常是做错了。实际上,不需要使用正确的中止逻辑手动关闭套接字。@JeroenMostert捕捉得很好,但问题不应该轻易显现出来。SR有终结器吗?不应该。此外,关闭至远程侧的can信号流完成。我想关闭receiving没有什么作用,是的。@Jeroenmoster在我使用的实际代码中使用了一个XmlSerializer,它没有实现IDisposable,问题仍然存在。我使用StreamWriter和StreamReader很容易说明我的问题,不过也许我应该离开XmlSerializer。@usr在代码中添加了关于异常的注释。至于关闭接收,这可能是不必要的,但我需要关闭发送,以向另一方发出信号,表明我已完成数据传输(连接需要与假设我在完成后关闭发送的实现兼容)。因此,不可能对TcpClient执行半关闭,那么?太遗憾了。有可能半关闭一个连接。悬而未决的问题是:如果代码看起来是半关闭的,为什么套接字是完全关闭的。另外,您可以在开始时只调用一次GetStream,这将使代码更加清晰@Noctiphobia请这样做并更新代码。@Noctiphobia好的,我已经重写了代码,看起来我可以毫无问题地来回发送数据。无论如何,如果您想将套接字重新用于双向通信,那么显式处理套接字会更安全,我说这是定义的预期行为。我一直认为TCP连接总是意味着两个开放流,每个方向一个。即使我不发送回一个字节,只读取/使用/确认传入字节,返回连接也必须存在,并且从我的代码角度来看是“向上”的。至少我一直这么想。这就是为什么我对最初的问题感到震惊,因为从定义上讲,这是不可能的,即使你可以使用垃圾收集技巧来实现它。就我的两块钱。
//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);
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;
}