[Node.js] TCP/IP 소켓 통신하기

[Node.js] TCP/IP 소켓 통신하기

Node.js에서 TCP/IP 사용하는 법 🌐✨


1. TCP/IP란 무엇인가? 🤔

  • *TCP/IP(Transmission Control Protocol/Internet Protocol)**는 인터넷과 네트워크에서 데이터를 송수신하기 위한 핵심 프로토콜이다. TCP는 데이터의 신뢰성을 보장하며, IP는 데이터를 목적지로 전달하는 역할을 한다.

📚 TCP/IP의 주요 특징

  • 신뢰성 보장: 데이터 패킷의 전송과정에서 손실이 발생하면 자동으로 재전송한다.
  • 연결 지향: 데이터를 전송하기 전에 송신자와 수신자 간의 연결을 설정한다.
  • 순차적 데이터 전달: 데이터를 올바른 순서로 수신할 수 있도록 보장한다.

2. TCP/IP의 동작 원리 🛠️

TCP/IP는 4계층 네트워크 모델을 기반으로 동작한다:

  1. 응용 계층(Application Layer): 사용자 애플리케이션이 네트워크를 사용할 수 있게 한다. (예: HTTP, FTP)
  2. 전송 계층(Transport Layer): 신뢰성 있는 데이터 전송을 담당한다. (예: TCP, UDP)
  3. 인터넷 계층(Internet Layer): 데이터 패킷을 목적지로 전달한다. (예: IP)
  4. 네트워크 접근 계층(Network Access Layer): 물리적 데이터 전송을 수행한다.

3. Node.js에서 TCP/IP 통신 구현하기 🛠️

net 모듈이란?

Node.js에서 TCP 통신을 구현하기 위해 제공되는 기본 내장 모듈이다.

이 모듈은 클라이언트와 서버 간 데이터 송수신을 가능하게 하며, 다음과 같은 주요 메서드를 제공한다

npm install net

TCP/IP 모듈 구현 및 코드 설명 🧩

1) TCP 연결 초기화: connect 함수

이 함수는 클라이언트가 서버에 연결을 설정하는 역할을 한다.

import net from 'net';
import { logger } from '#util/logger.js';

let client = null;
let isConnected = false;

/**
 * TCP 연결 초기화
 * @param {string} host - 서버 IP 주소
 * @param {number} port - 서버 포트 번호
 * @returns {Promise<void>}
 */
export const connect = async (host, port) => {
    return new Promise((resolve, reject) => {
        client = new net.Socket(); // 소켓 객체 생성
        client.connect(port, host, () => {
            isConnected = true; // 연결 상태 플래그 설정
            logger.info(`[tcp-connect] Connected to ${host}:${port}`);
            resolve(); // 연결 성공 시 resolve 호출
        });

        // 연결 에러 처리
        client.on('error', (error) => {
            logger.error(`[tcp-connect] Connection error: ${error.message}`);
            isConnected = false; // 연결 상태 플래그 해제
            reject(error); // 연결 실패 시 reject 호출
        });

        // 연결 종료 이벤트
        client.on('close', () => {
            logger.info(`[tcp-connect] Connection closed`);
            isConnected = false; // 연결 상태 플래그 해제
        });
    });
};

설명

  1. net.Socket 객체 생성:
    • TCP 클라이언트를 초기화한다.
  2. connect 메서드 호출:
    • 서버의 host와 port를 지정하여 연결을 시도한다.
    • 연결이 성공하면 resolve()를 호출하여 연결 상태를 업데이트한다.
  3. 에러 처리:
    • 연결 도중 에러가 발생하면 error 이벤트 핸들러가 호출되고, 연결 상태를 플래그(isConnected)로 표시한다.
  4. 종료 처리:
    • 서버 또는 클라이언트 측에서 연결이 종료되면 close 이벤트가 발생한다.

2) TCP 데이터 송수신: sendAndReceiveData 함수

서버로 데이터를 전송하고, 서버로부터의 응답을 대기한다.


/**
 * 데이터를 전송하고 응답을 대기
 * @param {Buffer|string} data - 전송할 데이터
 * @param sendEncoding - 데이터 송신 인코딩
 * @param receiveEncoding - 데이터 수신 인코딩
 * @returns {Promise<string>} - 수신된 데이터
 */
export const sendAndReceiveData = async (data, sendEncoding, receiveEncoding) => {
    if (!isConnected) {
        throw new Error(`[tcp-sendAndReceiveData] Client is not connected`);
    }

    return new Promise((resolve, reject) => {
        // 데이터 송신
        client.write(data, (error) => {
            if (error) {
                logger.error(`[tcp-sendAndReceiveData] Error sending data`);
                reject(error);
            } else {
                logger.info(`[tcp-sendAndReceiveData] Data sent: ${data.toString(sendEncoding)}`);
            }
        });

        // 데이터 수신 이벤트 등록
        const onData = (data) => {
            try {
                logger.info(`[tcp-sendAndReceiveData] Data received: ${data.toString(receiveEncoding)}`);
                client.off('data', onData); // 리스너 해제
                resolve(data.toString(receiveEncoding)); // 수신된 데이터를 resolve
            } catch (error) {
                client.off('data', onData); // 오류 발생 시 리스너 해제
                logger.error(`[tcp-sendAndReceiveData] Error processing received data: ${error.message}`);
                reject(error); // 에러 발생 시 reject 호출
            }
        };

        client.on('data', onData); // 데이터 수신 이벤트 핸들러 등록
    });
};

설명

  1. 연결 상태 확인:
    • TCP 연결이 활성화되지 않았다면 오류를 발생시킨다.
  2. 데이터 송신 (write):
    • 클라이언트가 서버로 데이터를 전송한다.
    • 송신이 성공하면 로그를 남긴다.
  3. 데이터 수신 (on('data')):
    • 서버가 데이터를 응답하면 해당 데이터를 처리한다.
    • 데이터 처리 후 이벤트 리스너(onData)를 해제하여 메모리 누수를 방지한다.
  4. 오류 처리:
    • 데이터 처리 도중 오류가 발생하면 리스너를 해제하고 reject로 에러를 반환한다.

3) TCP 연결 종료: disconnect 함수

TCP 연결을 안전하게 종료한다.


/**
 * TCP 연결 종료
 * @returns {Promise<void>}
 */
export const disconnect = async () => {
    if (!isConnected) {
        logger.info(`[tcp-disconnect] Client is not connected`);
        return;
    }

    return new Promise((resolve, reject) => {
        client.end(() => {
            logger.info(`[tcp-disconnect] Connection ended`);
            isConnected = false; // 연결 상태 플래그 해제
            resolve(); // 연결 종료 성공
        });

        // 연결 종료 중 에러 처리
        client.on('error', (err) => {
            logger.error(`[tcp-disconnect] Error while disconnecting: ${err.message}`);
            reject(err); // 에러 발생 시 reject
        });
    });
};

설명

  1. 연결 상태 확인:
    • 연결이 없는 경우, 추가 작업을 수행하지 않고 종료한다.
  2. end 메서드 호출:
    • 서버와의 연결을 안전하게 종료한다.
  3. 에러 처리:
    • 연결 종료 중 에러가 발생하면 reject를 호출한다.

4. 실제 사용 예제

  1. 서버 연결:
    • connect 함수로 서버와의 연결을 설정.
  2. 데이터 송수신:
    • sendAndReceiveData 함수로 데이터를 송신하고 응답을 대기.
  3. 연결 종료:
    • disconnect 함수로 TCP 연결을 안전하게 종료.
// TCP-IP를 사용하는 서비스단 구축 예제

import * as socket from '#module/tcp_module.js';
import { logger } from '#util/logger.js';
import { sleep } from '#util/wb_lib.js';

export const tcpService = async (tcpData, onErrorCallback) => {
    try {
    
        const tcpInfo= {
            connIp: tcpData.connIp,
            connPort: tcpData.connPort
        };
				
        await socket.connect(displayInfo.connIp, displayInfo.connPort);

        await getTcpDevice();
        await sleep(500);

    } catch (error) {
        logger.error(`[tcpService] Error!`);
        if (onErrorCallback) {
            onErrorCallback(error);
        }
    } finally {
        await socket.disconnect();
    }
};

const getTcpDevice = async () => {
    try {
        const cmd = Buffer.from([0x02, 0xC0, 0x01, 0x02, 0x03, 0x00)
        
        await socket.sendAndReceiveData(cmd, 'hex', 'hex');
        logger.info(`[getTcpDevice] Success`);

    } catch (error) {
        logger.error(`[getTcpDevice] Error!`);
        throw error;
    }
};

5. 결론 🏁

TCP/IP는 인터넷과 네트워크 통신에서 가장 중요한 프로토콜로, 신뢰성 있는 데이터 전송을 제공한다. Node.js의 net 모듈을 활용하면 TCP 기반의 네트워크 애플리케이션을 쉽게 구현할 수 있다.

댓글