소스코드는 이러합니다~
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.bytes.ByteArrayDecoder;
@Sharable
public class CustomDataDecoder extends ByteArrayDecoder{
// 단편화 데이터 임시 저장 버퍼
private static ByteBuf incompleteBuf = Unpooled.buffer();
// 단편화 여부 확인값
private static boolean isIncomplete = false;
Logger logger = LogManager.getLogger(this.getClass());
public CustomDataDecoder() {
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
ByteBuf buffer = Unpooled.buffer();
buffer = (ByteBuf)msg;
int length = buffer.readableBytes();
byte[] bytes = new byte[length];
while(buffer.isReadable()){
bytes[buffer.readerIndex()] = buffer.readByte();
}
// 데이터 시작 추가된 구분자 |
char startCode = 124;
byte delimterByte = (byte)startCode;
// 데이터 마지막 부분 구분자
char lastCode = 127;
byte lastCharCode = (byte)lastCode;
boolean hasStartDelimiter = false;
boolean hasEndDelimiter = false;
// 패킷 분석 시작
// 데이터 시작 끝 포함 여부 확인
for (int i = 0 ; i < bytes.length; i++){
if(bytes[i] == delimterByte){
hasStartDelimiter = true;
} else if(bytes[i] == lastCharCode){
hasEndDelimiter = true;
}
}
// 완전한 상태의 패킷 일 경우 바로 그냥 전송
if(bytes[0] == delimterByte && bytes[bytes.length-1] == lastCharCode){
ByteBuf returnByte = Unpooled.copiedBuffer(bytes);
out.add(returnByte);
//System.out.println("CASE 0");
// 완전하지 않은 상태의 패킷 CASE 1 : TEMP 데이터가 없을 경우 (첫 데이터)
} else if(bytes[0] == delimterByte && bytes[bytes.length-1] != lastCharCode && !isIncomplete){
incompleteBuf.writeBytes(bytes);
isIncomplete = true;
//System.out.println("CASE 1");
// 완전하지 않은 상태의 패킷 CASE 2 : TEMP 데이터가 있는 경우
// TEMP 데이터가 있는데 새로운 시작점이 들어온 경우
} else if(bytes[0] == delimterByte && bytes[bytes.length-1] != lastCharCode && isIncomplete){
if(hasEndDelimiter){
int endIndex = 0;
// 데이터 끝문자 포함 여부 및 위치 찾기
for (int i = 0 ; i < bytes.length; i++){
if(bytes[i] == lastCharCode){
endIndex = i;
}
}
byte[] releaseData = Arrays.copyOfRange(bytes, 0, endIndex+1);
ByteBuf returnByte = Unpooled.copiedBuffer(releaseData);
out.add(returnByte);
byte[] remainData = Arrays.copyOfRange(bytes, endIndex+1, bytes.length);
incompleteBuf.writeBytes(remainData);
} else {
incompleteBuf.writeBytes(bytes);
}
//System.out.println("CASE 2");
// 완전하지 않은 상태의 패킷 CASE 3 : TEMP 데이터가 있을 경우 (끝부분은 delimiter 존재)
} else if(bytes[0] != delimterByte && bytes[bytes.length-1] == lastCharCode && isIncomplete){
incompleteBuf.writeBytes(bytes);
byte[] tempBytes = new byte[incompleteBuf.readableBytes()];
int readerIndex = incompleteBuf.readerIndex();
incompleteBuf.getBytes(readerIndex, tempBytes);
int lastStartIndex = 0;
int startCount = 0;
int endCount = 0;
// TempByte의 데이터 체크
for (int i = 0 ; i < tempBytes.length; i++){
// 마지막 시작점 취득
if(tempBytes[i] == delimterByte){
lastStartIndex = i;
startCount++;
} else if(tempBytes[i] == lastCharCode){
endCount++;
}
}
// 시작점 종료점 카운트가 같으면 전부 전송
if(startCount == endCount){
ByteBuf returnByte = Unpooled.copiedBuffer(tempBytes);
out.add(returnByte);
incompleteBuf.discardReadBytes();
incompleteBuf.resetReaderIndex();
incompleteBuf.resetWriterIndex();
isIncomplete = false;
// 시작점 종료점 카운트가 같지 않으면 뒤에서 부터 추출하여 완성된 부분 전송
} else {
// 마지막 완성된 형태 커맨드 추출
byte[] releaseData = Arrays.copyOfRange(tempBytes, lastStartIndex, tempBytes.length);
if(tempBytes[lastStartIndex-1] == lastCharCode){
byte[] lastRemainData = Arrays.copyOfRange(tempBytes, 0, lastStartIndex);
int middleStartIndex = 0;
// TempByte의 데이터 체크
for (int i = 0 ; i < lastRemainData.length; i++){
// 마지막 시작점 취득
if(lastRemainData[i] == delimterByte){
middleStartIndex = i;
}
}
byte[] secondReleaseData = Arrays.copyOfRange(lastRemainData, middleStartIndex, lastRemainData.length);
byte[] concatBytes = ArrayUtils.addAll(releaseData,secondReleaseData);
incompleteBuf.discardReadBytes();
incompleteBuf.resetReaderIndex();
incompleteBuf.resetWriterIndex();
ByteBuf returnByte = Unpooled.copiedBuffer(concatBytes);
out.add(returnByte);
byte[] remainData = Arrays.copyOfRange(tempBytes, 0, middleStartIndex);
incompleteBuf.writeBytes(remainData);
} else {
incompleteBuf.discardReadBytes();
incompleteBuf.resetReaderIndex();
incompleteBuf.resetWriterIndex();
ByteBuf returnByte = Unpooled.copiedBuffer(releaseData);
out.add(returnByte);
byte[] remainData = Arrays.copyOfRange(tempBytes, 0, lastStartIndex);
incompleteBuf.writeBytes(remainData);
}
}
//System.out.println("CASE 3");
// 완전하지 않은 상태의 패킷 CASE 4 : 중간에 delimiter가 존재
} else if(hasEndDelimiter && bytes[bytes.length-1] != lastCharCode && isIncomplete){
incompleteBuf.writeBytes(bytes);
byte[] tempBytes = new byte[incompleteBuf.readableBytes()];
int readerIndex = incompleteBuf.readerIndex();
incompleteBuf.getBytes(readerIndex, tempBytes);
incompleteBuf.discardReadBytes();
incompleteBuf.resetReaderIndex();
incompleteBuf.resetWriterIndex();
int startDelimiterIndex = 0;
int endDelimiterIndex = 0;
boolean alreadyCheck = false;
// 서버 접속 코드 포함 여부 및 위치 찾기
for (int i = 0 ; i < tempBytes.length; i++){
if(tempBytes[i] == delimterByte && !alreadyCheck){
startDelimiterIndex = i;
alreadyCheck = true;
} else if(tempBytes[i] == lastCharCode){
endDelimiterIndex = i;
}
}
byte[] releaseData = Arrays.copyOfRange(tempBytes, startDelimiterIndex, endDelimiterIndex+1);
ByteBuf returnByte = Unpooled.copiedBuffer(releaseData);
out.add(returnByte);
// 처음이 데이터 시작 부분이면 마지막 데이터 끝 구분자 이후만 버퍼에 저장
if(startDelimiterIndex == 0){
byte[] remainData = Arrays.copyOfRange(tempBytes, endDelimiterIndex+1, tempBytes.length);
incompleteBuf.writeBytes(remainData);
} else {
byte[] firstRemainData = Arrays.copyOfRange(tempBytes, 0, startDelimiterIndex);
byte[] lastRemainData = Arrays.copyOfRange(tempBytes, endDelimiterIndex+1, tempBytes.length);
byte[] concatBytes = ArrayUtils.addAll(firstRemainData,lastRemainData);
incompleteBuf.writeBytes(concatBytes);
}
//System.out.println("CASE 4");
// 완전하지 않은 상태의 패킷 CASE 5 : 첫부분과 끝부분이 없음
} else if(!hasStartDelimiter && !hasEndDelimiter && isIncomplete){
incompleteBuf.writeBytes(bytes);
//System.out.println("CASE 5");
} else {
String tempBuffer = new String(bytes, "UTF-8").trim();
logger.debug("Unknown Message");
logger.debug(tempBuffer);
//System.out.println("Unknown Case");
}
}
}