网上说是由于内存溢出导致的 可我通过打印 bufferWritePos 和 bufferReadPos 以及process.memoryUsage 没有发现指针越界呀 以下是服务端代码,主要目的是接收客户端传来的流数据,然后截取图像数据保存在文件中。客户端数据 包大小20647字节 发送间隔10ms, 运行一会就会报:“段错误 (核心已转储)”
/**
* Created by fanlin on 7/28/15.
*/
'use strict'
var port = process.argv[2];
var net = require('net');
var fs = require('fs');
var path = require('path');
var util = require('util');
var log = require('../lib/log');
var dataBuffer = undefined; //数据缓冲区
var decoderList = [];
var fWriteStream = undefined;
var maxISKSize = 0;
var bufferWritePos = 0; //缓冲区写指针的位置
var bufferReadPos = 0; //缓冲区读指针的位置
var totalReceive = 0;
var tmpDataPath = '../temp/tmp.dat';
//启动脚本
start();
function start(){
try{
//初始化
initServer();
//创建服务
createServer();
}catch (err){
console.log(err);
}
}
function initServer(){
//提前加载所有的decoder
var pathname = path.resolve(__dirname, '../decoder');
var files = fs.readdirSync(pathname.toString());
files.forEach(function(file){
var filename = pathname + '/' + file;
var flg = fs.lstatSync(filename);
if(!flg.isDirectory()){
var item = require(filename);
if(item.hasOwnProperty('infoType') && item.hasOwnProperty('iskSize')){
decoderList[item['infoType']] = item;
//找到数据的最大长度, 拆分数据时根据此数值创建数据缓冲区的大小
if(maxISKSize < item['iskSize']){
maxISKSize = item['iskSize'];
}
}
}
});
}
function createServer(){
var server = net.createServer(function(socket){
console.log('socket connected');
socket.write('connected');
socket.setTimeout(15*1000);
log.info("建立链接,Client Address:" + socket.remoteAddress);
//创建数据缓冲区
dataBuffer = new Buffer(maxISKSize*200);
var pathname = path.resolve(__dirname, '../temp');
tmpDataPath = pathname.toString() + '/' + 'tmp.dat';
fs.open(tmpDataPath, 'w', function(err, fd){
if(err) {
log.warning(err.toString());
}
fWriteStream = fs.createWriteStream(tmpDataPath);
});
socket.on('data', function(data){
//接收数据 并根据数据内容进行处理
try {
//console.log(data.length);
dataBuffer.fill(data, bufferWritePos, bufferWritePos + data.length);
bufferWritePos += data.length;
dataSplite(data);
}
catch (e){
console.log(e.message);
}
});
socket.on('close', function(err){
if(err){
console.error(err);
log.warning("Socket出现一次异常关闭,此链接已断开,Client Address:" + socket.remoteAddress + ". ERR:" + err.msg)
}else{
//console.error("Socket正常关闭。");
}
log.info("关闭链接,Client Address:" + socket.remoteAddress);
});
socket.on('error', function(err){
console.error(err);
try{
log.error("Socket异常关闭,Client Address:" + socket.remoteAddress);
}catch (err){
log.error("Socket异常关闭,Client Address:" + socket.remoteAddress);
}
});
socket.on('end', function(){
//会话终止
console.log('Receive Total: ' + totalReceive);
fWriteStream.end();
totalReceive = 0;
});
socket.on('timeout', function(){
})
});
server.listen(port, function(){
});
}
function dataSplite(data){
/*
* 首先开辟一个buffer 大小为所有数据帧长的最大值
* 将数据放入buffer中。
* 根据首2个字节确定 decoderType
* 通过decoderType 确定数据帧长 (如果是定长数据帧可直接确定,如果是变长帧则需要通过第3、4两个字节确定实际帧长)
* 1.定长帧处理方式:
* 判断当前数据长度,
* 如果 = 期望帧长 则直接处理
* 如果 < 期望帧长 则填入buffer等待后续数据
* 如果 > 期望帧长 则截取出期望帧长的数据,然后将剩余数据填入buffer等待后续数据
* 2.变长帧处理方式:
* 暂不需要
*/
var total = bufferWritePos;
bufferReadPos = 0;
while(bufferReadPos < total){
var infoType = 2;//data.readUInt16LE(0);
var iskSize = getISKSize(infoType);
var isVarLenght = (iskSize == -1) ? true : false;
if(isVarLenght){
//如果是变长isk 则进行一下处理
//TODO:暂不考虑
}else{
//
}
/*
* 如果 iskSize 等于 0 则表示 infoType 为非法
* 应丢弃缓冲区中的所有数据
*/
if(iskSize == 0){
bufferWritePos = 0;
return;
}
if(bufferReadPos + iskSize <= total){
//分割出一个完整的isk数据,进行后续处理
var iskData = new Buffer(iskSize);
dataBuffer.copy(iskData, 0, bufferReadPos, bufferReadPos + iskSize);
dataProcess(iskData, decoderList[infoType]);
//读指针偏移
bufferReadPos += iskSize;
if(bufferReadPos == total){
bufferWritePos = 0;
}
}else{
/*
* 目前的缓冲区中的数据无法组成一个完整的isk消息
* 将当前缓冲区中的数据全部移动到缓冲区的前端,以等待后续数据
*/
bufferWritePos = total - bufferReadPos;
if(bufferReadPos > 0) {
dataBuffer.copy(dataBuffer, 0, bufferReadPos, total);
}
break;
}
}
}
function dataProcess(data, decoderObj){
//测试代码段
totalReceive += data.length;
console.log("wp : " + bufferWritePos +
"; rp : " + bufferReadPos +
"; mem: " + util.inspect(process.memoryUsage()));
if(decoderObj.hasOwnProperty('execute')){
var imageCode = decoderObj.execute(data);
fWriteStream.write(data);
//saveImageCode(imageCode, decoderObj);
}
}
function saveImageCode(code, decoderObj){
var route;
/*
if(decoderObj.hasOwnProperty('decoderName')){
route = '../temp/' + decoderObj.decoderName + '.dat';
}else{
route = '../temp/tmp.dat';
}
*/
}
function getISKSize(infoType) {
if (decoderList[infoType] == undefined) {
return 0;
} else {
return decoderList[infoType].iskSize;
}
}
段错误不仅仅是指针越界错误,访问非法指针(访问不存在的内存),栈溢出都有可能出现。还有一些莫名其妙的程序跑飞掉(多数因为一些不起眼的错误)都会引起段错误。 关键是定位,如果是linux可以用valgrid,用debug版本来调试定位。 如果确定在这个代码范围之内的话,可以注释排除,定位后再找出错原因。
//创建数据缓冲区
dataBuffer = new Buffer(maxISKSize*200);
这一句可能性比较大,var maxISKSize = 0;,new Buffer(0)应该是分配到了一个合法的地址,但是这个地址并没有可访问的内存。 具体见源代码src/smalloc.cc440行,malloc(0)返回的不一定是nullptr,可能是一个合法的指针,具体要看malloc的实现,如果是合法的指针,这个指针其实没有任何可访问的内存空间,所以你后续对内存的访问都是非法的。内存分配成功了,但是只是分配了内存头(储存内存链表信息),但是实际供用户使用的内存空间为0。
@coordcn 强!
@coordcn 能推荐一个比较好用的内存泄露调试工具么? valgrind 网上查不到如何调试js代码。
@FanTaSyLin valgrind是为c或c++服务的,内存泄漏检测只是它的一个功能,我主要用它来检测非法内存访问,js不知道用什么来调试这个问题,主要是自己小心吧。你碰到的这个问题如果的确像我说的,那这不是js的问题,而是c的问题。
内存泄漏检测工具可以问站长或其他高手,这个我不了解。我个人认为主要还是要依靠良好的编码习惯,清楚哪些代码可能会出现内存泄漏,自己编码的时候时刻注意。
段错误是最恶心的错误,很多时候都是莫名其妙的,我前段时间在一个段错误上耗了半个月时间,到最后才弄明白的,我在用lua + libuv实现一个伪同步的tcpserver,其中用到uv_timer_t,我原来以为timer不是handle,不需要像uv_tcp_t一样显式的uv_close,没有经过close的timer其实是在一个链表上的,直接释放某个timer的内存会导致链表前后的timer试图访问一个不能访问的内存,段错误就发生了。我之前一直以为是lua自动内存回收造成的,以为lua对内存管理有问题,其实是自己没有好好理解libuv的timer造成的。