본문 바로가기

Node

TCP 이해하기

TCP 로 서버 열기

 

TCP는 기본적으로 지원해주는 net모듈을 사용하여 서버를 열 수 있다.

import net from "net";

express와 비슷하지만 TCP서버는 기본적으로 제공해주는 createServer 메서드를 사용하여 서버를 선언해 준다.

const server = net.createServer((socket)=>{
console.log("연결확인")
//socket.id === undefined
})

선언한 서버를 listen을 사용하여 실행을 시켜준다.

server.listen('포트번호','127.0.0.1(host)',"10(backlog 선택사항)"()=>{});
  • backlog
    사람이 몰릴 경우를 대비하여 대기자 수를 지정하여 설정할 수 있다.
  • config
    config폴더는 일반적으로 소프트웨어 애플리케이션이나 시스템의 설정 파일을 저장하는 디렉토리이다. 이 폴더에는 애플리케이션의 동작 방식, 사용자 설정 , 환경변수 등과 관련된 다양한구성파일이 포함되어 있다.
//config.js 예시
export const config = {
  server: {
    HOST: "127.0.0.1",
    PORT: 5555,
  },
  client: {
    version: "2.0.1",
  },
};

net.createServer가 소켓을 받아오는 이유

net.createServer는 node.js의 net 모듈에서 제공하는 함수로 TCP 서버를 생성하는데 사용된다. 이 함수가 소켓을 받는 이유는 다음과 같다.
  1. 클라이언트와의 연결 TCP서버는 클라이언트가 연결할 때마다 소켓을 생성한다. 이 소켓은 클라이언트와 서버 간의 통신 경로를 제공한다.
  2. 데이터 전송 소켓을 통해 클라이언트와 서버 간에 데이터를 주고받을 수 있다. 각 클라이언트 연결은 별도의 소켓을 통해 처리되므로 동시에 여러 클라이언트의 연결이 가능하다.
  3. 이벤트 기반 처리 소켓은 다양한 이벤트 (data,end,error 등)를 발생시킨다. 이를통해 서버는 클라이언트의 요청을 비동기적으로 처리할 수 있다.
  4. 연결 관리 각각의 소켓은 특정 클라이언트와의 연결 정보를 가지고 있어 , 서버는 클라이언트의 상태를 관리하고 연결을 종료하는 등의 작업을 수행할 수 있다.

직렬화 와 역직렬화

  • 직렬화 객체나 데이터를 일련의 바이트로 변환하는 과정이다.
  //직렬화 스트링 -> 바이트 배열
  const message = "HanWooJong";
  const buffer = Buffer.from(message);
  client.write(buffer);
  • 역직렬화 직렬화된 데이터를 원래의 객체나 데이터 구조로 변환하는 과정이다.
  //역직렬화 바이트배열 ->스트링
  socket.on("data", (data) => {
    console.log(data);//바이트배열
    console.log(data.toString()); //파싱->스트링
  });
});
  • 직렬화 → 역직렬화 예시

 

직렬화 : 객체 ”name” : ”hong” , ”age” : 30}를 JSON문자열 {”name”:”hong”,”age”,30}으로 변환 
역직렬화 : JSON문자열{”name”:”hong”,”age”,30}을 다시 객체{ ”name” : ”hong” , ”age” : 30} 로 변환

버퍼배열의 총 길이 정의

버퍼의 총 길이를 정의하는 이유는 버퍼의 총 길이를 알고 있다면 수신하거나 저장한 데이터의 무결성을 검증할 수 있기 때문이다. 예를 들어 예상한 길이와 실제 길이를 비교하여 데이터가 손실되거나 변조되지 않았는지 확일할 수 있다.
또한 버퍼의 총 길이를 정의하면 메모리 사용을 최적화할 수 있다. 필요한 만큼의 메모리를 할당하고 불필요한 메모리 사용을 줄일 수 있으며 대용량 데이터를 처리할때 효율적으로 처리할 수 있다.
버퍼의 총 길이를 정의하였다면 이를 기준으로 오류를 감지하고 처리할 수 있다.
수신한 데이터가 예상보다 짧거나 길다면 해당 패킷을 무시하거나 재전송을 요청하는 프로세스를 구현할여 에러 처리 및 흐름 제어에 용이하다.

총 길이와 가변 데이터

총 길이 =4바이트 라고 가정해보자
여기서 총 길이는 패킷 전체의 크기를 나타낸다.
jong이라는 데이터를 버퍼로 감싸면 <Buffer 6a 6f 6e 67>이런 형식의 배열이 나오게 되는데
여기서 총 길이를 합치면 <Buffer 00 00 00 04 6a 6f 6e 67> 이런 방식의 배열이 나오게된다.
위처럼 직렬화된 바이트 배열을 직렬화 하기전에 직렬화 해줘도 되는값인지 검증을 해주어야한다.
총 길이가 4 인데 전달받은 패킷의 길이가 4보다 작다면 그건 문제가 있으므로
패킷의 길이가 4보다 크거나 같을 경우에만 검증을 시작한다.
socket.on("data", (data) => {
    while (data.length >= 4) {
      const packetLength = data.readInt32BE(0);
      if (data.length >= packetLength) {
        console.log(packetLength);
      } else {
        console.log(`패킷 도작 안함`);
        break;
      }
    }

 

여기서 readInt32BE 메서드를 통해 packetLength = buffer의 4번째(총 길이) 까지의 값까지만 읽어온다.
data.length가 그보다 크거나 같을 경우 패킷 전달 완료메시지를 보내준다.
하지만 실제 완료 데이터는 4번째(총 길이)가 아닌 그 이후부터이기때문에 다시한번 subarray로 잘라준다.
  socket.on("data", (data) => {
    while (data.length >= 2) {
      const packetLength = data.readInt32BE(0);
      if (data.length >= packetLength) {
        const realData = data.subarray(4, packetLength);//패킷의 총 길이까지만 
        console.log(realData.toString());
      } else {
        console.log(`패킷 도작 안함`);
        break;
      }
    }
  });
});

 

왜 data.subarray(4, packetLength) 에서 packetLength까지인가?
서로 다른데이터 2개를 빠르게 2번 보낸다고 생각해보자
<00 10 48 61 6e 57 6f 6f 4a 6f 6e 67> - 가
<00 10 48 61 6e 57 6f 6f 4a 6f 6e 67> - 나
의 데이터가 있다고 했을경우
48-가 61-가 6e-나 57-가 6f-가 이처럼 데이터가 오는 도중 데이터 하나가 넘어와 합쳐질 경우 데이터의 길이가 10 이아닌 11이 되는 상황이 발생한다 이를 방치하기위해 packetLength 까지만 subarray로 잘라 보내준다.

 

'Node' 카테고리의 다른 글

process.exit() 메서드  (0) 2024.11.01
객체지향 패턴(Object-Oriented Patterns)  (0) 2024.10.29
socket 이벤트 / 버퍼(Buffer)  (0) 2024.10.23
Socket.io 기본 기능  (0) 2024.09.30
webSocket 게임 서버 개발 강의 코드 이해하기  (1) 2024.09.27