tags:

views:

1095

answers:

1

I'm developing a simple application to send files over TCP using the TCPListener and TCPClient classes. Here's the code that sends the file.

Stop is a volatile boolean which helps stopping the process at any time and WRITE_BUFFER_SIZE might be changed in runtime (another volatile)

while (remaining > 0 && !stop)
{
  DateTime current = DateTime.Now;
  int bufferSize = WRITTE_BUFFER_SIZE;
  buffer = new byte[bufferSize];
  int readed = fileStream.Read(buffer, 0, bufferSize);
  stream.Write(buffer, 0, readed);
  stream.Flush();
  remaining -= readed;
  // Wait in order to guarantee send speed
  TimeSpan difference = DateTime.Now.Subtract(current);
  double seconds = (bufferSize / Speed);
  int wait = (int)Math.Floor(seconds * 1000);
  wait -= difference.Milliseconds;
  if (wait > 10)
    Thread.Sleep(wait);
}                
stream.Close();

and this is the code that handles the receiver side:

do
{
  readed = stream.Read(buffer, 0, READ_BUFFER_SIZE);
  // write to .part file and flush to disk
  outputStream.Write(buffer, 0, readed);
  outputStream.Flush();
  offset += readed;
} while (!stop && readed > 0);

Now, when the speed is low (about 5KBps) everything works ok but, as I increase the speed the receiver size becomes more prone to raise a SocketException when reading from the stream. I'm guessing it has to do with the remote socket being closed before all data can be read, but What's the correct way to do this? When should I close the sending client?

I haven't found any good examples of file transmission on google, and the ones that I've found have a similar implementation of what I'm doing so I guess I'm missing something.

Edit: I get this error "Unable to read data from the transport connection". This is an IOException whose inner exception is a SocketException.

I've added this in the sender function, still I get the same error, the code never reaches the stream.close() and of course the tcpclient never really get closed... so I'm completely lost now.

buffer = new byte[1];
client.Client.Receive(buffer);
stream.Close();
+1  A: 

Typically you want to set the LINGER option on the socket. Under C++ this would be SO_LINGER, but under windows this doesn't actually work as expected. You really want to do this:

  • Finish sending data.
  • Call shutdown() with the how parameter set to 1.
  • Loop on recv() until it returns 0.
  • Call closesocket().

Taken from: http://tangentsoft.net/wskfaq/newbie.html#howclose

C# sharp may have corrected this in its libraries, but I doubt it since they are built on top of the winsock API.

Edit:

Looking at your code in more detail. I see that you are sending no header across at all, so on the receiving side you have no idea of how many bytes you are actually supposed to read. Knowing the number of bytes to read of the socket makes this a much easier problem to debug. Keep in mind that shutting down the socket can still snip of the last bit of data if you don't close it properly.

Additionally having your buffer size be volatile is not thread safe and really doesn't buy you anything. Using stop as a volatile is safe, but don't expect it to be instant. In other words the loop could run several more times before it gets the updated value of stop. This is especially true on multiprocessor machines.

Edit_02:

For the TCPClientClass you want to do the following (as far as I can tell without having access to a C# at the moment).

// write all the bytes
// Then do the following

client.client.Shutdown(Shutdown.Send) // This assumes you have access to this protected member
while (stream.read(buffer, 0, READ_BUFFER_SIZE) != 0);
client.close()
grieve
LingerState doesn't work, I've tried ... :( I'm going to try the rest of the suggestions...
Jorge Córdoba
There is header information being sent, I've taken it out for the sake of the question length. There's also some error handling. Stop works as I don't really care if I loop 3 or 4 times before exiting the loop, it is there to allow the main thread to signal the executing threads to stop sending or receiving.
Jorge Córdoba