The bytes my server program receives is incomplete

2020-04-19 06:42发布

问题:

This doesn't always happen. But it happens more often than receiving the bytes completely.

This is how my client prog sends bytes to my server prog:

public void sendBytes()
{
    FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    TcpClient cli = tcpServer; //tcpServer is a global TcpClient variable I use to connect to the server. 
    NetworkStream ns = cli.GetStream();

    CopyStreamToStream(fs, ns, null);
    ns.Flush();
}

public static void CopyStreamToStream(Stream source, Stream destination, Action<Stream, Stream, Exception> completed)
{
    byte[] buffer = new byte[0x1000];
    int read;

    while ((read = source.Read(buffer, 0, buffer.Length)) > 0) // <- client loop
        destination.Write(buffer, 0, read);
}

This is how my server prog receives bytes from my client prog:

FileStream ms = new FileStream(tmpFile, FileMode.Create, FileAccess.Write);
do
{
    int szToRead = s.Available; //s is a socket
    byte[] buffer = new byte[szToRead];

    int szRead = s.Receive(buffer, szToRead, SocketFlags.None);
    if (szRead > 0)
        ms.Write(buffer, 0, szRead);            
}
while(s.Available != 0); //i also tried ms.length < size_of_file_to_be_received_in_bytes

i stepped into the program and watched the values, and I don't know what causes the lack of bytes received. I don't know if the problem is with the client prog or the server prog.

i don't know if this is relevant, but when i tried stepping into the client loop (to watch the value of fs.length and check if it will be received in the server), all 4 attempts to send a file from the client to the server succeeded. but when i did not watch the client loop, and only watch the server app (to see the value of ms.length), only 1 of 4 attempts to send a file to the server succeeded. on the 3 failed attempts, ms.length is less than the bytes of the source file (i checked it in the file's property).

This is where I based this from

ADDITIONAL INFORMATION:

On the original code (from the website I based my program), the loop has a different condition:

FileStream ms = new FileStream(tmpFile, FileMode.Create, FileAccess.Write);
do
{
    int szToRead = s.Available; //s is a socket
    byte[] buffer = new byte[szToRead];

    int szRead = s.Receive(buffer, szToRead, SocketFlags.None);
    if (szRead > 0)
        ms.Write(buffer, 0, szRead);            
}
while(SocketConnected(s)); // <--- i changed this in my program

private static bool SocketConnected(Socket s)
{
    return !(s.Poll(1000, SelectMode.SelectRead) && (s.Available == 0));
}

I think the SocketConnected tests if the socket is still connected because the original code for the client program was this:

CopyStreamToStream(fs, ns, null);
ns.Flush();
ns.Close();

i removed the ns.Close() from the program because I want to maintain the connection of the client application to the server. So I want to be able to check if I'm done reading all the bytes coming from the client app without closing the socket connection from the client side.

回答1:

If Available is zero this does not mean that you have finished reading bytes. It means that currently, in this nanosecond, there are no bytes queued. How could the server know how many bytes will be coming in the future?

In good approximation, every singe use of Available is an error!

Delete all usages of Available. Instead, always try to read a full buffer. If you get 0 this means the socket has been closed and you are done.

Edit: Here is some canonical read code:

var buffer = new byte[8192];
while(true) {
 var readCount = stream.Read(buffer, 0, buffer.Length);
 if (readCount == 0) break;
 outputStream.Write(buffer, 0, readCount);
}