user3379482
user3379482

Reputation: 567

Sending files with sockets c# client/server

I'm trying to send files using sockets..it's working with few bytes files no more...how to solve this problem? It's a client / server program I'm trying to send at least 50 MB.

The client side-Sending:

System.Net.Sockets.TcpClient tcpClient = new
    System.Net.Sockets.TcpClient();

tcpClient.Connect(recipientIP, FTPPORTNO);
int BufferSize = tcpClient.ReceiveBufferSize;
NetworkStream nws = tcpClient.GetStream();

FileStream fs;
fs = new FileStream(filename, FileMode.Open,
    FileAccess.Read);
byte[] bytesToSend = new byte[fs.Length];
int numBytesRead = fs.Read(bytesToSend, 0,
    bytesToSend.Length);
int totalBytes = 0;
for (int i = 0; i <= fs.Length/BufferSize; i++)
{
    //---send the file---
    if (fs.Length - (i*BufferSize) > BufferSize)
    {
        nws.Write(bytesToSend, i*BufferSize,
            BufferSize);
        totalBytes += BufferSize;
    }
    else
    {
        nws.Write(bytesToSend, i*BufferSize,
            (int) fs.Length - (i*BufferSize));
        totalBytes += (int) fs.Length - (i*BufferSize);
    }
    fs.Close();
}

The Recieving code:

try
{
    //---get the local IP address---
    System.Net.IPAddress localAdd =
        System.Net.IPAddress.Parse(
            ips.AddressList[0].ToString());
    //---start listening for incoming connection---
    System.Net.Sockets.TcpListener listener = new
        System.Net.Sockets.TcpListener(localAdd,
            FTPPORTNO);
    listener.Start();
    //---read incoming stream---
    TcpClient tcpClient = listener.AcceptTcpClient();
    NetworkStream nws = tcpClient.GetStream();
    //---delete the file if it exists---
    if (File.Exists("c:\\temp\\" + filename))
    {
        File.Delete("c:\\temp\\" + filename);
    }
    //---create the file---
    fs = new System.IO.FileStream("c:\\temp\\" + filename,
        FileMode.Append, FileAccess.Write);
    int counter = 0;
    int totalBytes = 0;
    do
    {
        //---read the incoming data---
        int bytesRead = nws.Read(data, 0,
            tcpClient.ReceiveBufferSize);
        totalBytes += bytesRead;
        fs.Write(data, 0, bytesRead);
        //---update the status label---
        ToolStripStatusLabel1.Text = "Receiving " +
                                        totalBytes + " bytes....";
        Application.DoEvents();
        counter += 1;
    } while (nws.DataAvailable);
    ToolStripStatusLabel1.Text = "Receiving " + totalBytes
                                    + " bytes....Done.";
    fs.Close();
    tcpClient.Close();
    listener.Stop();
}
catch (Exception ex)
{
    MessageBox.Show(ex.ToString());
}

The Server Code:

public void SendMessage (string message)
{
    //---adds a linefeed char---
    message += "\n";
    try
    {
        //---send the text---
        System.Net.Sockets.NetworkStream ns;
        lock (client.GetStream())
        {
            ns = client.GetStream();
            byte[] bytesToSend =
                System.Text.Encoding.ASCII.GetBytes(message);
            //---sends the text---
            ns.Write(bytesToSend, 0, bytesToSend.Length);
            ns.Flush();
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

Any Ideas?

Upvotes: 0

Views: 8382

Answers (2)

Jim Mischel
Jim Mischel

Reputation: 133975

Your primary problem is that you're expecting DataAvailable == false to mean that no more data is forthcoming. That's not the case. DataAvailable says whether data is available right now. It doesn't mean that there won't be any more available at some point in the future.

If you're sending and receiving data of any kind, there must be some way for the sender to tell the receiver where the end of the message is. In general there are two ways to do that:

  1. Send a header that tells how many bytes are to follow.
  2. Send an "end of message" packet after all the rest of the data has been sent.

With text protocols, the second option is typically used: a message is sent, and terminated with a newline. That marks the end of the message.

When sending binary data, the first method is typically used. That header can be something as simple as four bytes that say how long the file is. So in your case you'd write something like:

// tell receiver how many bytes are coming
byte[] lengthData = BitConverter.GetBytes[fs.Length];
nws.Write(lengthData, 0, lengthData.Length);
// then send the file

The receiver, then, needs to read the first four bytes, convert to an integer, and then read that many bytes. Only when it's read that many bytes can it know that it's reached the end of the file.

// read length
byte[] lengthData = new byte[4];
bytesRead = nws.Read(lengthData, 0, 4);
// I assume here that it got all four bytes. In your code, you'll want to
// make sure that you got four bytes before moving on.
int bytesToRead = BitConverter.ToInt32(lengthData, 0);
// Now start your reading loop, and read until the total number of bytes read
// is equal to the bytesToRead value above.

Upvotes: 1

jgauffin
jgauffin

Reputation: 101140

Why don't you just do this on the sender end:

fileStream.CopyTo(networkStream);

A lot simpler :)

On the receiver side you cannot assume that DataAvailable = false means that there are no more data. It's better that the sender closes the connection when done socket.Shutdown(SocketShutdown.Send) and that the receiver continues to read until 0 is returned.

Sender side:

  1. Server sends file
  2. Server calls socket.Shutdown(SocketShutdown.Send);
  3. Server does socket.Receive(xxx) to wait for graceful disconnect
  4. Socket.Close();

Receiver side:

  1. Reads from network until Read returns 0
  2. Closes the file stream (file completed)
  3. Calls socket.Shutdown(SocketShutdown.Both)
  4. Socket.Close();

However, you still got a flaw: The socket might have been disconnected due to network failure. Only way to know that is to send the file size first (before sending file)

Upvotes: 0

Related Questions