Search code examples
c#socketsclient-serverfilestreamfile-sharing

How to get client file name on server side before copying data


I'm running out of idea in my file sharing application where I am receiving data on server from client side using FileStream.copyto().It works well but currently i have set the buffer size of 1024 for var bytesread = fs.Read(buffer, 0, buffer.Length) but I have two issues :

  1. I am unsure about the maximum size it allows before buffer overflow.Should it be a hit and trial method ? or is there a fixed bytes limit to transfer data on WLAN.

  2. When i checked the code on server side using hard-coded filename with extension like in FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read) where i pre-defined path ,it works but how would I enable it to get the file name. I mean,should i use a fix-protocol where i should allocate a pre-data bytes for it and then check & recieve on server side before data copying ?

Any good idea.


Solution

  • Without code and a more precise description, it's hard to know what you actually need here. But, if I understand correctly you have a scenario where a client endpoint is reading a local file and sending the data to the server endpoint, and the server endpoint is using a NetworkStream object for the I/O.

    Assuming that's the case, then the client can include the file name with something like this:

    void TransmitFileName(Stream stream, string fileName)
    {
        byte[] fileNameBytes = Encoding.UTF8.GetBytes(fileName),
            fileNameLengthBytes = BitConverter.GetBytes(fileNameBytes.Length);
    
        stream.Write(fileNameLengthBytes, 0, 4);
        stream.Write(fileNameBytes, 0, fileNameBytes.Length);
    }
    

    On the server, assuming you have already read the data into an appropriately-sized byte[] buffer, you can decode the information as follows:

    string DecodeFileName(Stream stream)
    {
        byte[] fileNameLengthBuffer = new byte[4];
    
        FillBufferFromStream(stream, fileNameLengthBuffer);
        int fileNameLength = BitConverter.ToInt32(fileNameLengthBuffer, 0);
    
        byte[] fileNameBuffer = new byte[fileNameLength];
    
        FillBufferFromStream(stream, fileNameBuffer);
        return Encoding.UTF8.GetString(fileNameBuffer);
    }
    
    void FillBufferFromStream(Stream stream, byte[] buffer)
    {
        int cbTotal = 0;
        while (cbTotal < buffer.Length)
        {
            int cbRead = stream.Read(buffer, cbTotal, buffer.Length - cbTotal);
    
            if (cbRead == 0)
            {
                throw new InvalidDataException("premature end-of-stream");
            }
    
            cbTotal += cbRead;
        }
    }
    

    I leave the actual server-side I/O as an exercise for the reader. Do note, however, that using TCP you are not guaranteed any given read operation will block until all the bytes you've asked for have been read. So you will need to read continuously until you've gotten all of the bytes you need for the decoding operation to succeed.

    <EDIT> I've added the necessary logic to read the data from the NetworkStream object. Note that I include a helper method that will check the actual number of bytes read, and continue to read bytes, filling the buffer until as many bytes as are needed are actually read. You cannot count on the Read() method to block until you've actually read all of the bytes you were hoping for.</EDIT>

    In practice, this probably means doing the decoding in two parts: first part to get the fileNameLength value (which you know requires 4 bytes), and the second part to get the file name (the length of which you will know only after decoding the fileNameLength value).

    After the bytes for the file name, you can just transmit the file data itself just as you were before.


    EDIT: To use the above in the context of the code you posted…

    Client: call the TransmitFileName() method before you send the actual file data. E.g.:

    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
    {
        TransmitFileName(netstream, Path.GetFileName(path));
        ...
    

    Server: call the DecodeFileName() method before you receive the actual file data. E.g.:

    //open a stream to get data 
    NetworkStream netStream = client.GetStream(); 
    
    //Directory to save
    string DirName = @"D:\NewFolder\Test\";
    
    //File Name is requirement here to save data at.
    
    string fileloc = Path.Combine(DirName, DecodeFileName(netStream));
    MessageBox.Show(fileloc);