[TIL] 24.10.13 SUN

GDORI·2024년 10월 13일
0

TIL

목록 보기
70/79
post-thumbnail

IOCP

IOCP란 무엇인가?

IOCP는 I/O Completion Port의 약자로 Windows 운영체제에서 제공하는 비동기 I/O 처리 메커니즘이다. 고성능 네트워크 서버나 파일 처리 애플리케이션을 개발할 때 사용된다.

IOCP의 개념

IOCP는 여러 개의 비동기 I/O 작업을 동시에 처리할 수 있도록 설계된 이벤트 기반 큐 시스템이다. 이를 통해 서버는 소수의 스레드로도 다수의 클라이언트 요청을 효과적으로 처리할 수 있으며 스레드 풀을 활용하여 CPU와 메모리 자원을 효율적으로 사용할 수 있다.

IOCP 동작과정

  1. I/O 요청 등록
  2. I/O 작업 대기
  3. 완료 시 큐에 추가
  4. 스레드 풀 처리

비동기 작업을 등록하면 요청이 진행되며 운영체제는 요청된 I/O 작업을 완료할 때까지 기다렸다가 완료되면 IOCP는 완료되었다는 내용을 큐에 추가하고 이를 처리할 스레드가 활성화된다. IOCP는 작업을 처리할 스레드 풀을 운용하며 가용 스레드를 최적화하여 처리한다. 작업 수에 따라 스레드의 수를 동적으로 조절한다.

IOCP의 장점

  • 고성능 처리
    비동기 작업과 스레드 풀을 통하여 다수의 연결을 효율적으로 관리한다.
  • CPU 활용 최적화
    스레드가 필요할 때만 생성되므로 사용량이 적고 병목현상이 줄어든다.
  • 확장성
    스레드 수를 동적으로 조절하여 다중 CPU 코어 환경에서 성능이 확장된다.

사용 사례

  1. 고성능 네트워크 서버
    대용량의 트래픽을 처리하는 HTTP 서버나 채팅 서버에 사용
  2. 파일 입출력 처리
    대규모 로그 파일을 병렬로 처리하는 애플리케이션
  3. 게임 서버
    다수의 클라이언트를 동시에 처리해야 하는 MMO 게임 서버에서 유용

한계 및 고려사항

비동기 I/O와 스레드 풀을 다루는 코드가 복잡하고 작동간에 발생하는 버그는 디버깅에 까다롭다. IOCP의 경우 윈도우에만 적용되므로 타 플랫폼에 적합하지 않다.

오늘 구현한 팀프로젝트 코드

game.js 일부

function displayInfo() {
  ctx.font = '25px Times New Roman';
  ctx.fillStyle = 'skyblue';
  ctx.fillText(`최고 기록: ${highScore}`, 100, 50); // 최고 기록 표시
  ctx.fillStyle = 'skyblue';
  ctx.fillText(`현재 스테이지: ${currentStageNumber}`, 100, 100); // 최고 기록 표시
  ctx.fillStyle = 'white';
  ctx.fillText(`점수: ${score}`, 100, 150); // 현재 스코어 표시
  ctx.fillStyle = 'yellow';
  ctx.fillText(`골드: ${userGold}`, 100, 200); // 골드 표시
  ctx.fillStyle = 'black';
  ctx.fillText(`현재 레벨: ${monsterLevel}`, 100, 250); // 최고 기록 표시
}
let isStageActive = false; // 스테이지 진행 중 여부
let animationFrameId;

function gameLoop() {
  // 렌더링 시에는 항상 배경 이미지부터 그려야 합니다!
  ctx.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
  drawPath(monsterPath); // 경로 다시 그리기
  displayInfo(); // 게임 정보 표시

  // 타워 그리기 및 공격 처리
  towers.forEach((tower) => {
    tower.draw(ctx, towerImage);
    tower.updateCooldown();
    if (isStageActive) {
      monsters.forEach((monster) => {
        const distance = Math.hypot(tower.x - monster.x, tower.y - monster.y);
        if (distance < tower.range) {
          tower.attack(monster);
        }
      });
    }
  });

  // 기지 상태 갱신 및 그리기
  base.draw(ctx, baseImage);

  if (isStageActive) {
    // 활성 스테이지에만 몬스터 이동 및 게임 오버 체크
    for (let i = monsters.length - 1; i >= 0; i--) {
      const monster = monsters[i];
      if (monster.hp > 0) {
        const isDestroyed = monster.move(base);
        if (isDestroyed) {
          alert('게임 오버! 기지를 지키지 못했습니다...');
          cancelAnimationFrame(animationFrameId); // 애니메이션 루프 중지
          endGame();
        }
        monster.draw(ctx);
      } else {
        userGold += monster.goldDrop; // 몬스터 죽을 때 골드 추가
        monsters.splice(i, 1); // 몬스터 제거
      }
    }

    if (monsters.length === 0) {
      endStage(); // 모든 몬스터 제거 시 스테이지 종료
    }
  }

  // 루프를 유지하기 위해 다음 프레임 요청
  animationFrameId = requestAnimationFrame(gameLoop);
}

// 이미지 로딩 완료 후 서버와 연결하고 게임 초기화
Promise.all([
  new Promise((resolve) => (backgroundImage.onload = resolve)),
  new Promise((resolve) => (towerImage.onload = resolve)),
  new Promise((resolve) => (baseImage.onload = resolve)),
  new Promise((resolve) => (pathImage.onload = resolve)),
  ...monsterImages.map((img) => new Promise((resolve) => (img.onload = resolve))),
]).then(() => {
  if (!isInitGame) {
    initGame();
  }
});

function initGame() {
  if (isInitGame) return;

  loadGoldBalance(); // 골드 잔액 동기화
  loadCurrentStage(); // 현재 스테이지 동기화

  monsterPath = generateRandomMonsterPath(); // 몬스터 경로 생성
  initMap(); // 맵 초기화
  placeBase(); // 기지 배치
  startStageButton.style.display = 'block'; // 준비 완료 버튼 표시
  gameLoop(); // 게임 루프 시작

  isInitGame = true;
}

async function startStage() {
  loadCurrentStage(); // 서버에서 스테이지 받아옴

  alert(`${currentStageNumber} 스테이지 시작!`);
  isStageActive = true; // 스테이지 활성화
  await stageInit(currentStageId); // 스테이지 초기화 및 몬스터 생성 시작
  gameLoop();
}

async function stageInit(currentStageId) {
  // 인수로 받은 해당 스테이지에 맞게 몬스터 생성
  spawnMonster(currentStageId); // 몬스터 생성

  const result = await sendEvent(201); // 스테이지 시작 신호
  // 스테이지 신호 서버로 날림
  if (result.status === 'fail') {
    cancelAnimationFrame(animationFrameId);
    alert('게임 오류 발생');
    location.reload(); // 게임 재시작
  }
}

async function endStage() {
  isStageActive = false; // 스테이지 비활성화
  alert(`스테이지 ${currentStageNumber} 완료!`);
  const stageEndResult = await sendEvent(202);
  loadGoldBalance(); // 골드 잔액 동기화
  loadCurrentStage();
  console.log(stageEndResult);
  if (stageEndResult.status === 'fail' && stageEndResult.message === 'Last_Stage') {
    cancelAnimationFrame(animationFrameId);
    alert(`스테이지를 모두 완료하셨습니다.!`);
    location.reload(); // 게임 재시작
  }
  startStageButton.style.display = 'block'; // 준비 완료 버튼 다시 표시
}

function endGame() {
  isStageActive = false;
  cancelAnimationFrame(animationFrameId);
  alert('게임 오버! 다시 도전해보세요.');
  location.reload(); // 게임 재시작
}

아직 타워, 몬스터를 담당하신 분들의 코드를 적용하기 전이라 미완성이다. 내일 회의를 통해 연결할 예정이다.

profile
하루 최소 1시간이라도 공부하자..

0개의 댓글