在HTTP响应中发送大型比特流的有效方式
Efficient way of sending a large bit stream in an HTTP response
我需要通过连线将表示数字的大字符串发送到用Java Script编写的客户端。理论上,它是0s
和1s
的流,比如:011101011101101...
,但它可以非常非常大(长度为数百万比特)。当然,我的目标是尽量减少必要的处理和发送开销。我考虑过更改该字符串的基数,使其使用十六进制或更大的基数,这将大大减少必须发送的数据量。JavaScript内置了用于转换不同编号系统的函数,因此看起来是可行的。然而,支持的最大基数只有36。我的计算表明,当具有5000万比特的流并使用36基数时,仍然需要发送1,388,888
字符——太多了。
我的问题是,你知道有什么方法可以帮助我实现目标吗?一些限制:
- 解决方案必须适用于任意长度的流
- 比特流可以大到50mln比特
- 对于长度约为1-10mln的比特,应保证良好的性能。对于较大的流,它应该仍然有效,但不必线性扩展
- 我会更加强调优化必须发送的数据量,而不一定是减少CPU开销
您可以这样做(编写演示时没有测试方法,但它"应该可以工作™",这里有一把小提琴,适合那些想复制/剪切/粘贴的人:http://fiddle.jshell.net/sywz3aym/2/
请注意,小提琴不能演奏,我本来打算写一个回应者,但恐怕现在不行。
在javascript输入区域的底部,我有一个关于asp.net响应程序外观的评论部分,如果你使用visualstudio,它会使用一个"Generic Handler(.ashx)"文件;如果你使用任何其他语言,你必须使用那里的等效选项。你需要一个请求响应程序,你可以自定义它来返回二进制数据,你需要将内容类型设置为"应用程序/八位字节流"(八位字节用于那些不知道="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等中使用的数据类型
免责声明:
我没有在数据>1MB的情况下尝试过这种方法,但它确实将从服务器发送到客户端的数据总大小从2MB减少到了10-100~KB,而且对于我发送/读取的数据量,执行时间"无法测量",除了额外请求的往返时间,因为这是在浏览器中加载页面后请求的
最后一句警告:任何手动的位序列化/反序列化都应该小心进行,因为忘记在客户端或服务器端更改了某些内容很容易出错,如果在两端读写的字节太多/太少,就会产生不可调试的垃圾,这就是为什么我在服务器端代码中添加显式类型转换,这样,我可以同时打开服务器代码和客户端代码,并将readInt32与Write((int)…)匹配
这不是一项有趣的工作,但它使事情变得非常紧凑和快速(通常我会追求可读性,但有些任务只需要比可读代码工作得更快)
然而,打字阵列不可能在每个浏览器中都使用,但犬科动物表示,85%的互联网都可以使用它们:http://caniuse.com/#feat=typedarrays
- Rails将JavaScript对象存储到Model的有效方式
- 如何在jQuery中以有效的方式创建链接
- 如何以最有效的方式实现这个多维数组
- HTML5使用Javascript填充文本标记的有效方式
- 以有效的方式搜索对象列表 mongo
- HTML5游戏中最有效的视差方式
- 在网页上连续播放mp3的最有效方式(如网络收音机)
- 接收json异步响应的最有效方式
- 解析JSON的有效方式
- 如何以有效的方式按给定数组对节点列表进行排序
- 有效方式:一事多插或多道事多插
- 从mobilejs/html5向服务器发送数据的最有效方式是什么,反之亦然
- 浏览器加载资源的最有效方式
- 在HTTP响应中发送大型比特流的有效方式
- 选择器的有效方式
- 在单个网页上一次显示一个调查声明的有效方式
- 在url中传递数组的有效方式
- PHP和JavaScript传输数据的最有效方式是什么?
- 使用jQuery链接滚动到部分页面的有效方式
- 基于时间运行代码的有效方式