Question

I'm trying to stream a file over a socket, and write it to a file as I receive it (so that it can be opened in other applications). It mostly works except for near the end where (I'm guessing) it blocks because it's trying to read an entire 8 KB chunk when there isn't that much left to receive. I'm at a loss trying to figure out how to fix this. I know the total amount of bytes I'm supposed to receive, if that helps. Thanks!

using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) {
  socket.Connect(e.IPAddress, e.Port);
  using (var fs = new FileStream(e.Filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read)) {
    byte[] buffer = new byte[8192];
    int read;
    while ((read = socket.Receive(buffer)) > 0) {
      fs.Write(buffer, 0, read);
    }
  }
}
Was it helpful?

Solution

From the MSDN...

If no data is available for reading, the Receive method will block until data is available, unless a time-out value was set by using Socket.ReceiveTimeout. If the time-out value was exceeded, the Receive call will throw a SocketException. If you are in non-blocking mode, and there is no data available in the in the protocol stack buffer, the Receive method will complete immediately and throw a SocketException. You can use the Available property to determine if data is available for reading. When Available is non-zero, retry the receive operation.

The Socket.Receive() method blocks because the connection is still open on the other end, even if there isn't any more data available. There are a couple ways to get around this, one which requres you to know the size of the file beforehand and stopping once you've read that many bytes Another would be using a non-blocking socket (by setting the Socket.Blocking property to false) and getting the data available from the Socket.Available property. The latter requires a little more finesse in writing your code since read operations won't halt your code anymore.

If you know the size of the data beforehand, you can compare that to how much you've read so far and stop once you've reached that size:

//If you know the size beforehand, set it as an int and compare
// it to what you have so far
int totalBytes = 9001;

using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
    socket.Connect(e.IPAddress, e.Port);
    using (var fs = new FileStream(e.Filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read))
    {
        byte[] buffer = new byte[8192];
        int read;
        int bytesSoFar = 0; //Use this to keep track of how many bytes have been read

        do
        {
            read = socket.Receive(buffer);
            fs.Write(buffer, 0, read);
            bytesSoFar += read;

        } while (bytesSoFar < totalBytes);
    }
}

Keep in mind that this second example isn't very robust and will not work when trying to send multiple files using the same socket. But for just a simple 1 file transfer, this should work fine.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top