# 구현 | 서버제어

## 서버의 실행

select 함수를 실행한 뒤 각 서버의 run 함수를 호출하게 되는데, 서버의 run 함수는 자신이 연결되어 있는 커넥션들에 대해 입출력 작업을 최대 1회까지만 실행할 수 있습니다. select 함수의 결과로 read/write fd\_set이 말하는 것은 1번 입출력이 가능한지에 대한 여부이기 때문입니다. 즉, 서버는 자신의 커넥션들에 대해 1번 순회하는 것이 1사이클입니다. 각 서버가 1사이클씩 돌면  manager 단에서  select 함수를 다시 실행하고, 또 서버들은 1사이클씩 도는 과정이 계속해서 반복되는 것입니다. 코드는 아래와 같습니다.

```cpp
std::map<int, Connection>::iterator it = m_connections.begin();
while (it != m_connections.end())
{
	std::map<int, Connection>::iterator it2 = it++;
	int fd = it2->first;

	if (m_fd == fd)
		continue ;
		try {
			if (hasSendWork(it2->second))
			{
				runSend(it2->second);
				continue ;
			}
			if (hasExecuteWork(it2->second))
			{
				runExecute(it2->second);
				continue ;
			}
			if (hasRequest(it2->second)) {				
				runRecvAndSolve(it2->second);
			}
		} catch (Server::IOError& e) {
			ft::log(ServerManager::log_fd, ft::getTimestamp() + e.location() + std::string("\n"));
			closeConnection(fd);
		} catch (...) {
			ft::log(ServerManager::log_fd, ft::getTimestamp() + "detected some error" + std::string("\n"));
			closeConnection(fd);				
		}
}
```

{% hint style="warning" %}
이터레이터를 2개 사용하는 점을 눈여겨보세요. 이터레이터를 사용한 컨테이너 순회 중 인스턴스를 삭제하는 작업은 안전하지 않습니다.
{% endhint %}

{% hint style="warning" %}
읽고, 처리하고, 응답을 생성하고, 전송하는 것이 일반적인 흐름입니다. 하지만 왜 순서가 반대로 되어있을까요? 모든 입출력 작업은 select를 통해 안전성이 확인되어야 하고, 최대 1회까지 밖에 할 수 없기 때문입니다. 요청을 수신하고 처리한 뒤, 해당 클라이언트 소켓에 응답을 발송해도 되는지 확인하는 데 1사이클이 추가로 필요합니다.
{% endhint %}

{% hint style="warning" %}
runRecvAndSolve는 이름에서부터 2개 이상의 작업이 묶여있기 때문에 잘 고안된 함수는 아닙니다. Solve와 Execute의 차이가 직관적으로 구분되지 않는 것도 좋은 네이밍은 아닙니다.
{% endhint %}

## 커넥션 관리

같은 이유로 새로운 커넥션(클라이언트)을 추가하는 작업도 해당 서버의 커넥션 순회가 끝난 뒤에 처리합니다. 새롭게 연결한 클라이언트 소켓도 read fd\_set에 등록되어 select 함수를 실행한 뒤 결과에 따라 요청을 읽어야 하기 때문에, 커넥션을 순회하기 전에 위치하면 처리 흐름이 직관적이지 않습니다. 한 번의 select 함수로 동시에 감시할 수 있는 소켓의 수는 1024개이고, 서버는 여러 개 생성될 수 있는 반면 디스크립터 테이블은 공유하기 때문에 현재 연결된 커넥션이 많은 경우에는 새로운 연결을 받지 않던가 한가한 커넥션을 끊고 연결해야 합니다.

```bash
if (hasNewConnection())
{
	if (m_connections.size() >= (1024 / m_manager->get_m_servers().size()))
	{
		int fd = getUnuseConnectionFd();
		if (fd == -1)
			return ;
		closeConnection(fd);
	}
	if (!acceptNewConnection())
		reportCreateNewConnectionLog();
}

```
