작성
·
294
2
public void Send(byte[] sendBuff)
{
lock (_lock)
{
_sendQueue.Enqueue(sendBuff);
if (_pendinglist.Count == 0)
RegisterSend();
}
}
//
private void RegisterSend()
{
while (_sendQueue.Count > 0)
{
byte[] buff = _sendQueue.Dequeue();
_pendinglist.Add(new ArraySegment<byte>(buff, 0, buff.Length));
}
_sendArgs.BufferList = _pendinglist;
bool pending = _socket.SendAsync(_sendArgs);
if (pending == false)
{
OnSendCompleted(null, _sendArgs);
}
}
처음에 send에서 lock을 걸고 pendinglist의 Count가 0일 때만 RegisterSend()를 호출하는데, RegisterSend() 내부에서 while문을 돌며 sendqueue의 count만큼 pendinglist에 sendqueue 데이터를 넣어주고 있잖아요?
근데, sendqueue의 count가 2이상인 경우가 가능한지 궁금합니다.. 위의 Send에서 lock을 걸어두면 RegisterSend()의 호출이 끝날때까지 다른 Session이 접근 못하지 않나요?
답변 6
4
멀티쓰레드보다는 비동기 네트워크 쪽과 관련이 있는데,
SendAsync를 한다고 무조건 상대방이 받아준다는 보장이 없습니다.
로컬 환경에서야 pending이 거~~의 대부분 false로 뜨고 바로 완료 되겠지만
저 지구 반대편에 있는 사람이 접속했다면 이런저런 이유로
SendAsync가 바로 완료되지 않고 pending = true로 설정될 확률도 높습니다.
물론 말씀하신대로 Latency 뿐 아니라 Recv할게 너무 많아서
상대쪽에서 처리를 못해서 그럴 수도 있구요.
Send가 동시에 호출되는 경우는,
사실 Part7 컨텐츠를 만들어 보시면 잘 알게 되실테니 이해가 안 가면 넘어가도 됩니다만.
스포일러를 미리 드리자면 온라인 게임에서
어떤 유저의 주변에서 일어나는 모든 일들은 다 해당 유저한테도 알려야 합니다.
주변 몬스터가 움직인다거나, 누군가 스킬을 쓴다거나 하는 지역 기반 컨텐츠는 물론
길드원이 길드채팅을 한다거나, 누군가 귓말을 보낸다거나 하는 유저 한정 컨텐츠도 있을겁니다.
이렇게 동시다발적으로 컨텐츠가 진행될테니
어디선가는 채팅Send 패킷을, 어디선가는 몬스터Move 패킷을 해당 유저한테 보내고 싶어질 수도 있겠죠.
그러니 어떤 식으로는 동시 다발적인 Send가 일어날테니
이에 대한 방비책은 필수입니다.
물론 게임 컨텐츠를 다 싱글쓰레드로 묶었다면 Lock이 필요없을 수도 있긴 한데,
좀 규모 있는 MMO라면 대부분 지역 단위로 쓰레드를 배정하거나
Actor 단위로 (모든 게임 오브젝트 단위로) 쓰레드를 배정하게 됩니다.
참고로 Recv는 Recv->Completed->Recv->Completed 이 흐름대로 무한 반복이라
동시 다발적으로 Recv가 다중으로 일어날 수 없습니다.
2
2
음.. 뭔가 복잡하네요 ㅜㅜ 멀티스레드를 제대로 이해를 못하고 있는것 같아요
일단 pending이 true인 경우에 _pendingList.Count의 초기화가 이루어지지 않으니
_sendQueue에 버퍼가 쌓인다고 이해는 했습니다. Send가 완료되지 않는 경우는 상대가
Recv할게 많아서 미뤄진거라 생각해도 좋을까요?
그리고 또 다른 질문으로, Send 함수에 동시에 접근할 수 있는 경우가 어떤 경우가 있는지 잘 모르겠어요..
recv는 뭔가 여러 세션이 데이터를 동시에 보내면 차례로 처리하는 경우가 있을 것 같은데
send가 애초에 서버는 하나고 클라이언트 입장에서도 각기 다른 클라이언트가 send처리를 한다고 생각해서
lock이 필요없다고 느껴졌습니다. 정리가 잘 안되네요 죄송합니다 ㅜㅜ..
2
_pendingList는 이전에 보낸 요청들을 담고 있는데,
요청 처리가 끝나면 _pendingList를 비워주기 때문에
이를 이용해 이전 요청이 완료 되었는지를 간접적으로 확인할 수 있습니다.
이전에 보낸 요청이 완료되지 않았다면
_pendingList.Count == 0를 체크 하는 부분 때문에
RegisterSend를 해주지 않으니 결과적으로 _sendQueue의 Count만 늘어나겠죠.
참고로 저 부분은 Lock을 걸고 하는 경우도 있고
LockFreeQueue를 사용하는 경우도 있습니다.
0
0
언제나 친절한 답변 감사합니다 ㅜㅜ 이해했습니다.
이 강의 외의 질문이라 여기다가 하는게 맞는지는 모르겠는데 part4 다음 part 7으로 건너뛰어도 되는건가요?
아니면 part 5 part 6도 필수인가요? mssql은 책보고 한 번 써본 적은 있습니다..