다른 포스팅에서 'SocketAsyncEventArgs'를 사용하는 방법을 알아봤습니다.

(참고 : [.Net] 'SocketAsyncEventArgs' 사용하기)

 

그런데 'SocketAsyncEventArgs'를 이용하면 이상한 현상이 있는데....

설정된 크기 이상의 데이터를 보내면 리시브가 여러 번 오거나 데이터가 깨져서 오는 현상이 있습니다.

(디폴트값이 8192byte였나 그럴 겁니다.)

이런 문제 때문에 큰 데이터(Large Data)를 보낼 수 없다는 것입니다.

 

그런데 말입니다.

몇 가지 테스트를 해보니 이게 문제가 아니고 제가 관련 정보를 못 찾아서 그런거 같습니다 ㅎㅎㅎㅎ

(바보 된 거죠-_-;)

 

 

1. 문제의 발견

한번 'SocketAsyncEventArgs'로 큰 데이터를 보내 봅시다.

 

 

리시브 한 번에 8192byte가 왔습니다.

그리고 다시 리시브를 받으면....

 

 

헛?

 

 

 

 

 

2. 문제의 원인

처음에는 이게 리시브 자체가 꼬이는 줄 알았는데 그게 아닙니다.

 

2-1. 보내는쪽 데이터 확인

일단 보내는 쪽 데이터를 확인해 봅시다.

 

 

8192번째 데이터 주변을 확인해봅시다.

2-2. 받는쪽 데이터 확인

그리고 받는 쪽 첫 번째 리시브의 데이터입니다.

 

 

받는 쪽 두 번째 리시브 데이터입니다.

 

 

이것으로 우리가 알 수 있는 건 큰 데이터의 경우 리시브가 버퍼만큼 오고 나머지 데이터는 다음 리시브에서 온다는 것을 알 수 있습니다.

그런데 데이터가 이상하다?

첫 번째 데이터는 제대로 왔는데 두 번째 데이터가 이상합니다.

 

8190번째부터 데이터가

112, 178, 187, 107, 0, 20, 156, 217

이니 두 번째 데이터는 원본데이터의 8192번째부터 시작하고 있는데 첫 번째 데이터의 맨 끝 데이터는 178이 아닙니다. 

 

3. 없어진 데이터를 찾아서...

그렇다면 없어진 데이터는 어디로 간 것일 까요?

 

여기서 잠깐 짚고 넘어가야 할 것이 8192byte는 헤더가 포함되지 않은 크기입니다.

하지만 제가 데이터 확인용으로 따로넣은 4byte의 데이터가 추가되어 있습니다.

즉, 첫 번째 데이터는 8186byte였다는 것입니다.

그렇다면 두 번째 데이터에서 잃어버린 데이터는 4byte라는 것입니다.

 

오호 드디어 실마리가 잡히네요.

두 번째 리시브의 헤더를 봅시다.

 

 

역시나 빠진 '178'이 여기 있습니다.

 

결국  큰 데이터를 보내면 두 번째 리시브부터는 뒷부분 데이터가 들어온다는 것입니다.

헤더 자리에 데이터를 넣어서 말이죠.

그래서 두번째 리시브부터는 '8192byte + 4byte = 8196byte'만큼 데이터가 오게 됩니다.

4. 주의 사항

이제 우리는 'SocketAsyncEventArgs'를 사용할 때 데이터가 설정된 크기로 잘라서 읽어드린다는 것을 알았습니다.

문제는 리시브가 전달되었다고 해서 설정된 크기만큼의 데이터를 받았음을 의미하지는 않는다는 것입니다.

전체 데이터를 다 받고 나서 리시브가 오는 것이 아니라 데이터 전송이 시작되면(헤더 기준인 듯) 리시브가 오기 시작합니다.

그리고 리시브가 오고 나서서도 대기('Thread.Sleep'과 같은 방법으로)를 하고 있으면 데이터가 계속 오는 것을 확인할 수 있습니다.

결국 속도가 느리거나 하는 문제로 데이터가 빨리 오지 않으면 리시브가 몇 번 올지 알 수 없다는 의미가 됩니다.

(하지만 첫 리시브는 무조건 4바이트가 수신되야 오기 때문에 크기를 받아오기위한 버퍼는 무조건 수신됩니다.)

 

그리고 데이터가 오지않았더라도 리시브는 옵니다.

즉, 'Socket.Available'이 '0'이여도 리시브는 오게 됩니다.

이런경우 잠시 기다리면 데이터가 축적됩니다.

 

'SocketAsyncEventArgs' 처리 순서

위의 내용들을 토대로 'SocketAsyncEventArgs'를 처리하기위한 방법은 다음과 같습니다.

 

--- 첫번째 리시브 ---

1. 헤더를 읽어 전체 파일크기를 받아온다.

2. 헤더를 제외한 데이터를 저장한다.

 

--- 두번째 리시브 부터 계속 ---

3. 헤더와 전체데이터를 읽습니다.

4. 전체 파일크기만큼 데이터를 읽었는지 확인합니다.

5. 전체 데이터가 온것이 아니라면 다음 리시브로 처리를 넘깁니다.

 

--- 마지막 리시브 ---

6. 전체 데이터가 왔으면 다음 리시브는 없으므로 데이터 처리를 합니다.

 

마무리

이런 내용을 제가 못 찾았다는 게 맞겠죠-_-;;

그 잘돼 있는 MSDN에서도 해당 내용을 못 찾고 삽질을 하다니 OTL

 

원래 'SocketAsyncEventArgs'를 사용할 때는 소켓 객체에 직접 접근하는건 좋은 생각이 아닙니다.

이 글에서 사용된 예제는 'SocketAsyncEventArgs'와 소켓에 직접 접근하는 방법을 섞어 쓰고 있는데 결과적으로 이것때문에 더욱 코드가 복잡해지는 문제가 있습니다.

 

검색해보니 'socketasynceventargs.setbuffer'에 버그가 있다는 소리가 있네요.

 

이 문제를 해결하여 대용량 데이터를 보내는 예제입니다.

참고 : SocketAsync Chatting 0.82 - 'SocketAsyncEventArgs'를 이용한 Client/Server

 

 



 

 

댓글 작성

이름
패스워드
홈페이지
비밀글

티스토리 툴바