TCP를 이용해서 통신을 하는 경우 클라이언트 개수만큼 소켓이 있어야한다.
read(), write() 함수는 호출 후 실행이 완료될때까지 리턴을 하지않는다 그러나 게임에서는 이렇게 호출 후 실행이 완료 될때까지 기다리는 것은 절대로 안된다 이유는 이렇게 기다리는 시간들을 보았을때 호출한 메인 스레드는 사용자 입장에서 보았을 때 그 기다리는 시간동안 일시 정지하는 것처럼 보이기때문이어서 부드러운 화면을 보여줄 수가 없다..
위의 결과로 네트워크 소켓은 보통 비동기 입출력상태로 다룬다.
블로킹 소켓
read, write같이 요청을 해 놓고 응답을 대기하는 함수일때 스레드는 대기를 하게 되는데 이러한 현상을 블로킹이라고한다.
스레드가 대기하는 현상 (블로킹) -> 이러한 것들은 cpu연산을 하지 않는다 사용률 0% -> 스레드는 waitable state -> 파일 읽기 쓰기 같은 함수가 끝나면 다시 스레드는 running 으로 바뀌고 함수 리턴 후 종료
- 위의 스레드 현상은 소켓에서도 동일, 소켓도 네트워크 수신하는 함수를 호출하면 블로킹이 발생 이럴 때 상대방 컴터에서 아무것도 응답이 없다면 영원한 블로킹 상태로 변하게 될 것이다.
네트워크 연결
s = socket(TCP) (소켓 핸들생성)
s.bind(any_port) (사용가능한 빈포트를 차지)
s.connect(xxx.xx.) (상대방 끝점을 향해 tcp 연결 시도)
s.send() (상대방 긑점을 향해 데이터를 전송)
s.close().. (소켓 닫고 종료)
- 위의 send는 블로킹 없이 즉시리턴한다 이유는 밑으로 ...
블로킹과 소켓버퍼
- 소켓은 각각 송신버퍼랑 수신버퍼를 가지고있습니다. 크기는 고정되어있으나 변경가능, 버퍼는 큐로구성
- 송신 버퍼에 채워진 data는 빠져나가면서 네트워크 선로를 통해 송출된다 그래서 뭔가가 채워지더락도 빈상태가 된다..
- 이렇게 되더라도 송신 버퍼가 가득 차 있을 수는 있다 이럴 때는 블로킹이 발생해서 os는 버퍼의 맨 앞에 있는 것을 빼내서 자리를 만들어서 블로킹을 해제 해버립니다.
네트워크 연결받기 및 수신
- .listen()은 연결을 수락하는 역할만, 데이터를 주고 받지는 않는다..
int main() {
s = socket(TCP);
s.bind(5959); // 5959포트 점유
s.listen(); // 연결을 받는 역할
s2 = s.accept(); //acept()함수에서 받은 새로운 소켓 핸들을 이용해서 상대방 끝점과 통신한다.
while (true)
{
r = s2.recv(); // 소켓으로부터 데이터를 수신, 수신할 데이터가 없으면 블로킹이 일어난다
if (r.length <= 0) break; // 길이가 0보다 작으면 tcp 연결이 끝났다는 것이다.
}
s2.close()
송신 버퍼랑 수신버퍼는 같다 차이점은 순서가 반대라는 것이다. 송신은 보내는 것이니 사용자가 버퍼에 push하고 os가 pop을 하지만 수신 버퍼는 받을 때 os가 push , 사용자가 pop을한다
수신버퍼는 수신되는 데이터를 모조리 채워 넣는다 그러다가 자리가 없다면 데이터를 더 이상 받지않는다,
수신 버퍼가 가득 차면 발생
recv은 1바이트라도 수신할 수 있으면 즉시 리턴한다..
수신 버퍼가 가득차면 TCP로 데이터를 송신 함수 send가 블로킹된다. 이러한 상태에서는 TCP통신은 없고 연결만 존재하는 것이다.
정리를 하면 수신버퍼가 가득차면 -> send()함수가 블로킹이 된다 -> tcp연결만 되어있고 통신은 발생하지 않는다.
TCP와 UDP의 차이는 TCP는 담을 공간이 없으면 블로킹이 되는데 UDP는 담을 공간이 없으면 DATA를 버립니다. 그래서 UDP는 송신측에서 송신 활동이 멈추지 않는다. -> 이렇게 되면 데이터를 받는 쪽에서 데이터를 놓치게 된다...
논블록 소켓
- 블로킹 소켓을 할때는 문제가 있다 대상이 많을 경우 소켓을 엄청 많이 만들어야하는데, 스레드가 많이 있을 경우 컨텍스트 스위치를 하게 된다면 자원 낭비가 심해진다
소켓 함수가 블로킹 되지 않도록 하는 것이 논블록킹
논블록 소켓은 무조건 함수 호출에 대해서 즉시 리턴을 한다 -> 리턴은 성공 or would block(오류) 둘 중에 하나이다.
select(), poll() =
- 로켓리스트 A가있다, A 소켓 중 하나라도 IO가능한 것이 있다면 블로킹한다
- 블로킹이 끝나면 어떤 소켓이 IO처리 가능한지 알려준다
- 블로킹은 타임아웃을 지정할 수 있다
Overlapped I/O (비동기 I/O)
TCP 소켓의 send()에서는 송신 버퍼에 1바이트라도 비어있으며녀 IO가능, 보내려는 데이터가 5바이트인데 비어있는 공간이 1바이트면 쪼개서 1바이트만 소켓 송신버퍼에 채워진다, 수신쪽에서도 수신버퍼가 1바이트라도 들어 있으면 IO가능하다
그러나 UDP는 문제가 있다 UPD는 TCP처럼 일부만 보낼 수 없어서 would block이 발생한다 그래서 send()를 하지 못한 채 반복 해서 cpu 낭비가 발생한다...
would block이 리턴되면 오류라서 아무것도 하지 않았음을 의미,
논블록은 하나의 스레드에서 여러 소켓을 다룰 수 있다. , 수백개의 소켓이 있을 때 루프를 도는 동안 논블록 블록이 난무하면 비효율적인 프로그램이된다 이것을 논블록 소켓으로 처리하면 문제를 해결 할 수 있다
송수신 함수를 논블록킹으로 할때는 would block이 리턴되면 소켓안에서 아무것도 일어나지 않는다.. 그러나 connect함수에서 would block이 리턴되면 어떤일이 발생한 것이다. connect는 연결을 시도하다가 실패한 것이라서 '연결과정이 진행 중인 상태'로 변하게 된다.
epoll
IOCP
I/O Completion Port IOCP
출처 : 게임 서버 프로그래밍 교과서
'게임서버' 카테고리의 다른 글
소켓 프로그래밍 (0) | 2022.01.07 |
---|---|
네트워크 (0) | 2021.11.08 |
멀티스레드, lock,... (0) | 2021.11.08 |