2024-11-11 CH-5 팀 과제 (랜덤 타워 디펜스) sequence 체크

MOON·2024년 11월 13일
0

내일배움캠프 과제

목록 보기
32/36

오늘은 클라이언트에서 패킷으로 보내는 sequence값을 체크해보는 로직을 작성해 보았습니다.

기존 onData

  • 기존 코드에선 sequence값을 체크하고 있지 않았습니다.
export const onData = (socket) => async (data) => {
  try {
    socket.buffer = Buffer.concat([socket.buffer, data]);

    while (socket.buffer.length >= PACKET_TYPE_LENGTH + VERSION_LENGTH) {
      // 패킷 타입 읽어오기
      const packetType = socket.buffer.readUInt16BE(0);
      const versionLength = socket.buffer.readUInt8(PACKET_TYPE_LENGTH);
      const totalHeaderLength =
        PACKET_TYPE_LENGTH + VERSION_LENGTH + versionLength + SEQUENCE_LENGTH + PAYLOAD_LENGTH;

      ...

      // 시퀀스
      const sequence = socket.buffer.readUInt32BE(
        PACKET_TYPE_LENGTH + VERSION_LENGTH + versionLength,
      );
      // 시퀀스 체크 TODO

      ...

      // payload
      const payload = socket.buffer.subarray(totalHeaderLength, totalPacketLength);
      // console.log('payload: ', payload);
      // 이후의 데이터 다시 저장
      socket.buffer = socket.buffer.subarray(totalPacketLength);

      const data = packetParser(packetType, payload);

      const handler = getHandlerById(packetType);
      // console.log(handler);a
      await handler({ packetType, data, socket });
    }
  } catch (e) {
    console.error('onData error: ', e);
  }
};

기존 createResponse

  • 기존 버퍼객체를 만들어 응답으로 보낼 데이터를 만드는 함수입니다.
  • 현재 sequence를 일단 임시방편으로 0으로해서 진행해 왔습니다.
export const createResponse = (packetType, responseData, socket) => {
  try {
    const protoMessages = getProtoMessages();
    const gamePacket = protoMessages['protoPacket']['GamePacket'];

    // 따로 저장한 responseProto에서 protoType을 가져옴
    const [namespace, typeName] = responseProto[packetType].protoType.split('.');
    const response = protoMessages[namespace][typeName];

    const responseInstance = response.create(responseData);

    const gamePacketFieldName = responseProto[packetType].fieldName;
    const gamePacketInstance = gamePacket.create({ [gamePacketFieldName]: responseInstance });

	const sequence = 0;

    const buffer = gamePacket.encode(gamePacketInstance).finish();

    const headerPacket = createHeader(packetType, sequence, buffer.length);

    return Buffer.concat([headerPacket, buffer]);
  } catch (e) {
    console.error(e);
  }
};

먼저 해당 클라이언트를 정보를 가질 class를 하나 만들고 클라이언트 전용 세션 관리 파일을 만들어 클라이언트와 서버가 연결될때 해당 클라이언트를 새로 세션에 저장하게 설계해 봤습니다.

class Client

  • 클라이언트의 간단한 정보와 여기서 각 클라이언트의 sequence값을 관리할 수 있게 만들어 보았습니다.
let clientCounter = 1;

class Client {
  constructor(socket) {
    this.id = this.getClientId();
    this.socket = socket;
    this.sequence = 1;
  }
  getSequence() {
    return this.sequence;
  }
  updateSequence() {
    this.sequence++;
  }
  getClientId() {
    return clientCounter++;
  }
}
export default Client;

client 세션 관리 파일

  • 세션에 저장 삭제 등의 관리를 해줍니다.
import Client from '../classes/models/client.class.js';
import { clientSessions } from './sessions.js';

export const addClient = (socket) => {
  const client = new Client(socket);
  clientSessions.push(client);
  return client;
};
export const removeClient = (socket) => {
  const index = clientSessions.findIndex((client) => client.socket === socket);
  if (index !== -1) {
    return clientSessions.splice(index, 1)[0];
  }
};
export const getAllClient = () => {
  return clientSessions;
};
export const getClientBySocket = (socket) => {
  return clientSessions.find((client) => client.socket === socket);
};

onConnection

  • 클라이언트와 연결이 될떄 해당 클라이언트를 세션에 추가 저장합니다. addClient(socket)
export const onConnection = (socket) => {
  console.log(`Client connected from: ${socket.remoteAddress}:${socket.remotePort}`);

  socket.buffer = Buffer.alloc(0);
  addClient(socket);

  socket.on('data', onData(socket));
  socket.on('end', onEnd(socket));
  socket.on('error', onError(socket));
};

수정된 OnData

  • 해당 세션에 저장된 클라이언트를 찾아 sequence값을 올려줍니다.(이 부분이 사실 애매한면이 있습니다.)
  • 이 sequence값이 서버에서 응답을 보내줄때 올려주는 것이 맞을 지에 대해 고민해 보았습니다.
  • 근데 일단 이번 과제에서 여러 패킷을 교환되는 중 여러 sequence값이 올바르게 오르고 가는 부분에서 여러 에러 사항들이 생겨서 패킷(데이터)를 받을때 sequence값을 올리게 되었습니다.
export const onData = (socket) => async (data) => {
  try {
    socket.buffer = Buffer.concat([socket.buffer, data]);
    const client = getClientBySocket(socket);
    client.updateSequence(); // 시퀀스 업데이트

    while (socket.buffer.length >= PACKET_TYPE_LENGTH + VERSION_LENGTH) {
      // 패킷 타입 읽어오기
      const packetType = socket.buffer.readUInt16BE(0);
      const versionLength = socket.buffer.readUInt8(PACKET_TYPE_LENGTH);
     ....
     ....
      // 시퀀스
      const sequence = socket.buffer.readUInt32BE(
        PACKET_TYPE_LENGTH + VERSION_LENGTH + versionLength,
      );
      // 시퀀스 체크
      if (sequence !== client.getSequence()) {
        throw new Error('시퀀스가 일치하지 않습니다.');
      }
      ....
      ....
      const handler = getHandlerById(packetType);
      // console.log(handler);a
      await handler({ packetType, data, socket });
    }
  } catch (e) {
    console.error('onData error: ', e);
  }
};

수정된 createResponse

  • 해당 클라이언트를 찾아 sequence값을 찾아오게 변경하였습니다.
export const createResponse = (packetType, responseData, socket) => {
  try {
    const protoMessages = getProtoMessages();
    const gamePacket = protoMessages['protoPacket']['GamePacket'];

...

    const client = getClientBySocket(socket);
    let sequence;

    if (client) {
      sequence = client.getSequence();
    } else {
      sequence = 0;
    }

    const buffer = gamePacket.encode(gamePacketInstance).finish();

    const headerPacket = createHeader(packetType, sequence, buffer.length);

    return Buffer.concat([headerPacket, buffer]);
  } catch (e) {
    console.error(e);
  }
};

오늘의 회고

벌써 또 팀과제 제출이네요 이번엔 진짜 시간이 촉박하다고 느껴졌지만 어찌저찌 거의 완성이 된것 같습니다. 제출까지 화이팅할 생각합니다.

오늘도 화이팅!!

profile
안녕하세요

0개의 댓글