Java: convertendo string de e para bytebuffer e problemas associados
-
12-09-2019 - |
Pergunta
Estou usando o Java Nio para minhas conexões de soquete, e meu protocolo é baseado em texto, por isso preciso ser capaz de converter strings em bytesbuffers antes de escrevê -los no SocketchAnnel e converter os bytebuffers de entrada em cordas. Atualmente, estou usando este código:
public static Charset charset = Charset.forName("UTF-8");
public static CharsetEncoder encoder = charset.newEncoder();
public static CharsetDecoder decoder = charset.newDecoder();
public static ByteBuffer str_to_bb(String msg){
try{
return encoder.encode(CharBuffer.wrap(msg));
}catch(Exception e){e.printStackTrace();}
return null;
}
public static String bb_to_str(ByteBuffer buffer){
String data = "";
try{
int old_position = buffer.position();
data = decoder.decode(buffer).toString();
// reset buffer's position to its original so it is not altered:
buffer.position(old_position);
}catch (Exception e){
e.printStackTrace();
return "";
}
return data;
}
Isso funciona na maioria das vezes, mas questiono se essa é a maneira preferida (ou mais simples) de fazer cada direção dessa conversão ou se há outra maneira de tentar. Ocasionalmente, e aparentemente aleatoriamente, chamadas para encode()
e decode()
vai jogar umjava.lang.IllegalStateException: Current state = FLUSHED, new state = CODING_END
Exceção, ou similar, mesmo se eu estiver usando um novo objeto de bytebuffer cada vez que uma conversão é feita. Preciso sincronizar esses métodos? Alguma maneira melhor de converter entre cordas e bytesbuffers? Obrigado!
Solução
Confira o CharsetEncoder
e CharsetDecoder
Descrições da API - você deve seguir um Sequência específica de chamadas de método para evitar esse problema. Por exemplo, para CharsetEncoder
:
- Redefinir o codificador através do
reset
método, a menos que não tenha sido usado antes; - Invocar o
encode
Método zero ou mais vezes, desde que a entrada adicional possa estar disponível, passandofalse
para o argumento do endofinput e preenchendo o buffer de entrada e a liberação do buffer de saída entre as invocações; - Invocar o
encode
Método uma última vez, passandotrue
para o argumento do endofinput; e depois - Invocar o
flush
Método para que o codificador possa liberar qualquer estado interno no buffer de saída.
A propósito, essa é a mesma abordagem que estou usando para o NIO, embora alguns de meus colegas estejam convertendo cada char diretamente em um byte, com conhecimento que eles estão usando apenas ASCII, que eu posso imaginar provavelmente é mais rápido.
Outras dicas
A menos que as coisas tenham mudado, você está melhor com
public static ByteBuffer str_to_bb(String msg, Charset charset){
return ByteBuffer.wrap(msg.getBytes(charset));
}
public static String bb_to_str(ByteBuffer buffer, Charset charset){
byte[] bytes;
if(buffer.hasArray()) {
bytes = buffer.array();
} else {
bytes = new byte[buffer.remaining()];
buffer.get(bytes);
}
return new String(bytes, charset);
}
Normalmente, o buffer.hasarray () será sempre verdadeiro ou sempre falso, dependendo do seu caso de uso. Na prática, a menos que você realmente queira funcionar em qualquer circunstância, é seguro otimizar o ramo que você não precisa.
A resposta de Adamski é boa e descreve as etapas em uma operação de codificação ao usar o método Geral Encodes (que pega um buffer de byte como uma das entradas)
No entanto, o método em questão (nesta discussão) é uma variante da codificação - codificar (charbuffer in). Isto é um Método de conveniência que implementa toda a operação de codificação. (Por favor, consulte a referência do Java Docs no PS)
De acordo com os documentos, Portanto, este método não deve ser invocado se uma operação de codificação já estiver em andamento (que é o que está acontecendo no código do Zenblender - usando o codificador/decodificador estático em um ambiente com vários rosqueados).
Pessoalmente, eu gosto de usar conveniência Métodos (sobre os métodos mais gerais de codificação/decodificação) enquanto eles tiram o ônus executando todas as etapas sob as cobertas.
Zenblender e Adamski já sugeriram várias maneiras de fazer isso com segurança em seus comentários. Listando todos eles aqui:
- Crie um novo objeto de codificador/decodificador quando necessário para cada operação (não eficiente, pois pode levar a um grande número de objetos). OU,
- Use um ThreadLocal para evitar a criação de um novo codificador/decodificador para cada operação. OU,
- Sincronizar toda a operação de codificação/decodificação (isso pode não ser preferido, a menos que sacrificar alguma simultaneidade seja bom para o seu programa)
Ps
Java Docs Referências:
- Método de codificação (conveniência): http://docs.oracle.com/javase/6/docs/api/java/nio/charset/chartesencoder.html#encode%28java.nio.charbuffer%29
- Método de codificação geral: http://docs.oracle.com/javase/6/docs/api/java/nio/charset/chartesencoder.html#encode%28java.nio.charbuffer ,%20java.nio.byteBuffer ,%20BoOLean%29