0

When the file is sent to c# server through firefox, it works but it doesn't work on chrome.

In firefox, every files size can be received well. in chrome, only small files are received. however, received bytes of Files larger than 50kb are not equal to the file size.

public class Client
{
    private static readonly string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    public Socket Socket { set; get; }
    private byte[] Buffer { get; set; }
    private int BufferSize { get; set; } = 1024 * 1024;
    private List<byte> Buffers = new List<byte>();
    private BytesDecoder decoder = new BytesDecoder();

    public void Listen()
    {
        SendHandshake();
        BeginReceive();
    }

    private void SendHandshake()
    {
        try
        {
            byte[] buffer = new byte[1024];
            Socket.Receive(buffer);
            var key = new Regex(@"Sec-WebSocket-Key:\s(.*?)\r\n").Match(Encoding.UTF8.GetString(buffer)).Groups[1].Value.Trim();
            byte[] keyBytes = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(key + guid));
            key = Convert.ToBase64String(keyBytes);
            var response = string.Format(string.Join("\r\n", new string[]{
                "HTTP/1.1 101 Switching Protocols",
                "Upgrade: websocket",
                "Connection: Upgrade",
                "Sec-WebSocket-Accept: {0}",
                "",
                ""
            }), key);
            Socket.Send(Encoding.UTF8.GetBytes(response));
        }
        catch (SocketException ex)
        {
            Console.WriteLine("{0}: {1}", "SendHandshake", ex.Message);
            Close();
        }
        catch (Exception ex)
        {
            Console.WriteLine("{0}: {1}", "SendHandshake", ex.Message);
        }
    }

    private void BeginReceive()
    {
        try
        {
            Buffer = new byte[BufferSize];
            Socket.BeginReceive(Buffer, 0, BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), this);
        }
        catch (SocketException ex)
        {
            Console.WriteLine("{0}: {1}", "BeginReceive1", ex.Message);
            Close();
        }
        catch (Exception ex)
        {
            Console.WriteLine("{0}: {1}", "BeginReceive2", ex.Message);
        }
    }


    public static byte[] TrimEnd(byte[] array)
    {
        int lastIndex = Array.FindLastIndex(array, b => b != 0);
        Array.Resize(ref array, lastIndex + 1);
        return array;
    }

    private void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            var bytes = new byte[0];
            if (decoder.indexFirstMask==0)
            {
               bytes = decoder.DecodeMessage(TrimEnd(Buffer));
            }
            else
            {
                bytes = decoder.DecodeRemainingMessage(TrimEnd(Buffer));
            }
            Buffers = Buffers.Concat(bytes).ToList();
            if (Buffers.ToArray().Length == Convert.ToInt32(fileInfo["size"]))
            {
                File.WriteAllBytes(@"C:\" + fileInfo["name"], Buffers.ToArray());
            }
            BeginReceive();
        }
        catch (SocketException ex)
        {
            Close();
            Console.WriteLine("{0}: {1}", "ReceiveCallback", ex.Message);  
        }
        catch (Exception ex)
        {
            BeginReceive();
            Console.WriteLine("{0}: {1}", "ReceiveCallback", ex.Message);
        }
    }

    public void Close()
    {
        Socket.Shutdown(SocketShutdown.Both);
        Socket.Close();
        Socket.Dispose();
    }     

}

when file size less than 50kb, byte 0 is 130 (opcode=2) and file size larger than 50 kb, byte 0 is 2;

It is clear that there is a problem with frame decoding or handshake response....

public class BytesDecoder
{
    public int dataLength, indexFirstMask=0;
    public IEnumerable<byte> keys;
    public byte[] DecodeMessage(byte[] bytes)
    {
        Console.WriteLine("+DecodeMessage+");
        byte secondByte = bytes[1];
        bool masked = (bytes[1] & 128) != 0;
        dataLength = secondByte & 127;
        indexFirstMask = 2;
        if (masked)
        {
            Console.WriteLine("Masked bit SET");
        }
        if (dataLength == 126)
        {
            indexFirstMask = 4;
            dataLength = bytes[3] | bytes[2] << 8;
        }
        else if (dataLength == 127)
        {
            indexFirstMask = 10;
            dataLength = bytes[9] | bytes[8] << 8 | bytes[7] << 16 | bytes[6] << 24 | bytes[5] << 32 |
                bytes[4] << 40 | bytes[3] << 48 | bytes[2] << 56;
        }
        keys = bytes.Skip(indexFirstMask).Take(4);
        int indexFirstDataByte = indexFirstMask + 4;

        byte[] decoded = new byte[bytes.Length - indexFirstDataByte];
        Console.WriteLine("dataLength : " + dataLength + " ; bytes.Length : " + bytes.Length);
        int j = 0;
        for (int i = indexFirstDataByte; i < bytes.Length; i++)
        {
            decoded[j] = (byte)(bytes[i] ^ keys.ElementAt(j % 4));
            j++;
        }
        Console.WriteLine("-DecodeMessage-");
        return decoded;
    }
    public byte[] DecodeRemainingMessage(byte[] bytes)
    {
        Console.WriteLine("+DecodeRemainingMessage+");
        int indexFirstDataByte = 0;
        byte[] decoded = new byte[bytes.Length - indexFirstDataByte];
        int j = 0;
        for (int i = indexFirstDataByte; i < bytes.Length; i++)
        {
            decoded[j] = (byte)(bytes[i] ^ keys.ElementAt(j % 4));
            j++;
        }
        Console.WriteLine("-DecodeRemainingMessage-");
        return decoded;
    }
}

js code:

connect:function(){
    var root = this;
    root.websocket = new WebSocket(root.url);
    root.websocket.binaryType = "arraybuffer";
    root.websocket.onopen = () => root.fireEvent('connect');
    root.websocket.onmessage = (e) => {
        root.fireEvent('receive',JSON.parse(e.data));
    }
    window.addEventListener("beforeunload", function() {
        root.websocket.onclose = function () {}; 
        root.websocket.close();
    });
},

sendMessage:function(msg){
    var root = this;
    root.controller.websocket.send(message);
},
sendFile:function(){
    var root = this;
    var file = document.getElementById('filename').files[0];
    var root = this;
    var loader = new FileReader();
    loader.onload = (e) => {
        var byteArray = new Uint8Array(e.target.result);
        root.buffer = byteArray.buffer;
        root.byteLength = root.buffer.byteLength;
        root.server.websocket.send(root.buffer);
    }
    loader.readAsArrayBuffer(file);
}

Edit 1:

problem found large messages dataLength not currect

dataLength = (int)BitConverter.ToInt64(new byte[] { buffer[9], buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3], buffer[2] }, 0);

for example file size is 169174 received data length received data length in firefox= 169174 received data length in chrome= 131000

Edit 2:

FIN bit in chrome is 2 and in firefox is 1

soheilo0
  • 21
  • 4
  • How do you know where the end of message? What happens if not all data is received in one chunk? You have a framing issue. What you think is the beginning of one message is the end character of previous message. – jdweng Oct 12 '22 at 10:27
  • before transfer file, i send file size to server and store it in fileInfo["size"]. every time bytes received, store it in List and when bytes length equal the file size, i know this is a end of file and save it do disc also check first byte value if equal to opcode 2 I know this a start file transferring – soheilo0 Oct 12 '22 at 11:44
  • if all data received in one chunk decode it with `DecodeMessage()` method after that if byte length equal to file size i know end of transfering and save it to disc if all data received in multiple chunk, first chunk decode with `DecodeMessage()` method and other chunks decode with `DecodeRemainingMessage()` method after that if byte length equal to file size i know end of transfering and save it to disc – soheilo0 Oct 12 '22 at 11:48
  • sorry for my bad english – soheilo0 Oct 12 '22 at 11:49
  • this code is works fine in firefox... my problem is chrome how handle it? – soheilo0 Oct 12 '22 at 11:50
  • You are using TCP transport layer. TCP can be broken pieces and combined during transmission. Firefox and chrome could be handling the chunks differently. Smaller message work. So something is happening with the large messages only. – jdweng Oct 12 '22 at 12:53
  • @jdweng How can I cover both? – soheilo0 Oct 12 '22 at 12:55
  • do you suggest a better method? – soheilo0 Oct 12 '22 at 12:57
  • Better to fix current code. Length will only be one first Receive. If you get multiple chunks do not get length. Also do not know why you are trimming. If you get multiple chunks it will create issues. – jdweng Oct 12 '22 at 13:42
  • @jdweng problem found large messages dataLength not currect `dataLength = (int)BitConverter.ToInt64(new byte[] { buffer[9], buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3], buffer[2] }, 0);` – soheilo0 Oct 12 '22 at 17:53
  • @jdweng for example file size is 169174 received data length in firefox= 169174 received data length in chrome= 131000 – soheilo0 Oct 12 '22 at 20:43
  • Message could be using compression (GZIP) or connection terminate in the middle. Compression the header would contain an indicator. See : https://stackoverflow.com/questions/24138373/unzip-gz-file-using-c-sharp – jdweng Oct 12 '22 at 23:09
  • @jdweng i check gzip & deflate and doesn't work. problem is not compression – soheilo0 Oct 13 '22 at 08:48
  • based on [link](https://bugs.chromium.org/p/chromium/issues/detail?id=517090) the subject of this bug is "websocket text frames over 131k get truncated" – soheilo0 Oct 13 '22 at 08:51
  • when send any file , received data length is 131000..... – soheilo0 Oct 13 '22 at 08:53
  • You code is only receiving one chunk. Yoiu care calling BeginReceive() which is terminating the recieve data after one chunk is received (131,000). See msdn async sample where new AsyncCallback(ReceiveCallback), state); is called instead. See https://gist.github.com/leandrosilva/656054 – jdweng Oct 13 '22 at 09:04

0 Answers0