【问题标题】:TcpClient: How do I close and reconnect it again?TcpClient:如何关闭并重新连接?
【发布时间】:2017-01-17 00:52:43
【问题描述】:

您好,感谢您的帮助。 这次想问一下TcpClient。 我有一个服务器程序,我正在编写一个客户端程序。 此客户端使用 TcpClient。首先创建一个新客户端

clientSocket=new TcpClient(); 

(顺便说一句,这会导致异常吗?以防万一我把它放在 try-catch 中,但我不确定这是否真的有必要) 无论如何,稍后我进入一个循环,在这个循环中我连接到服务器

clientSocket.Connect("xx.xx.xx.xx",port);

然后我用

创建一个 NetworkStream
clientStream=clientSocket.GetStream();

然后开始通过Read等待来自服务器的数据。我知道这是阻塞的,所以我还设置了 ReadTimeOut(比如 1 秒)

无论如何,到目前为止一切都很好。 稍后,如果我没有从服务器收到任何东西,我会尝试向它发送一些东西。如果这种情况持续发生 3 次,我想关闭连接并再次重新连接到服务器

(请注意,一个完全不同的问题是当服务器以某种方式关闭时,会导致客户端出现其他类型的错误——也许我稍后会问这个问题)

那么,我该怎么办?

 if(clientSocket.Connected)
            {
                Console.WriteLine("Closing the socket");
                clientSocket.Close();
            }

我关闭了套接字。 循环结束,所以我再次回到开头并尝试连接到服务器。

clientSocket.Connect("xx.xx.xx.xx",port);

但是这会导致错误(实际上是未处理的异常)“无法访问已处置的对象”

所以我的问题是 我怎样才能再次关闭并重新连接到服务器?

再次感谢您的帮助

【问题讨论】:

  • 谢谢。我添加了一个标签。那样可以么? (顺便说一下,我如何在 cmets 中添加新行...如果我按 enter 只需添加评论。尝试 ctrl-enter, shift-enter ,不起作用...
  • 是的,这很棒。谢谢!注释不支持段落或回车。它们始终是单个文本块。它们是 cmets,而不是段落。 :-) 很少(如果有的话)需要足够长的评论以需要多个段落。
  • 只有在明确知道要捕获哪个异常并且可以有意义地处理它时,才应该将代码放在try/catch 块中。仅捕获异常是一种反模式。见blogs.msdn.microsoft.com/ericlippert/2008/09/10/…

标签: c# tcpclient


【解决方案1】:

TcpClient 实例只能用于连接一次。您可以简单地实例化一个新的TcpClient,而不是尝试重新打开一个已关闭的。

【讨论】:

  • 谢谢。是这样吗?所以我必须实例化一个新的......在 C++ 的一些代码中,我让他们使用套接字,他们只是“创建”一个新的套接字(他们正在使用异步)。但是在 TcpClient 中没有“创建”,所以我想应该只使用一个新的,对吧?顺便说一句,我有另一个相关的问题,我会在解决这个问题后发布。也将感谢您对此的帮助:)
  • 顺便问一下,实例化一个新的 TcpClient 会引发异常吗?我应该把它放在 try-catch 中吗?
  • 它似乎正在工作:) 顺便说一句,当我关闭 TcpClient 时,我想所有资源都被释放了?或者也许垃圾收集器得到它?我不想在每次执行新的实例化迭代时都遗漏一些东西.....
  • 我不认为构造函数可以抛出异常。 TcpClient 实现了IDisposable,这意味着当你在它上面调用Dispose 时,它会自行清理。使用IDisposable 的约定是使用using 语句。见msdn.microsoft.com/en-AU/library/yh598w02.aspx
  • “我认为构造函数不能抛出异常” -- 不正确。构造函数当然可以抛出异常。 TcpClient 在构造过程中创建其底层的Socket 对象,如果原生套接字对象创建失败,Socket constructor 会抛出一个SocketException
【解决方案2】:

the other answer 中所述,TcpClient 对象只能连接一次。如果你想重新连接到服务器,你必须创建一个新的TcpClient对象并再次调用Connect()

也就是说,您的问题中有一些明显的误解:

  1. 首先也是最重要的一点,如果您有任何再次尝试使用TcpClient 对象的意图,则不应使用ReceiveTimeout,例如向服务器发送一些数据。一旦超时期限到期,底层套接字就不再可用。

    如果您想在服务器没有向您发送数据时定期向服务器发送数据,您应该使用异步 I/O (无论如何你都应该这样做,尽管有学习曲线)并使用一个常规的计时器对象来跟踪你从服务器接收到数据以来的时间。
  2. TcpClient 构造函数当然可以抛出异常。至少,any 尝试new 引用类型对象可能会抛出OutOfMemoryException,而在TcpClient 的情况下,它最终会尝试创建本机套接字句柄,这也可能

    虽然所有 I/O 对象和方法都可能引发异常,但您应该只捕获您有办法优雅处理的异常。因此,在将try/catch 块添加到代码之前,请确定在发生异常时要执行的操作,以确保您的代码不会损坏任何数据并继续正确运行。通常不可能优雅地处理OutOfMemoryException(在任何情况下保护new 的所有使用都是不切实际的),但您当然可以捕获SocketException,它可能被构造函数抛出。如果抛出该异常,您应该立即放弃创建和使用TcpClient 的尝试,并向用户报告错误,以便他们可以尝试纠正阻止创建套接字的任何问题。
  3. 如果您的服务器预计会向您发送数据,但您没有收到它,那么关闭连接并重试不太可能改善这种情况。这只会导致服务器上的额外负载,使其更有可能无法响应。同样,一遍又一遍地发送相同的数据。您应该请求一次,尽可能等待服务器的响应,如果在所需时间内没有收到响应,请将错误报告给用户并让他们决定做什么

    请注意,在这种情况下,您可以使用 ReceiveTimeout 属性,因为如果您没有及时得到响应,您将只能放弃连接很好。

【讨论】:

  • 感谢您的建议。我想您是在谈论 ReadTimeout 属性?
  • @KansaiRobot:TcpClient 上的相关属性是ReceiveTimeout。但是,是的,这相当于NetworkStream.ReadTimeout。两者最终都只是用SocketOptionName.ReceiveTimeout 调用Socket.SetSocketOption()
  • @PeterDuniho 我不同意你的观点 3。TCP 连接可能而且确实会中断......服务器可能会重新启动,从 Azure 到你的 ISP 到你的本地网络的任何地方都可能存在网络故障。否则,这是一个很好的答案,谢谢
【解决方案3】:

很简单:

client.Close();
client = new TcpClient();
client.Connect(host, port);

【讨论】:

    猜你喜欢
    • 2010-09-30
    • 2010-11-26
    • 2019-03-15
    • 2011-09-05
    • 2020-10-28
    • 2011-08-20
    • 2017-02-08
    • 1970-01-01
    相关资源
    最近更新 更多