구현 | Select

이 서브젝트에서 가장 중요한 함수 중 하나가 바로 Select입니다. 좌측의 학습 레퍼런스에서 Select에 관한 아티클들을 가능한 많이 읽는 것을 추천합니다. fd_set이라는 게 무엇인지, 어떻게 생겼는지, select 함수를 실행했을 때 어떤 일이 일어나는지, 함수 실행 후 read fd_set에, write fd_set에 특정 fd의 bit가 1로 설정되어 있다는 것이 무엇을 의미하는지, timeout 인자에 따라 동작이 어떻게 달라지는지 등에 대한 학습이 먼저 선행되어야 합니다.

코드는 아래와 같습니다. fd_set은 select 함수를 실행하면 내용이 바뀌기 때문에 backup해두었던 data를 인자로 넘길 data에 copy하는 작업을 먼저 실행하고, select 함수를 실행해 요청을 보내거나 응답을 받을 준비가 된 클라이언트가 있다면 서버들을 한 번씩 실행시키고, 서버의 오래된 커넥션들을 제거합니다. 이런 작업들로 인해 클라이언트들의 소켓 fd가 변경될 수 있으므로, select를 통해 감시할 fd의 범위를 다시 잡아줍니다. ctrl+c와 같은 signal이 들어와 반복문의 조건(g_live)이 false가 되기 전까지는 이 과정을 무한히 반복합니다.

void
ServerManager::runServer()
{
	signal(SIGINT, changeSignal);

	timeval timeout;
	timeout.tv_sec = 0;
	timeout.tv_usec = 0;

	g_live = true;
	resetMaxFd();
	while (g_live)
	{
		int cnt;
		fdCopy(ALL_SET);

		if ((cnt = select(this->m_max_fd + 1, &this->m_read_copy_set, &this->m_write_copy_set, \
		NULL, &timeout)) == -1)
		{
			perror("Server select error: ");
			ft::log(ServerManager::log_fd, "[Failed][Function]Select function failed(return -1)");
			throw std::runtime_error("select error");
		}
		else if (cnt == 0)
			continue ;
		writeServerHealthLog();
		for (std::vector<Server>::iterator it = m_servers.begin() ; it != m_servers.end() ; ++it)
		{
			it->run();
			closeOldConnection(it);
		}
		resetMaxFd();
	}
	exitServer("server exited.\n");
}

fdSet, fdIsset, fdClr, fdZero 등의 fd_set을 조작하는 함수들은 모두 직접 작성하여야 합니다.

Select 함는 소스 전체를 통틀어 1개만 있어야 합니다.

Last updated