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