1

I'm looking for some assistance with shutting down a listening socket please. The problem I have is that when I call Shutdown() just before Close(), one of these two calls seems to trigger a bogus read on the socket of the last data received. How can I stop that please?

My app has two pairs of sockets, rather like FTP. One is a client that it connects to a remote server, and one is a server that listens and accepts a second connection from the remote host. This inbound connection is wired up to an asynch OnReceived to handle the unsolicited data coming down form the remote host.

This all works fine and both sockets can remain connected for days or weeks. But if something goes wrong then I react by shutting down everything and starting again. During the call to either inboundSocket.Shutdown() or inbounSocket.Close() (not sure which, it's hard to debug in a second thread), it's as if I am re-reading the last inboudn packet on the inbound socket. This then causes even more problems.

How can I shutdown. close, disconnect, etc etc without forcing this re-read?

Sample code below, stripped down to show the nitty gritty.

Thanks in advance.

Daniel

public class TcpIpSenderReceiver { /// <summary> /// Main thread, main entry point /// </summary> public void ConnectAndLogonAndStartReceivingInboundMessages() { CreateListenerAndStartListening(); AcceptInboundSocketAndHookUpDataReceiptCallBack(); } /// <summary> /// Main thread /// </summary> int CreateListenerAndStartListening() { tcpListener = new TcpListener(LocalBoundIpAddress, listeningPort); tcpListener.Start(); } /// <summary> /// SECOND thread /// </summary> void AcceptInboundSocketAndHookUpDataReceiptCallBack() { int i = 0; while (i < 100 && !tcpListener.Pending()) { i++; Thread.Sleep(100); } inboundSocket = tcpListener.AcceptSocket(); bufferForReceive = new byte[bufferSize]; WireUpCallbackForAsynchReceive(); } /// <summary> /// SECOND thread /// </summary> void WireUpCallbackForAsynchReceive() { if (asynchCallbackFunctionForReceive == null) { asynchCallbackFunctionForReceive = new AsyncCallback(OnDataReceived); } if (inboundSocket.Connected) { try { asyncResultForReceive = inboundSocket.BeginReceive(bufferForReceive, 0, bufferForReceive.Length, SocketFlags.None, asynchCallbackFunctionForReceive, null); } catch (Exception) { //... } } } /// <summary> /// SECOND thread /// </summary> void OnDataReceived(IAsyncResult asyn) { // Read data call goes here..... if (asyncResultForReceive != null) { inboundSocket.EndReceive(asyncResultForReceive); } WireUpCallbackForAsynchReceive(); // listen again for next inbound message } void Shutdown() { shouldAbortThread = true; asyncResultForReceive = null; asynchCallbackFunctionForReceive = null; if (outboundStreamWriter != null) { try { outboundStreamWriter.Close(); outboundStreamWriter.Dispose(); outboundStreamWriter = null; } finally { } } if (outboundNetworkStream != null) { try { outboundNetworkStream.Close(); outboundNetworkStream.Dispose(); outboundNetworkStream = null; } finally { } } if (tcpClient != null) { try { tcpClient.Close(); tcpClient = null; } catch (SocketException) { // ... } } if (inboundSocket != null) { try { // I think this is where it's puking inboundSocket.Shutdown(SocketShutdown.Both); inboundSocket.Close(); inboundSocket = null; } catch (SocketException) { //... } } if (tcpListener != null) { try { tcpListener.Stop(); tcpListener = null; } catch (SocketException) { //... } } } #region Local variables volatile bool shouldAbortThread; TcpListener tcpListener; TcpClient tcpClient; StreamWriter outboundStreamWriter; NetworkStream outboundNetworkStream; Socket inboundSocket = null; IAsyncResult asyncResultForReceive; public AsyncCallback asynchCallbackFunctionForReceive; byte[] bufferForReceive; static string HostnameShared; #endregion } 
1
  • 1
    Assuming that sockets are buggy for basic functionality is usually wrong... Your code is broken, not theirs. Commented Mar 19, 2013 at 16:03

2 Answers 2

1

In OnDataReceived it looks (from the comment, and the fact that you ignore the return value of EndReceive) that you're processing the data in the buffer before having called EndReceive on inboundSocket. If this is the case, you have no indication of the number of bytes that were actually read from the socket (which would likely be 0 if the socket is being shut down), and so you're processing the data still in the buffer from the previous read, hence the appearance of re-reading old data.

You need to call EndReceive before attempting to process any data. Something like:

void OnDataReceived(IAsyncResult asyn) { var bytesRead = inboundSocket.EndReceive(asyn); if (bytesRead == 0) return; // Socket is closed // Process the data here WireUpCallbackForAsynchReceive(); // listen again for next inbound message } 

Furthermore, the fact that you weren't checking the return value of EndReceive suggests that you're expecting each receive to return either an entire buffer's worth of data, or perhaps the same number of bytes as were sent by the server in it's call to Send/BeginSend, which is not always the case.

Sign up to request clarification or add additional context in comments.

1 Comment

Iridium, yes, that's right, I am reading and processing data before EndReceive. Many thanks for your clear explanation, I will give that a try.
0

I got around this as follows. Instead of providing a callback function and letting the framework handle the second thread, I explicitly started my own worker thread and let it block until data is received. If zero bytes are received, I know that the other end is shutting down and can terminate my end nicely. The only gotcha I wanted to share was that I needed to try-catch around the blocking socket.Receive() function and handle a SocketError.Interrupted exception. This occurs when the main program is terminated.

Code looks a bit like this:

 Thread dataReceivedWorkerThread; void AcceptInboundSocketAndStartWorkerThread() { inboundSocket = tcpListener.AcceptSocket(); dataReceivedWorkerThread = new Thread(new ThreadStart(ListenForAndWaitToReceiveInboundData)); dataReceivedWorkerThread.Start(); } void ListenForAndWaitToReceiveInboundData() { var bytesRead = 0; try { bytesRead = inboundSocket.Receive(bufferForReceive, 0, bufferForReceive.Length, SocketFlags.None); // blocks here } catch (SocketException se) { if (se.SocketErrorCode == SocketError.Interrupted) { // Handle shutdown by (e.g.) parent thread.... } else { // handle other } return; } if (bytesRead == 0) { // handle shutdown from other end return; } // Do stuff with received data.... } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.