Search code examples
c#socketstcpclienttcplistenernetworkstream

C# - Get error result by sending images via NetworkStream


I currently need to send big data (such as images) from a client(C#) to a server (C#). There is no problem with sending and receiving the string data via NetworkStream. But when sending large data (images) will get an error result (loss data or image crash). Any idea?

client image output before sending (test_send.jpg) output after the server received(test_received.jpg)

Server

static void Main(string[] args)
{
    int Port = 20000;
    TcpListener? server = null;
    try
    {
        server = new TcpListener(IPAddress.Any, Port);
        server.Start();

        var client = server.AcceptTcpClient();
        var stream = client.GetStream();
        
        // get image
        var ms = new MemoryStream();
        int total = 0;
        int read = -1;
        do
        {
            if (!stream.CanRead)
            {
                continue;
            }
            
            Console.WriteLine("get");
            byte[] data = new byte[client.ReceiveBufferSize];
            read = stream.Read(data, 0, client.ReceiveBufferSize);
            ms.Write(data, 0, data.Length);
            total += read;
        } while (read > 0);

        Console.WriteLine("get image finish");
        Console.WriteLine($"total is {total}"); // 8590651
        
        byte[] image = ms.ToArray();

        // image.Length is different every time, I don't know why
        Console.WriteLine($"the byte array size is {image.Length}");

        File.WriteAllBytes("/Users/tim/Downloads/test_received.jpg", image);
        Console.WriteLine("saved");
    }
    catch (Exception e)
    {
        Console.WriteLine($"Error: {e.Message}");
    }
    finally
    {
        server?.Stop();
    }
}

Client

private static void SendData(byte[] image)
{
    Console.WriteLine($"image byte size is {image.Length}"); // 8590651
    File.WriteAllBytes("/Users/tim/Downloads/test_send.jpg", image); // test
    
    // TCP
    var tcpClient = new TcpClient();
    try
    {

        tcpClient.Connect("127.0.0.1", 20000);
    }
    catch (Exception e)
    {
        Console.Error.WriteLine($"TCP connect error, {e.Message}");
        return;
    }

    try
    {
        var stream = tcpClient.GetStream();
        
        if (stream.CanWrite)
        {
            stream.Write(image, 0, image.Length);
        }
    }
    catch (Exception e)
    {
        Console.Error.WriteLine($"send error, {e.Message}");
    }

    tcpClient.Close();
}

Solution

  • Your mistake is in this line

    ms.Write(data, 0, data.Length);
    

    It should be

    ms.Write(data, 0, read);
    

    To be honest, it's far easier to just use CopyTo.

    var ms = new MemoryStream();
    stream.CopyTo(ms);
    

    You should also consider transitioning to a fully async flow, and you are also missing using everywhere.

    static async Task Main(string[] args)
    {
        int Port = 20000;
        TcpListener? server = null;
        try
        {
            server = new TcpListener(IPAddress.Any, Port);
            server.Start();
    
            using var client = await server.AcceptTcpClientAsync();
            using var stream = client.GetStream();
            
            // get image
            var ms = new MemoryStream();
            await stream.CopyToAsync(ms);
    
            Console.WriteLine("get image finish");
            Console.WriteLine($"total is {total}"); // 8590651
            
            byte[] image = ms.ToArray();
    
            // image.Length is different every time, I don't know why
            Console.WriteLine($"the byte array size is {image.Length}");
    
            await File.WriteAllBytesAsync("/Users/tim/Downloads/test_received.jpg", image);
            Console.WriteLine("saved");
        }
        catch (Exception e)
        {
            Console.WriteLine($"Error: {e.Message}");
        }
        finally
        {
            if(server?.Active)
                server?.Stop();
        }
    }
    
    private static async Task SendData(byte[] image)
    {
        Console.WriteLine($"image byte size is {image.Length}"); // 8590651
        await File.WriteAllBytesAsync("/Users/tim/Downloads/test_send.jpg", image); // test
        
        // TCP
        using var tcpClient = new TcpClient();
        try
        {
            await tcpClient.ConnectAsync("127.0.0.1", 20000);
        }
        catch (Exception e)
        {
            Console.Error.WriteLine($"TCP connect error, {e.Message}");
            return;
        }
    
        try
        {
            using var stream = tcpClient.GetStream();
            await stream.WriteAsync(image, 0, image.Length);
        }
        catch (Exception e)
        {
            Console.Error.WriteLine($"send error, {e.Message}");
        }
    }
    

    Consider also passing the NetworkStream directly to a FileStream.