2014-11-05 8 views
0

Я написал простой TCP-сервер на node.js для отправки некоторых данных в приложение Chrome. В хромированном приложении, когда я получаю данные, конвертировать, что в строку, используя ниже функции, я получаю исключение «длиной байта Uint16Array должна быть кратна 2»как преобразовать arraybuffer в строку

String.fromCharCode.apply(null, new Uint16Array(buffer)) 

Я не мог найти информацию о том, что может быть причиной этого и как это исправить. Любые указатели на это высоко ценятся.

Ниже приведен код в Node.js сервера для отправки данных клиента:

socket.on('data', function(data) { 

    console.log('DATA ' + socket.remoteAddress + ': ' + data); 
    // Write the data back to the socket, 
    // the client will receive it as data from the server 
    var r= socket.write('from server\r\n'); 

}); 

Ниже приведен код из хромированного приложения:

chrome.sockets.tcp.onReceive.addListener(function (info) { 
      console.log('onListener registered'); 
      if (info.socketId != socketid) 
       return; 
      else { 
       try { 

        data = ab2str(info.data); 
        console.log(data); 
       } 
       catch (e) { 
        console.log(e); 
       } 

      } 
      // info.data is an arrayBuffer. 
     }); 

function ab2str(buf) { 
    return String.fromCharCode.apply(null, new Uint16Array(buf)); 
} 
+0

вы можете показать больше, как вы получаете буфер? – Victory

+0

В общем, [этот вопрос] (https://stackoverflow.com/questions/6965107/converting-between-strings-and-arraybuffers) является каноническим. Впрочем, я не собираюсь шутить. – Xan

ответ

6

Вы, вероятно, видите эту проблему, потому что ваше приложение получило нечетное количество байтов в сокете, но вы пытаетесь создать из него массив из 2-байтных предметов (потому что это то, что подходит для Uint16Array)

Если ваше приложение получает строку "Hello" по сети (5 байт), то вы можете бросить, что в Uint8Array, и это будет выглядеть следующим образом:

Item:  0 1 2 3 4 
Char:  H e l l o 
Uint8 Value: 72 101 108 108 111 

приведения его к Uint16Array, хотя будет пытаться сделать это:

Item 0  1  2 
Chars He ll o? 
IntVal 25928 27756 ????? 

Без 6-го байта, с которым можно работать, он не может построить массив, и поэтому вы получите исключение.

Использование данных Uint16Array для данных имеет смысл только в том случае, если вы ожидаете строковые данные UCS-2 на сокете. Если вы получаете простые данные ASCII, тогда вы хотите отдать это Uint8Array вместо этого, и наведите на карту String.fromCharCode. Если это что-то другое, например UTF-8, вам придется сделать другое преобразование.

Независимо от того, что слой сокета всегда может отправлять вам данные в кусках любой длины. Вашему приложению придется иметь дело с нечетными размерами и сохранять остаток, с которым вы не можете справиться сразу, чтобы вы могли использовать его, когда получаете следующий фрагмент данных.

+0

И решение будет ... ??? – CesarD

+0

Это зависит от приложения: вместо этого вы можете использовать Uint8Array или, как я уже сказал в последнем абзаце, буферизировать любой конечный байт, а затем использовать его в качестве первого байта, когда вы получите следующий пакет данных. –

+0

Моя проблема заключалась в том, что у меня был тип responsebypeer arraybuffer, который возвращал ошибку, но часть текста внутри ошибки содержала символы с акцентами, и они были декодированы как мусор, почти нечитаемые символы. Я попытался декодировать его с помощью Uint16Array, как OP, но получил ту же ошибку. Я нашел это: https://gist.github.com/boushley/5471599, и я смог правильно декодировать строку, не используя Uint16Array. – CesarD

1

Вид старого и поздно, но, возможно, с помощью этой функции (original source) работает лучше (он работал для меня для декодирования ArrayBuffer в строку, не оставляя некоторые специальные символы, как полная мусора):

function decodeUtf8(arrayBuffer) { 
    var result = ""; 
    var i = 0; 
    var c = 0; 
    var c1 = 0; 
    var c2 = 0; 

    var data = new Uint8Array(arrayBuffer); 

    // If we have a BOM skip it 
    if (data.length >= 3 && data[0] === 0xef && data[1] === 0xbb && data[2] === 0xbf) { 
    i = 3; 
    } 

    while (i < data.length) { 
    c = data[i]; 

    if (c < 128) { 
     result += String.fromCharCode(c); 
     i++; 
    } else if (c > 191 && c < 224) { 
     if(i+1 >= data.length) { 
     throw "UTF-8 Decode failed. Two byte character was truncated."; 
     } 
     c2 = data[i+1]; 
     result += String.fromCharCode(((c&31)<<6) | (c2&63)); 
     i += 2; 
    } else { 
     if (i+2 >= data.length) { 
     throw "UTF-8 Decode failed. Multi byte character was truncated."; 
     } 
     c2 = data[i+1]; 
     c3 = data[i+2]; 
     result += String.fromCharCode(((c&15)<<12) | ((c2&63)<<6) | (c3&63)); 
     i += 3; 
    } 
    } 
    return result; 
} 
0

Существует асинхронный способ используя Blob и FileReader.

Вы можете указать любую допустимую кодировку.

function arrayBufferToString(buffer, encoding, callback) { 
    var blob = new Blob([buffer],{type:'text/plain'}); 
    var reader = new FileReader(); 
    reader.onload = function(evt){callback(evt.target.result);}; 
    reader.readAsText(blob, encoding); 
} 

//example: 
var buf = new Uint8Array([65,66,67]); 
arrayBufferToString(buf, 'UTF-8', console.log.bind(console)); //"ABC" 
+0

Вы можете захотеть просмотреть ['TextDecoder' API] (https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) синхронным способом. – Xan

+0

Спасибо. [TextDecoder] (https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) очень хорош, но поддерживается только firefox и chrome. Для IE и Safari требуется большая прокладка. – cuixiping

+0

Вопрос о приложении Chrome, поэтому время выполнения явно указано в Chrome. – Xan

2

Современный (Хром 38+) способ сделать это было бы, предполагая, что кодирование является UTF-8:

var decoder = new TextDecoder("utf-8"); 

function ab2str(buf) { 
    return decoder.decode(new Uint8Array(buf)); 
} 

Это использует TextDecoder API; см. documentation для получения дополнительных параметров, таких как различная кодировка.

Смотрите также: Easier ArrayBuffer<->String conversion with the Encoding API @ Google Developers

+0

Для других браузеров есть [TextDecoder shim/polyfill] (https://github.com/inexorabletash/text-encoding), которые не поддерживают этот API. – cuixiping

Смежные вопросы