작성
·
276
0
나름대로 식당 예시로 비유를 해봤는데요, 제가 정확히 이해한게 맞는지 확인해보고자 질문을 남깁니다.
ReceiveAsync가 비동기로 처리되는 상황 : 대기 손님이 없는 경우. 일단 아무나 입장할 수 있도록 준비를 해둔 상태이고, 손님이 등장하면 바로 입장시킴. (`e.Completed` 호출)
ReceiveAsync가 동기로 처리되는 상황 : 대기 손님이 있는(많은) 경우. 그냥 존재하는 손님을 바로 입장시킬 수 있음.
SendAsync가 비동기로 처리되는 상황 : 음식을 3개 시킨 손님이 있다. 첫 번째 음식 `A`가 나왔는데, 손님이 아직 테이블로 가져가지 못한 경우이다. 손님이 아직 테이블로 음식을 가져가지 못했다고 해서, `A`를 가져갈 때 까지 두 번째 음식 `B`를 내보내지 않고 기다릴 이유가 없으므로 `B`를 카운터에 놓여있는 `A`의 옆에 둔다. (_sendQueue에 Enqueue한다.)
SendAsync가 동기로 처리되는 상황 : 음식을 3개 시킨 손님이 음식 `A`가 나온 즉시 곧바로 테이블로 가져가는 데 성공한 경우이다.
감사합니다.
답변 1
6
Recv는 맞는데 Send는 좀 얘기가 다릅니다.
이 부분은 비유하기 보다는 그냥 직빵으로 원리를 이해하는게 더 편할 수도 있는데요.
소켓을 만들면, 내부적으로 커널 레벨에
[수신버퍼(RecvBuffer)][송신버퍼(SendBuffer)] 짝이 만들어집니다.
우리가 서버 코드에서 Send를 요청할 때
커널 송신버퍼에 데이터를 복사하면 성공으로 간주합니다.
그 다음 운영체제가 이를 상대 컴퓨터에 보내는 것을 처리할테고,
이 데이터는 상대방의 커널 수신버퍼에 들어가게 됩니다.
반대로 우리가 Recv를 호출하면,
커널 수신버퍼에 있는 데이터를 유저레벨로 복사하는데
데이터가 1바이트라도 있으면 성공, 아니면 실패한 상황이 됩니다.
결국 나와 상대방의 상황이 맞물려 있는데
상대방이 재빨리 데이터를 Recv해서 상대 수신버퍼를 비워주지 않으면,
우리의 송신버퍼를 비울 수가 없게 됩니다.
결국 Send가 바로 완료되는 조건은, [커널 송신버퍼에 데이터를 복사할 수 있는 경우]이고
그럴 수 없다면 커널 송신버퍼가 비워질때까지 대기하면서
한참 후에 완료됩니다. 그리고 이 때 완료 통지가 콜백 함수로 호출됩니다.
Recv는 거꾸로 [커널 수신버퍼에 데이터가 1바이트라도 있는 경우] 바로 완료되고,
그렇지 않다면 수신버퍼에 데이터가 채워지기 전까지 완료되지 않습니다.
생각보다 더 복잡하게 돌아가는군요 감사합니다 😄