Używam Java NIO do połączeń przez gniazdo, a mój protokół jest oparty na tekście, więc przed zapisaniem ich w SocketChannel muszę być w stanie przekonwertować ciągi znaków na ByteBuffers i przekonwertować przychodzące ByteBuffers z powrotem na ciągi. Obecnie używam tego kodu:
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;
}
Działa to przez większość czasu, ale pytam, czy jest to preferowany (lub najprostszy) sposób wykonania każdego kierunku tej konwersji, czy też istnieje inny sposób, aby spróbować. Sporadycznie i pozornie losowo wywołuje encode()
i zgłasza wyjątek lub decode()
coś
java.lang.IllegalStateException: Current state = FLUSHED, new state = CODING_END
podobnego, nawet jeśli używam nowego obiektu ByteBuffer za każdym razem, gdy wykonywana jest konwersja. Czy muszę zsynchronizować te metody? Czy jest jakiś lepszy sposób na konwersję między ciągami znaków i ByteBuffers? Dzięki!
źródło
Odpowiedzi:
Zapoznaj się z opisami interfejsu API
CharsetEncoder
iCharsetDecoder
- aby uniknąć tego problemu, należy wykonać określoną sekwencję wywołań metod . Na przykład dlaCharsetEncoder
:reset
metody, chyba że nie był wcześniej używany;encode
metodę zero lub więcej razy, o ile dostępne są dodatkowe dane wejściowe, przechodzącfalse
argument endOfInput i wypełniając bufor wejściowy i opróżniając bufor wyjściowy między wywołaniami;encode
metodę po raz ostatni, przechodząctrue
argument endOfInput; i wtedyflush
metodę, aby koder mógł opróżnić dowolny stan wewnętrzny do bufora wyjściowego.Nawiasem mówiąc, to jest to samo podejście, którego używam dla NIO, chociaż niektórzy z moich kolegów konwertują każdy znak bezpośrednio na bajt, wiedząc, że używają tylko ASCII, co, jak mogę sobie wyobrazić, jest prawdopodobnie szybsze.
źródło
O ile nic się nie zmieniło, lepiej będzie z
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); }
Zazwyczaj wartość buffer.hasArray () zawsze będzie miała wartość true lub zawsze false, w zależności od przypadku użycia. W praktyce, chyba że naprawdę chcesz, aby działało w żadnych okolicznościach, możesz bezpiecznie zoptymalizować gałąź, której nie potrzebujesz.
źródło
Odpowiedź Adamskiego jest dobra i opisuje kroki w operacji kodowania przy użyciu ogólnej metody kodowania (która przyjmuje bufor bajtów jako jedno z wejść)
Jednak omawiana metoda (w tej dyskusji) jest wariantem encode - encode (CharBuffer in) . Jest to wygodna metoda, która implementuje całą operację kodowania . (Zobacz dokumentację java w PS)
Zgodnie z dokumentacją, ta metoda nie powinna zatem być wywoływana, jeśli operacja kodowania jest już w toku (co dzieje się w kodzie ZenBlender - przy użyciu statycznego kodera / dekodera w środowisku wielowątkowym).
Osobiście lubię korzystać z wygody metod (zamiast bardziej ogólnych metod kodowania / dekodowania), ponieważ odciążają one wykonywanie wszystkich czynności pod okładkami.
ZenBlender i Adamski zasugerowali już wiele sposobów bezpiecznego zrobienia tego w swoich komentarzach. Wymieniając je wszystkie tutaj:
PS
odniesienia do dokumentacji java:
źródło