TcpListener receives file in chunks from Tcp Client

82 views Asked by At

I have a C# project with a server and client using TcpListener and TcpClient, and I want to have the client send a file to the server.

I am new to networking, but as far as I understand the best way to do this is by having the client send the size of the file first to the server, so it can prepare to receive with the right amount of bytes, and then the client sends the file over and the server receives it in not all at once but in chunks.

I have this code:

Client:

 byte[] fileNameByte = Encoding.UTF8.GetBytes(fileName);
 byte[] fileNameLen = BitConverter.GetBytes(fileNameByte.Length);
 byte[] fileData = File.ReadAllBytes(fileName);
 byte[] clientData = new byte[4 + fileNameByte.Length + fileData.Length];

 fileNameLen.CopyTo(clientData, 0);
 fileNameByte.CopyTo(clientData, 4);
 fileData.CopyTo(clientData, 4 + fileNameByte.Length);

 StreamCommands.Write(clientData, 0, clientData.Length);

Server:

byte[] fileData = new byte[999999999];

int recievedBytesLen = await Task.Run(() => currentClient.StreamCommands.Read(fileData, 0, fileData.Length));
int fileNameLen = BitConverter.ToInt32(fileData, 0);
string fileName = Encoding.UTF8.GetString(fileData, 4, fileNameLen);

int st = fileName.LastIndexOf("\\") + 1;
int en = fileName.Length;
string fname = fileName.Substring(st, en - st);

BinaryWriter bWrite = new BinaryWriter(File.Open(recievedFilePath+fname, FileMode.Create));
bWrite.Write(fileData, 4 + fileNameLen, recievedBytesLen - 4 - fileNameLen);
bWrite.Close();

This code does work and I am able to send a small file all at once but I don't want to be limited to small files and I know it's more efficient to receive the data in chunks, so I am wondering how I would go about implementing that in my code.

1

There are 1 answers

0
Stephen Cleary On

I strongly recommend that you use a higher-level networking stack; e.g., you could embed an ASP.NET Core server and then use standard HTTP file uploads.

If you really need TCP/IP, then you'll need to revisit your protocol and then start using loops. The protocol as it currently stands sends the length of the file name and then the filename and then the file data. What's missing is the length of the file data; you should send the length of the file name, the file name, the length of the file data, and the file data.

Then, on the receiving side, you need to repeatedly read bytes from the socket connection until you have enough for the file name length, then allocate space for the file name, then repeatedly read bytes until you have the entire file name, then repeatedly read bytes until you have enough for the file data length, then allocate space for the file data, then repeatedly read bytes until you have the entire file data.

Alternatively, you can do a single length prefix for the entire message, but you'll still need length prefixes for the fields to distinguish between the file name and the file data.

I have some code that does per-message length prefixing on my blog.