在HTTP响应中发送大比特流的有效方式

时间:2014-10-16 06:46:11

标签: javascript algorithm rest

我需要将通过线路表示数字的大字符串发送到用Java Script编写的客户端。从理论上讲,它是0s1s的流,如:011101011101101...,但它可能非常非常大(长度为数百万位)。当然,我的目标是尽量减少必要的处理和发送开销。我考虑过更改该字符串的基数,以便它使用HEX或更大的基数,这将大大减少必须发送的数据量。 JavaScript内置了用于转换到不同编号系统的函数,因此它看起来像是要走的路。但是,支持的最大基数只有36个。我的计算结果显示,当拥有50毫升的数据流并使用36个基数时,你仍然需要发送1,388,888个字符 - 太多了。

我的问题是 - 你知道有什么方法可以帮助我实现目标吗?一些约束:

  • 解决方案必须适用于任意长度的流
  • 比特流可以大到50mln比特
  • 长度约1-10mln位应保证良好的性能。对于较大的流,它应该仍然有效,但它不必线性扩展
  • 我会更加强调优化必须发送的数据量,而不是减少CPU开销

1 个答案:

答案 0 :(得分:0)

你可以做这样的事情(这个演示已经编写过,无法测试它,但它“应该工作了”,这对于那些想要复制/剪切/粘贴的人来说是个小提琴:http://fiddle.jshell.net/sywz3aym/2/
请注意小提琴不能运行,我打算写一个响应者,但我现在不能害怕。

在javascript入口区域的底部,我有一个关于asp.net响应者可以看的评论部分,如果你使用visual studio,它使用“Generic Handler(.ashx)”文件,如果你使用任何其他语言你将不得不使用那里的等价选项。您需要一个可以自定义的请求响应器来返回二进制数据,您需要将Content-Type设置为“application / octet-stream”(对于那些不知道的人来说是八位字节=“8位组”,例如一个字节: ))

这里是javascript +评论,就像文字格式的辉煌一样:

$(document).ready(function () {
    var url = 'your handler URL';
    var oReq = new XMLHttpRequest();
    oReq.open('GET', url, true);
    oReq.responseType = 'arraybuffer';

    oReq.onload = function (oEvent) {
        var buffer = oReq.response;
        var yourData = ExtractData(buffer);
    }
    oReq.send(null);
});

function ExtractData(buffer) {
    var dataReader = {
        dataView: new DataView(buffer),
        readPtr: 0,

        littleEndian: (function () {
            var buffer = new ArrayBuffer(2);
            new DataView(buffer).setInt16(0, 256, true);
            return new Int16Array(buffer)[0] === 256;
        })(),

        setReadPtrOffset: function (byteIndex) {
            this.readPtr = byteIndex;
        },

        nextInt8: function () {
            var data = this.dataView.getInt8(this.readPtr);
            this.readPtr += 1; // Sizeof int
            return data;
        },
        nextUint8: function () {
            var data = this.dataView.getUint8(this.readPtr);
            this.readPtr += 1; // Sizeof int8
            return data;
        },

        nextInt32: function () {
            var data = this.dataView.getInt32(this.readPtr, this.littleEndian);
            this.readPtr += 4; // Sizeof int32
            return data;
        },
        nextUint32: function () {
            var data = this.dataView.getUint32(this.readPtr, this.littleEndian);
            this.readPtr += 4; // Sizeof uint32
            return data;
        },

        nextFloat32: function () {
            var data = this.dataView.getFloat32(this.readPtr, this.littleEndian);
            this.readPtr += 4; // Sizeof float
            return data;
        },
        nextFloat64: function () {
            var data = this.dataView.getFloat64(this.readPtr, this.littleEndian);
            this.readPtr += 8; // Sizeof double
            return data;
        },

        nextUTF8String: function (length) {
            var data = String.fromCharCode.apply(null, new Uint8Array(this.dataView.buffer, this.readPtr, length));
            this.readPtr += length; // Sizeof int
            return data;
        },
    }

    var numberOfInt32ToRead = dataReader.nextInt32(); // First data could be, for example, the number of ints to read.
    for(var i = 0; i < numberOfInt32ToRead; i++){
        var someInt = dataReader.nextInt32();
        // doStuffWithInt(someInt);
    }
}

/*
Serverside code looks kind of like this (asp.net/c#):

    public class YourVeryNiceDataRequestHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "application/octet-stream"; // <- very important
            List<int> data = SomeMethodWithGivesYouData();
            context.Response.BinaryWrite(BitConverter.GetBytes((int)data.Count)); // Explicit type casting only to help me get an overview of the data being sent, in this case it has no functional meaning other then to help me debug
            foreach(int i in data)
            {
                context.Response.BinaryWrite(BitConverter.GetBytes((int)i));
            }
            // You could send structs as well, either with the help of a binary formatter, or you can do like i did here and use BitConverter.GetBytes, be carefull when sending strings/chars since they are by default (usually) in a widechar format (1 char = 2 bytes), since i only use english for this i can convert it to a UTF8 char (1 byte):                         context.Response.BinaryWrite(System.Text.Encoding.UTF8.GetBytes(new char[] { (motion.Phase == Phases.Concentric ? 'C' : 'E') })); // Phase (as single char)

        }
    }
*/

我希望这可以直接帮助您,或指导您正确的方向 请注意,这很大程度上取决于您的服务器使用的数据类型,并且可能不属于您的“REST”类别,但您确实表示您希望针对流大小进行优化,而此afaik是最佳方法除了添加数据压缩之外
Javascript类型数组“模仿”c样式数据类型,如c,c ++,c#,java等中使用的

<强> Disclamer:
我还没有尝试使用数据&gt;然而,它确实将从服务器发送到客户端的数据的总大小从2MB减少到几个10-100~KB,并且对于我发送/读取执行时间的数据量是“不可人为可测量的”其他然后是额外请求的往返时间,因为这是在浏览器中加载页面后请求的

最后的警告:任何手动位序列化/反序列化应该小心,因为很容易弄错,忘记你已经改变了客户端或服务器端的东西,它会让你联合国-debuggable垃圾,如果你在任何一端读取或写入太多/太少的字节,这就是为什么我在我的服务器端代码中添加显式类型转换,这样我可以打开服务器代码和客户端代码旁边-side并将readInt32与Write((int)...)匹配 这不是一个有趣的工作,但它使事情变得非常紧凑和非常快(通常我会考虑可读性,但有些任务只需要比可读代码更快地工作)。

然而,键入的数组不能在每个浏览器中使用,但可以说85%的互联网可以使用它们:http://caniuse.com/#feat=typedarrays