다시 코드로 돌아와서
오늘은 ReceiveBuffer를 개선을 해보는 시간을 갖도록 하겠다.
이게 "패킷"으로 넘어가기 위한 기초 작업이다.
Session에서 여기서 SetBuffer해줌
이녀석을 설정만 하고 안 바꿨으니까
결국 이 부분에서 보내준 메세지는 무엇이냐하면은
buffer는
byte[1024] 에다가 시작 offset은 0이고 크기는 1024만큼을 우리가 Read를 할 수 있다고
간접적으로 얘기를 해준 것이다.
TCP 이론 시간에 설명을 한 부분인데 100byte를 보냈다고 해서 무조건 100byte가 가는 것은 아니다.
실제로 100byte를 보내고 그 100을 받아야지만 서버에서 분석을 하고 처리를 할 수 있음에도 불구하고
TCP의 특성때문에 100이 안오고 80정도만 올 수 있다는 얘기이다.
그래서 여기서 80만오면 바로 처리를 못하니까 RecvBuff에다가 보관만 하고 있다가,
나중에 20이 오면 조립을 해서 한방에 보낼 수 있게
수정을 해야된다는 것이다.
이런식으로 OnRecv가 완벽한 상태로 왔다고 가정을 하고
이렇게 다시 만들어서
등록을 하는 것이 아니라
80만 왔다고 가정을 하면은
80바이트 후에
이부분에서 offset이 바뀐다는 말이다.
0부터 시작이 아니라 80으로 조정을 해가지고,
그 다음 위치부터 데이터를 조정을 해가지고 받아야 된다는 얘기이다.
RecvBuffer.cs 만들어 주자.
이딴 식으로 만들어 줌.
ArraySegment로 만들어 주는데 혹시라도 나중에 엄청나게 큰 바이트 배열에서
부분적으로 사용하고 싶을 수도 있으니까 Segment로 만들어 주도록 하자.
그 다음
생성자에서 buffer사이즈를 받아주도록 하자.
생성자에서 버퍼 사이즈를 받아서 _buffer를 초기화를 해주도록 하자.
이녀석이 우리가 사용할 ReadBuffer가 되는 것이다.
readPos, writePos의 개념이 들어갈 것이다.
커서가 여기서 깜빡깜빡 거리는데 이상태에서 자판을 치면 해당 입력을 받은
예를 들어 asdad이런게 들어 갈 것인데
이게 _writePos의 개념이 될 것이고
이렇게
이렇게 앞에서 부터 뒤로읽는 커서가 될 것이다.
그래서 처음에 애내들이 시작을 할때는
둘다
첫번째 Pos에서 시작을 할 것이다,
(지금 10바이트라고 가정을 하고 하는 것이다)
그런데, 클라에서 5바이트의 데이터를 보냈다고 가정을 하면은
그렇다면 5byte를 Receive하는데 성공을 했으니까
writePos를
이렇게 밀어 줄 것이다.
이런다음 어떤 처리도 하지않고 패킷을 클라로 부터 받게 된다면
이 부분이 아니라 이제는
이 부분 부터 이제 계속 이어서 받겠다라는 의미이다.
그렇다면 r이라는 readPos는 왜 있느냐?
반대로 이제 컨텐츠 코드에서
이쪽을 처리를 할지말지 결정을 해야된다.
그러니까 만약에 5byte를 받았는데
5byte가 하나의 패킷의 크기 여가지고 한번에 처리를 할 수 있었다고 가정을 하면은
readPos도 5byte를 처리를 해가지고
이렇게 이동을 하는 것이다.
그런데 그게 아니라 만약에 실제로는 패킷 크기가 8byte짜리 여가지고
아직 데이터가 모자라다 싶으면은
이상태에서 다음 패킷까지 기다려가지고 변화를 주지 않을 것이다.
그런데 이상태에서 추가로 3byte가 더 온다고 하면 w가 옮겨 질 것이다.
이렇게 옮겨 질 것이고
이제서야
이렇게 완성된 패킷이 있으니까, 이녀석을 처리를 할 수 있을 것이다.
그래서 성공적으로 처리가 된다면 read가
이렇게 돌아오게 된다는 뜻이다.
그리고 중간중간에 한번씩 정리를 해줘가지고
rw를
이렇게 돌아오게 할 것이다.
이게 일반적인 경우이다.
이제는 반대로 패킷들이 다 2byte짜리 였다고 가정을 해보도록 하자.
그래서 4byte짜리 패킷이 와야되는데
운이 나빳는지 3byte만 왔다고 가정을 해보자.
그럼 w는 이쪽에 위치를 하게 될 것이다.
그리고 지금까지 데이터 유효범위는
이까지가 된다.
그런데 이상태로 "클라"쪽에다가 넘기게 되면은
분석을 해보니까
이렇게 첫 2byte는 처리를 할 수 있을 것이다.
그러면 처리를 한다음에 r을
이렇게 옮길 것이다.
그런데 그 다음에는 데이터의 "유효 범위"가
이렇게 1byte밖에 안되는데
이녀석 같은 경우에는 처리를 할 수 없다.
그러니까, 이 상태에서 대기를 해야된다는 얘기이다.
그래서 이런식으로 계속 뒤로 밀리게 되면은
나중에 buffer공간이 부족하게 되니까
이런 경우에는 어떻게 하냐하면은...
r이랑 w를 맨 처음으로 복사를 하는 것이다.
지금 유효범위가 1byte만 있으니까 중간중간에 정리를 한다고 했는데
이게 계속 밀리면 안되니까
이렇게 옮겨 준다는 것이다.
이상태를 분석을 해보면
읽기시작하는 처음 커서의 Pos는
이부분이고
쓰는 writePos의 커서는 이쪽이다.
그러니까 데이터 "유효범위"는
이쪽이고,
나중에 우리가 AsyncRecv를 요청을 할 때는
여기서부터 써달라고 요청을 하면된다.
그래서 이런 느낌으로 이제 작업을 하면 될 것이다.
DataSize, FreeSize를 계산을 해보도록 하자.
DataSize는 말그대로 "유효범위" == 데이터가 지금 얼마나 쌓여있는지를 말한다.
첫상태는
이렇게 rw가 곂쳐 있었다고 했었다.
(데이터가 올때마다 w가 (오른쪽으로) 점점 늘어날 것이다.)
2byte가 왔다면
이렇게 이동 할 것임.
그래서 결국 실제 DataSize는 얼마냐?
_writePos에서 _readPos를 "뺀" 값이 되는 것이다.
이렇게 처리를 하게되면
여기 실제로 버퍼에 들어가있고, 아직 "처리되지 않은" 데이터의 Size가 될 것이다.
이것은 지금 뭘 의미하냐하면은
지금
이 Buffer의 남은 공간을 얘기를 하는 것이다.
지금 이상태를 유지를 하고 있다고 가정을 하면은
AsyncReceive를 추가로 한다고 가정을 했을 때,
이만큼이 "유효범위" 이다. == 빈공간이다.
그리고
이쪽 공간은 건들면 안되는 공간이 되겠습니다~
여기에는 혹시라도 나중에 처리해야될 데이터가 들어가 있는 공간이고
여기서 부터 작업을 이어서 _write를 해주면 된다.
그래서 _buffer.Count를 하게 되면
여기 전체 크기가 나오게 되고
_writePos를 빼주게 되면
여기 남아있는 공간이 나오게 된다.
(이해가 안가면 w를 r과 곂쳐 있다고 생각을 해보면됨 -> 그럼 10칸 나온다)
Segment두개 만들어주는데
ReadSegment같은 경우네는 무 하는애냐고 하면은
이정도 위치에 있다고 가정을 하면은
ReadSegment같은경우에는 == 유효범위의 Segment라고 해가지고
"어디서부터 데이터를 읽으면 되냐??"라고 요청을 하는 것이다.
(어디서 부터 읽을지 컨텐츠 딴에 넘겨주는 것이다)
지금 같은 경우에는 ReadSegment가
이까지가 될 것이다.
즉, 말그대로 현재까지 받은 데이터의 유효범위가 어디서부터 어디까지 이냐를 나타내는 것이다.
그래서 r ~ 부터 w 이전까지 집어서 ReadSegment로 주는 것인데
시작위치는 _buffer.Array로 뽑아 올 수 있었고
Offset은 _buffer.Offset + readPos 해주고
전체 사이즈는 DataSize를 해주면된다.
이녀석은 어디서부터 어디까지 유효범위인지 나타내기 때문에
첫인자는 _buffer.Array로 마찬가지로 받고
두번째는 _buffe.offset
여기서 _writePos를 더한
여기서부터 시작점이라고 offset을 설정을하고
크기는 FreeSize (우리가 계산한) 로 해주면 될 것이다.
이 녀석 같은 경우에는 중간중간에 한번씩 정리르 해야되는데
이렇게 한번씩 앞으로 댕겨서 사용해야된다.
만약
이상태라고 가정을 하면은
데이터 유효범위는 이까지 이다.
이 3byte는 건들면 안되고
이렇게 3byte를 복사를 하고 당겨줘야지만 처음 위치로 돌아 갈텐데
그런데 만약에 r && w 가 운좋게 곂친다 == 데이터가 아무것도 없다 라고하면은
커서 위치만 시작 위치로 돌려보내면 된다.
이렇게 받아와서 dataSize가 0이라면
== 클라에서 보낸 모든 데이터를 다 처리를 한 상태
그럴때는 커서를 그냥 처음 위치로 바꿔주면 된다.
그래서 Copy를 이버젼을 사용할 것인데
rw 같으면 커서 위치만 리셋.
어디서 == source, 어디로 == destination, 크기
_buffer.Array에서 시작을 해가지고
offset은 _buffer.offset + _readPos부터 시작할 것이다.
이 위치 offset을 정해준 것이다.
그 다음에 이녀석을 처음으로 돌려보내야 되니까
destination은
_buffer.Array에서 _buffer.Offset이 첫 위치가 될 것이고
크기는 dataSize만큼 해준다.
그러면 이제
데이터를 복사를 해서 시작위치로 돌려보냈으면은,
이렇게 될 것이다.
그래서 이렇게 이동을 시켜준게 된다.
그다음
이녀석들 움직이는 부분 구현해야되는데
1) 데이터 성공적으로 Read해서 처리 했을때
OnRead
읽는 것이 DataSize보다 크면 안되니까 이렇게 해주자
2) 데이터를 Receive를 해서 받았을 때 == Write해야 될 때
OnWrite()
애도 똑같은 남아있는 size == FreeSize 보다 크면 안되니까
Session으로 돌아와서
여기서 이렇게 만들고 있었는데
이렇게 만들게 아니라
이렇게 해주고
이거 여기서 만드는게 아니라 삭제 ㄱㄱ
그러면 어느 시점에서 RecvBuff가 호출이 되어야 하느냐 보면은
여기서 뭔가가 이루어 져야 한다.
여기서 현재 유효한 범위를 찝어 줘야 한다는 것이다.
여기서 다음으로 buffer를 받을 공간은 Writesegment로 관리를 하고 있었다.
이녀석을 추출을 해주도록 하겠다.(segment로 받자)
그래서 이제 _recvArgs.SetBuffer를 해주는데
시작은 segment.Array로 받아주고
Offset도 segment받아주고 크기도 남아있는 WriteSegment만큼 (FreeSize)받아준다.
이게 FreeSize Receive할수 있는 크기.
그리고 Register하기 전에 Clean을 함 해줘서 뒤로 가있는것을 방지하도록 하겠다.
그래서 이렇게 한 것은
"유효 범위"를 찝어 준것이다.
이 상태에서
이제 Async를 하게 되면,
OnRecvCompleted가 호출이 될 것인데
여기서 이제또 코드를 넣어줘야된다.
누군가 나한테 데이터를 보냈다 == OnWrite했다고 하면은
커서를 이동을 시켜 줄 것인데
애가 수신 받은 애니까 args.BytesTransferred 만큼 이동 시켜 줄 것이다.
라고하면 뭔가 버그가 있다는 것이니까 연결 끊겠다.
그다음 해야할 부분인데
이녀석이 컨텐츠 코드에다가 데이터를 넘겨주는 부분이 였는데
이런식으로 첨음부터 args.Buffer, 0 부터 끝까지 == args.BytesTransferred 까지 넘겨주는게 아니라
데이터 범위를 찝어서 넘겨줘야 하는데
그 인터페이스를 ReadSegment로 만든것을 넘겨주자.
그러면 컨텐츠 == 서버 에서 OnRecv를 보면
여기서 OnRecv를 해줬는데
이제는 이녀석을 최대한 처리를 해줄려고하다가
부분적인 패킷이였다고 하면은 다 처리를 못할 테니까
그런데 그게 아니라 다 패킷이 전부다오면 처리를 할텐데
근데 지금 OnRecv가 처리를 했는지의 "여부"를 받고싶으니까
이녀석의 인터페이스를 좀 바꿔주자.
그래서 이렇게하면 OnRecv는 이제 얼마만큼의 데이터를 처리를 했느냐를 뱉어주게 될 것이다.
Server > 수정 (임시로)
Dummy > 수정 (임시)
다시 세션으로 와서
얼마만큼의 데이터를 처리를 했는지 processLen로 받겠다.
그리고 이럴 경우에도 연결을 끊겠다. => 말이 안되니까
그럼 이까지 잘 왔다면
데이터를 처리를 했거나 보류를 했거나 둘중 하나일 텐데
이제는 처리를한 Read커서를 이동을 시켜주겠다.
OnRead가
이것인데
Read커서를 이동을 시킬 것인데 false라면 연결 끊는다.
그리도 다시한번 RegisterRecv가 일어나면
다시 이런식으로 진행이 될 것이다.
이해가 안가는 부분이??
지금 이것을 보면 if문에서 함수를 호출을 해도
이렇게 10이였는데 함수가 실행이 되니까 15로 출력이 된다.
그래서
SEssion에서
이부분에서도
이렇게 처리를 해주면
_wrtie커서를 움직이기 + 오류가 났는지 안났는지 더블 체크 가능함!!
ㅅㅂ ㅈㄴ 지리네