Receive 하는 부분을 새로운 Session Class로 구분하여 코드를 정리하고, listener 처리와 동일하게 SocketAsyncEventArgs를 사용해 비동기 처리를 제작하였다. 오늘은 이어서 Send 부분을 처리할 것이다.
Receive는 사용 시점이 정해져있지만, Send의 경우는 언제 사용될지, buffer의 byte가 얼마 일지 알수가 없어 다른 구조로 비동기 처리를 해야했다.
Send를 동작 시킬 큰 흐름은 Send -> RegisterSend -> OnSendCompleted 로 지난 번과 유사하다.
하지만 Start에서 Asyn 소켓을 생성했던 Receive와는 다르게 여기서는 이벤트만 연결해줄 것이다.
또한 buff 크기 설정의 이유로 SocketAsyncEventArgs를 처음에만 생성하고 돌려쓰는 방식은 불가능하여 멤버 변수로 선언하여 사용한다.
SocketAsyncEventArgs _sendArgs =
new SocketAsyncEventArgs();
Queue란?
간단하게 선입선출의 개념이라고 생각하면 된다. 먼저 저장된 애는 먼저 뽑아서 사용하여야 한다. 비슷한 개념으로 Stack(나중에들어온게 먼저나간다)이 있다.
_sendArgs를 여러개 생성해놓거나, 할 때마다 생성하는 구조가 아닌 멤버변수로 선언하여 하나씩 돌아가며 사용해야 하니 대기 하고 있는 작업들을 저장해놓기 위해 Queue를 사용한다.
// 버퍼의 사이즈에 Send 할 때 값이 달라지기에 byte[]사용
Queue<byte[]> _sendQueue = new Queue<byte[]>();
// lock도 걸어서 사용할 것이니 미리 추가해두자
object _lock = new object();
// 한 번에 실행되지 않게 하기 위한 boll 값 추가
bool _pending = false;
_sendQueue.Enqueue()
하여 우선 queue에 담아준다.
Register라는 곳에서 sendAsyn에 _sendArgs를 하나씩 넣어 동작 시키기 위해서는 한 번에 여러개가 들어가면 안되기에 lock 구조 안에서 실행시킨다.
마지막으로 _pending을 처리해주어, 이미 실행 중이라 대기중일 때 일감이 들어온다면 Queue만 들어가도록 해준다.
void RegisterSend()
{
_pending = true; // 들어오자마자 막기
byte[] buff = _sendQueue.Dequeue();
_sendArgs.SetBuffer(buff, 0, buff.Length);
// args가 true되어 콜백으로 OnSend를 부르기 전에 Send()에서 lock을 뚫고,
// EnQueue 해준 애는 어디선가 누군가가 처리는 해주어야한다.
bool pending = _socket.SendAsync(_sendArgs);
if (pending == false)
{
OnSendCompleted(null, _sendArgs);
}
}
위에서 넣어준 일감을 Dequeue로 뽑아 _sendArgs.SetBuffer()
를 통해 사이즈를 지정해준 뒤에 소켓에 담고 다음 작업으로 날린다.
이 부분 역시 Recev와 동일하게 여기서 _socket.SendAsync(_sendArgs)
이 뱉는 값이 true라면 알아서 콜백을 해주고, 만약에 너무 빨라서 true인데도 pending은 false인 상황이라면 스스로 실행시켜준다.
해당 영역 역시 콜백을 통해서도 들어올 수 있고, Register에서도 pending이 false인 상황에서도 들어오기에 lock으로 독립적인 구역을 만들어준다.
우선 먼저 Queue에 남은 게 있는지 체크를 하고 남아있다면 다시 buffer 설정 부터 일감 처리를 시키고, 아니라면 pending을 false 시켜 다시 대기 상태로 돌린다.
그후 실행시켜보면 잘 작동하는 걸 볼 수 있다. 다만, 아직은 너무 케이스가 작기도 하니 나중에 스트레스 테스트를 하며 찾아내볼 수 있을 것이다.
또한 처리 과정 중 _socket.SendAsync(_sendArgs)
를 계속 호출하는 방법은 비효율적이기에 다음 시간에 구조를 개선할 것이다.(어떤 것이 비효율인지는 아직 모르겠다.)
그동안 배운 개념들을 사용하여 비동기 상태로 변경해주었다. 개념만 알고 잘 사용하지 않던 Queue 같은 것도 이렇게 활용해보니 좋다. 나라면 아마 List에 담아서 사용했을 것 같다.
SocketAsyncEventArgs 멤버변수로 선언, Session - Start() 함수에서는 이벤트 연결만 해주기
Send() 함수 생성
1) Queue를 통해 한 번에 몰리지 않게 조정
2) lock 구조로 멀티 쓰레드 환경조성
RegisterSend() 함수 생성
1) DeQueue 방식으로 buff 값 넣기
OnSendCompleted() 함수 생성
1) lock 구조로 Queue에 남은 게 있다면 다시 Register를 동작 시켜 처리하기