개발자공부일기

oneof 본문

TIL(Today I Learned)

oneof

JavaCPP 2025. 1. 22. 21:07


oneof는 하나의 필드만 활성화되도록 하는 기능으로, 통신 패킷에서 여러 선택 가능한 데이터 중 하나만 보낼 때 사용됩니다. 예를 들어, 클라이언트와 서버가 서로 다른 메시지 타입(텍스트, 이미지, 상태 변경 등)을 주고받을 때 유용합니다.

oneof 필드는 oneof 공유 메모리의 모든 필드를 제외하고 일반 필드와 같으며 최대 한 필드를 동시에 설정할 수 있습니다.

oneof의 member를 설정하면 다른 모든 member는 자동으로 지워집니다.

선택한 언어에 따라 특별한 case() 또는 WhatOneof() 메서드를 사용하여 oneof에 어떤 값이 세팅되는지 확인할 수 있습니다.

Node.js 환경에서 TCP 통신을 할 때 Protocol Buffers의 oneof를 사용하는 예제

1. Protobuf 파일 정의

다음은 oneof를 사용하는 예제 Protobuf 정의 파일입니다 (message.proto).

syntax = "proto3";

message Packet {
  oneof payload {
    string text_message = 1;
    bytes image_data = 2;
    int32 status_code = 3;
  }
}

설명:

  • Packet은 TCP 통신에 사용될 메시지입니다.
  • oneof payload는 한 번에 text_message, image_data, 또는 status_code 중 하나만 설정 가능합니다.

2. Protobuf 컴파일

protoc를 사용해 .proto 파일을 JavaScript로 컴파일합니다.

protoc --js_out=import_style=commonjs,binary:. message.proto

이 명령으로 message_pb.js 파일이 생성됩니다.


3. Node.js에서 TCP 통신 구현

서버 코드 (server.js):

const net = require('net'); // TCP 통신을 위한 기본 모듈
const { Packet } = require('./message_pb'); // Protobuf 파일에서 생성된 메시지 클래스 로드

// TCP 서버 생성
const server = net.createServer((socket) => {
  console.log('Client connected.');

  // 클라이언트로부터 데이터 수신
  socket.on('data', (data) => {
    try {
      // 수신한 바이너리 데이터를 Protobuf 메시지로 디코딩
      const receivedPacket = Packet.deserializeBinary(data);

      // 활성화된 oneof 필드를 확인
      const field = receivedPacket.getPayloadCase();

      // 활성화된 필드에 따라 처리
      switch (field) {
        case Packet.PayloadCase.TEXT_MESSAGE:
          // text_message 필드가 활성화된 경우
          console.log('Received text message:', receivedPacket.getTextMessage());
          break;

        case Packet.PayloadCase.IMAGE_DATA:
          // image_data 필드가 활성화된 경우
          console.log('Received image data (length):', receivedPacket.getImageData().length);
          break;

        case Packet.PayloadCase.STATUS_CODE:
          // status_code 필드가 활성화된 경우
          console.log('Received status code:', receivedPacket.getStatusCode());
          break;

        default:
          // 어떤 필드도 활성화되지 않은 경우
          console.log('Unknown payload type.');
      }
    } catch (error) {
      console.error('Error processing data:', error.message);
    }
  });

  // 클라이언트 연결 종료 시 호출
  socket.on('end', () => {
    console.log('Client disconnected.');
  });
});

// 서버 시작 및 포트 4000에서 대기
server.listen(4000, () => {
  console.log('Server listening on port 4000');
});

클라이언트 코드 (client.js):

const net = require('net'); // TCP 클라이언트 생성용 기본 모듈
const { Packet } = require('./message_pb'); // Protobuf 파일에서 생성된 메시지 클래스 로드

// 서버에 연결
const client = net.connect(4000, 'localhost', () => {
  console.log('Connected to server.');

  // 1. 텍스트 메시지 전송
  const packet = new Packet(); // Packet 메시지 생성
  packet.setTextMessage('Hello, Server!'); // text_message 필드 설정

  // Protobuf 메시지를 직렬화(바이너리 변환)하여 서버로 전송
  client.write(packet.serializeBinary());
});

client.on('data', (data) => {
  // 서버로부터 받은 데이터를 처리 (현재 예제에서는 생략 가능)
  console.log('Received from server:', data.toString());
});

client.on('end', () => {
  // 서버 연결 종료 시 호출
  console.log('Disconnected from server.');
});

4. 주요 동작 설명

  1. oneof 필드 활성화:
    클라이언트는 Packet 메시지에 text_message, image_data, 또는 status_code 중 하나만 설정합니다.
  2. 데이터 직렬화 및 전송:
    packet.serializeBinary()로 데이터를 바이너리 형식으로 직렬화한 뒤 서버로 전송합니다.
  3. 서버에서 필드 확인:
    서버는 Packet.deserializeBinary(data)로 바이너리 데이터를 디코딩하고, getPayloadCase()를 호출해 활성화된 필드를 확인합니다.
  4. 유연한 메시지 처리:
    oneof을 사용하면 다양한 데이터 타입을 처리하는 코드가 간결하고 명확해집니다.

5. 실행 예제

  • 클라이언트가 텍스트 메시지를 전송:
  • Received text message: Hello, Server!
  • 클라이언트가 이미지 데이터를 전송 (예: Buffer로 설정):
  • Received image data (length): 1024
  • 클라이언트가 상태 코드를 전송:
  • Received status code: 200

 

'TIL(Today I Learned)' 카테고리의 다른 글

패킷에 헤더 붙여서 전송하기  (0) 2025.01.24
프로그래머스 문제:타켓 넘버  (0) 2025.01.23
게임 서버 아키텍처  (0) 2025.01.20
운영체제  (0) 2025.01.15
버퍼 객체  (0) 2025.01.14