티스토리 뷰

 

소켓 통신, 서버소켓을 구현하고 수신한 데이터(바이트)를 문자로 변환하는 작업을 해보게 되었다.

그 과정에서 알게 된 개념과 정보들을 기록해보고자 한다.

 

소켓(Socket)

**소켓(Socket)**은 TCP/IP 기반 네트워크 통신에서 데이터 송수신의 마지막 접점을 말한다. 소켓통신은 이러한 소켓을 통해 서버-클라이언트간 데이터를 주고받는 양방향 연결 지향성 통신을 말하는데, 보통 지속적으로 연결을 유지하면서 실시간으로 데이터를 주고받아야 하는 경우에 사용된다.

소켓은 클라이언트 소켓과 서버 소켓으로 구분되며, 소켓간 통신을 위해서는 네트워크상에서 클라이언트와 서버에 해당되는 컴퓨터를 식별하기 위한 IP주소와 해당 컴퓨터내에서 현재 통신에 사용되는 응용프로그램을 식별하기 위한 포트번호가 사용된다.

 

서버 소켓 구현하기

  1. 서버소켓 생성
ServerSocket serverSocket = new ServerSocket(8000);// 포트번호

2) 클라이언트 접속 대기

 Socket socket = serverSocket.accept( );

3) 데이터 송수신을 위한 input/output 스트림 생성

InputStream in = socket.getInputStream( );    OutputStream out = socket.getOutputStream( );

4)  input 스트림을 통한 데이터 수신 (클라이언트 → 서버)

byte[ ] inputData = new byte[100];    
int length = in.read(inputData);    
String inputMessage = new String(inputData, 0, length);

5) output 스트림을 통한 데이터 송신 (서버 → 클라이언트)

String outputMessage = "보낼메시지";    
out.write(outputMessage.getBytes( ));    
out.flush( );

6) 통신 종료

socket.close( );    
serverSocket.close( );

 

위와 같은 과정을 통해 서버소켓을 구현하는데, 나는 서버측에서 클라이언트에게 송신할 데이터는 따로 없이 대기상태에서 클라이언트로부터의 데이터만 수신하여 데이터를 변환, 적재 하는 기능을 구현하였다. 데이터가 수신될 때마다 쓰레드를 생성하고 쓰레드에서 데이터 작업을 처리하도록 되어있었다.

 

try {
	is = socket.getInputStream();
	buffer = new byte[bufSize];
} catch (IOException e) {
	log.error("===> SocketReceiver : " + e.getMessage());
}

데이터가 오면 버퍼를 생성하고

try {
    while ((readLen = is.read(buffer, 0, buffer.length)) != -1) {
    	while(readLen < buffer.length) {
   	buffer[readLen++] = 0x00;
   	}
    bos.write(buffer, 0, readLen);
}
 	byteArray = bos.toByteArray();
		```

이러한 과정을 거쳐 받은 데이터를 적재한다.

그리고 이 데이터를 다시 읽어 처리하는 작업을 했는데, 이 때 바이트 저장 순서와 관련있는 엔디안 이라는 개념을 만나게 되었다.

 

Endian ?

바이트 저장 순서

컴퓨터는 데이터를 메모리에 저장할 때 Byte 단위로 나눠서 저장한다. 따라서 연속되는 바이트를 순서대로 저장해야 하는데, 이것을 바이트 저장 순서(Byte Order)라고 한다. 이때 바이트가 저장된 순서에 따라 빅 엔디안, 리틀 엔디안 두 가지 방식으로 나눌 수 있다.

Bic Endian(빅 엔디안)

빅 엔디안 방식은 낮은 주소에 데이터의 높은 바이트(MSB : Most Significant Byte)부터 저장하는 방식이다. 이 방식은 평소 사람이 사용하는 선형 방식과 같아 메모리에 저장된 순서 그대로 읽을 수 있으며, 이해하기 쉽다.

상위 바이트의 값을 작은 번지수에 저장하는 방식.

위 그림과 같이 상위 바이트의 값 0x12를 작은 번지수인 0x20번지 부터 저장한다.

Little Endian(리틀 엔디안)

리틀 엔디안 방식은 낮은 주소에 데이터의 낮은 바이트(LSB : Least Significant Byte)를 저장하는 방식이다. 이 방식은 평소 사람이 숫자를 사용하는 선형 방식과 반대로 거꾸로 읽어야 한다.

상위 바이트의 값을 큰 번지수에 저장하는 방식.

위 그림과 같이 상위 바이트의 값 0x12를 큰 번지수인 0x23번지부터 저장한다.

cpu별로 저장방식이 다른데 cpu별 저장방식을 host byte order라고 하고,

네트워크에서는 network byte order라고 한다.

network byte order에서는 정수값을 주고 받을 경우 Big Endian방식으로 통일 되어있다.

Byte Order 변환

컴퓨터에서 사용하는 host byte order는 다를 수 있기 때문에 네트워크로 보낼 때 network byte order로 변환한 후 보내고, 상대방이 받았을 경우에도 network byte order에서 host byte order로 변환 후 사용한다.

참고

💡 x86 아키텍처가 리틀 엔디안을 쓰기 때문에 오늘날 x86 아키텍처를 사용하는 대부분의 데스크톱 컴퓨터는 리틀 엔디언 방식을 사용한다. 반면, 네트워크에서는 주소를 주로 빅 엔디언으로 쓰는데, 역사적으로 라우팅이 전화를 거는 식으로 접두 부호로 이루어졌기 때문이다.

작업할 때 little endian방식으로 데이터를 읽어와야 했으며, 또 하나 기억할 점은 byte를 HexString으로 변환하는 방법 이다.

byte를 HexString으로 변환하는 방법

byte 변수는 -127 ~ 127 범위를 표현하므로 127을 넘어가면 음수로 표출되어 이를 방지하기 위해 값 &0xff 연산을 하여 양수로 값을 표현해주는 작업을 하게 되었다.

&0xff 비트연산을 하는 이유?

: 의도치 않게 채워진 1을 전부 0으로 바꾸기 위해 수행한다.

  1. byte형은 8비트의 공간을 차지하고, int는 32비트의 공간을 차지한다.
  2. 비트연산자 &를 수행하는 경우, 비트수가 넓은 곳에 맞춰 낮은 비트를 가진 자료형을 확장한다.
  3. byteData & 0xff를 수행하는 경우, byte는 32비트의 int형으로 강제 형변환이 된다. 이 때 비트의 확장 시, byteData의 가장 앞의 비트가 0인 경우는 0으로, 1인 경우는 1로 모든 비트를 채워 확장한다.
  4. 이 경우 원본 값과 전혀 다른 값이 되기 때문에 &0xFF와 비트 연산을 수행한다.
  • &0xFF는 10진수 255, 2진수 11111111을 16진수로 나타낸 것이다. 8비트의 byte와 32비트(4바이트)의 int를 비트연산할 경우 다음과 같이 의도치 않게 확장되며 채워진 1을 0으로 변경한다.

 


널널한 개발자 youtube 강의를 보면,

Application 영역(User 영역)"에서 "Process"가 있고,

이 Process 밑에, "운영체제 수준(kernel 영역)"에서 "프로토콜"이 구현되어 있다.

이 프로토콜은 "TCP/IP"로 구현되어 있다.

Kernel의 구성요소를 User 모드 Application으로 추상화 할 때는, 어떤 file의 형태로 추상화를 한다.

이 때, 이 file은 Socket이라고 한다.

💡 TCP/IP Socket이라고 가정했을 때,

Socket은 TCP를 User 모드 Application 프로세스가 접근할 수 있도록 file 형식으로 추상화한 인터페이스가 속해진 것이다.

 

웹 공부를 하게 되면 웹도 결국 소켓통신이 본질인 것 같다.

클라이언트가 브라우저로 서버에 연결을 하고, 서로 응답과 요청을 하려면 byteStream이 필요할 것이다.

웹에서 이루어지는 통신은 http통신에 규칙을 따르는데, 이 http통신의 뿌리가 되는 통신이 소켓통신이 되는 것이다.

http와 소켓통신의 차이점중 하나는 http통신에서는 서버 부하를 줄이기 위해 많은 노력을 하는데 그 중 하나가 stateless로 상태를 관리하지 않는다, 연결이 지속되지 않는다는 점이고, 소켓통신은 stateful 로 연결이 끊기지 않고 지속된다.

'ALL > Java' 카테고리의 다른 글

Memory Analyzer(MAT) 툴 이용 Heap Dump 분석  (0) 2023.11.29
[Java] Thread  (0) 2023.11.27
Java 현재 시간 구하기 코드  (0) 2022.11.27
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함