2

As it's not specified that Close method is thread safe I should call it from the lock. How can I be sure that it will not block my thread? Should I disable LingerState or can it be enabled?

btw do I need to call both TcpClient.Close and TcpClient.Client.Close?

Vlad
  • 3,001
  • 1
  • 22
  • 52

1 Answers1

3

According to the documentation for TcpClient.Close, calling this method disposes the instance. A quick look in ILSpy reveals that it simply calls Dispose, which calls Dispose(true).

Furthermore, the documentation states that calling Close also disposes of the underlying Socket.

In fact, before disposing of the Socket, it calls Shutdown(SocketShutdown.Both).

Given that .NET sockets are wrappers for Windows sockets, the shutdown function is also called, and its documentation states:

Note The shutdown function does not block regardless of the SO_LINGER setting on the socket.

However, the Socket overloads its Close method to accept a timeout parameter and similar to TcpClient, the Socket.Close method calls Socket.Dispose(true), which uses the timeout value to determine whether or not it may block.

According to the documentation for Socket.Close(int):

If you need to call Close without first calling Shutdown, you can ensure that data queued for outgoing transmission will be sent by setting the DontLinger option to false and specifying a non-zero time-out interval. Close will then block until this data is sent or until the specified time-out expires. If you set DontLinger to false and specify a zero time-out interval, Close releases the connection and automatically discards outgoing queued data.

Apparently, neither TcpClient or Socket expose the timeout value to you as a settable property and the default value is not 0; therefore, it would seem that to avoid blocking you must call Socket.Close(0) yourself prior to calling TcpClient.Close.

Keep in mind that Close can also throw a SocketException, despite the Framework Design Guideline's recommendation to never throw in a call to Dispose. Perhaps that's one of the reasons why it's named Close and TcpClient explicitly implements the Dispose method.

Dave Sexton
  • 2,562
  • 1
  • 17
  • 26
  • I checked that if I use TcpClient.Client.Close(1000) it blocks. But if I set TcpClient.LingerState (true, 1000) it doesn't block when I call TcpClient.Close() or TcpClient.Client.Close() without arguments. Does it mean that I can still use linger option but avoid blocking (simple closing TcpClient with Close())? – Vlad Sep 12 '14 at 18:09
  • When you tested, was there data in the buffer that wasn't sent yet? – Dave Sexton Sep 12 '14 at 20:09
  • It seems to me that unless you explicitly specify Close(0) there's a chance that it will block. – Dave Sexton Sep 12 '14 at 20:12
  • http://msdn.microsoft.com/en-us/library/system.net.sockets.lingeroption(v=vs.110).aspx > The IP stack computes the default IP protocol time-out period to use based on the round trip time of the connection. In most cases, the time-out computed by the stack is more relevant than one defined by an application. This is the default behavior for a Socket when the LingerState property is not set and for a TcpClient when the LingerState property is not set. – Dave Sexton Sep 12 '14 at 20:44
  • 1) "was there data in the buffer that wasn't sent yet" - there was a data that wasn't received on another side (no Receive call). I'm not sure whether I can control it to be "not sent". Hm.. I've rechecked it. It seems like it doesn't matter if data was received - it still blocks after Receive(). 2) that documentation says that the data will be sent when closing the socket but it does not say that it's done in a blocking way... – Vlad Sep 12 '14 at 22:35
  • Found out it. I should disable linger: "SO_LINGER controls the action taken when unsent messages are queued on socket and a close(2) is performed. The system will block the process on the close(2) attempt until it is able to transmit the data or a timeout period. If SO_LINGER is disabled and a close(2) is issued, the system will process the close in a manner that allows the process to continue as quickly as possible." – Vlad Sep 12 '14 at 22:51
  • I looked in ILSpy. It literally compares the closing timeout to 0 and if true it disposes of the socket (handle) and does nothing else; otherwise, it may follow a path that ends up looping on (!this.m_Released) and doing a Thread.SpinWait(1). Regardless, I think the documentation is very clear. It discusses blocking on Close and it provides an overload with a timeout. What more do you expect? E.g., see also [closesocket function](http://msdn.microsoft.com/en-us/library/windows/desktop/ms737582(v=vs.85).aspx). – Dave Sexton Sep 12 '14 at 22:51
  • Great, which documentation was that? – Dave Sexton Sep 12 '14 at 22:53
  • FreeBSD Man, mentioned in the second answer of http://stackoverflow.com/questions/6418277/how-to-close-a-non-blocking-socket – Vlad Sep 12 '14 at 22:57
  • FYI, [linger is disabled by default](http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.lingerstate(v=vs.110).aspx). I believe you should only have to call Close(0), as I stated previously. – Dave Sexton Sep 12 '14 at 22:58