2010-07-15 3 views
2

나는 현재 내가 다음 (간체) 코드를 사용하여 데이터를 수신 다중 스레드 응용 프로그램에서 작동 : 나는 receivedData가 예상되는 데이터뿐만 아니라 더 많은뿐만 아니라 포함되어 있음을 경험하고 있습니다소켓에서 수신 된 스트림이 단일 보내기 명령으로 제한됩니까?

private void BeginReceiveCallback(IAsyncResult ar) 
{ 
bytesReceived = this.Socket.EndReceive(ar); 
byte[] receivedData = new byte[bytesReceived]; 
Array.Copy(buffer, receivedData, bytesReceived); 

long protocolLength = BitConverter.ToInt64(receivedData, 0); 
string protocol = Encoding.ASCII.GetString(receivedData, 8, (int)protocolLength); 
IList<object> sentObjects = 
ParseObjectsFromNetworkStream(receivedData, 8 + protocolLength); 

InvokeDataReceived(protocol, sentObjects); 
} 

. 나는 이것이 나중에 스트림에있는 이전 것과 섞여서 보내진 데이터라고 의심한다.

내 질문에,이 버퍼에 어떤 데이터가 저장 될 것으로 예상됩니까? 클라이언트 측에서 두 가지 다른 보내기 작업의 데이터를 포함 할 수 있습니까? 이 경우 클라이언트 측에서 보낸 데이터 '메시지'를 구별 할 수있는 프로토콜이 필요하다고 생각합니다. 간단한 방법은 각 스트림을 특정 (고유 한) 바이트로 시작하고 끝내는 것입니다. 메시지 분리에 대한 일반적인 접근법이 있습니까? 게다가 이것은 단일 수신 호출이 클라이언트로부터 모든 데이터를 얻기에 충분하지 않을 수도 있다는 것을 의미하는 것으로 추측합니다. 이는 최종 바이트가 발견 될 때까지 반복해야한다는 것을 의미합니까?

답변

3

TCP/IP 소켓 연결은 두 개의 독립적 인 스트림 (수신과 수신)으로 구성됩니다.

이것은 종종 놓친 TCP/IP의 주요 개념 중 하나입니다. 응용 프로그램의 관점에서 볼 때 TCP/IP는 패킷에서 작동하지 않습니다. 스트림에서 작동합니다!

패킷을 보낼 방법이 없습니다. API는 단순히 존재하지 않습니다. 데이터를 보낼 때 그 바이트를 나가는 스트림에 넣기 만하면됩니다.그런 다음 상대방의 들어오는 스트림에서 읽습니다.

예를 들어, 한 쪽에서 5 바이트를 전송 한 다음 다른 5 바이트를 보낼 수 있습니다. 수신 측은 한 번에 5 바이트 또는 한 번에 하나씩 총 2 개의 배치를 수신 할 수 있습니다.

들어오는 바이트 스트림을 메시지로 분할하려면 메시지 프레임이 필요합니다. 두 가지 솔루션 중 하나가 일반적으로 사용됩니다. 당신이 제안한 메시지는 구분 기호입니다. 여기서 SOT/EOT 바이트는 메시지 경계를 지정하는 데 사용됩니다. 또 하나 (선호하는) 길이 접두어 솔루션입니다. 여기서 메시지의 길이는 메시지 자체의 접두어입니다.

더 자세한 설명은 on my blog이고 sample code for length prefixing입니다.

+0

간결한 답변 주셔서 감사합니다. 현재 솔루션은 길이 프리픽스를 사용하지만 메시지를 구별 할 수는 없습니다. 도착하지 않은 메시지의 부분을 정상적으로 처리하기 위해 어떤 접근법을 사용할 수 있습니까? 타임 아웃 기반 접근 방식을 추구하는 것이 가장 좋은 방법입니까? –

+0

또한, 메시지의 절반은 무엇입니까 - 중간이라고 말하면 분실됩니다. 갑자기 나는 다음 메시지의 반을 파싱하여 보증 된 충돌을 일으킬 것이다. TCP를 맹목적으로 신뢰하여 이러한 일이 발생하지 않도록 할 수 있습니까? –

+1

TCP는 안정적인 스트림을 제공합니다. 그것은 당신을 위해 잃어버린 조각의 재전송을 처리합니다. 연결이 재설정되지 않는 한 상대편 피어에 항상 한 피어에서 보내는 모든 바이트가 올바른 순서로 도착합니다. –

1

TCP/IP에서 데이터는 일반적으로 스트림으로 간주됩니다. 전송 된만큼받을 수 있습니다 (전송 된 모든 것을 얻으려면 다시 "수신"해야 할 수도 있음). 일반적인 상황은 두 종점에 일종의 앞뒤 대화가 있어야한다는 것입니다. 그것은 당신이 묘사하고있는 상황처럼 들리지는 않습니다. 애플리케이션이 수행하는 작업을 처리하는 것은 처음 몇 바이트 (예 : 4 바이트 정수)의 데이터 길이를 보내는 것입니다. 수신 측은 4 바이트를 수신하여 예상 길이를 찾은 다음 정확한 양을 수신합니다.

1

당신이 맞다면, 소켓이 수신 한 스트림이 잘 리거나 강제로 연결이 끊어 지거나 다른 전송 작업에서 추가 데이터가 포함 된 것으로 볼 수 있습니다.

당신은 내가 바이너리 데이터와 다른 먹으 렴을 위해 사용되는 통신의 경우 프로토콜이 TCP으로 수행 한 작업으로

(? 나는 그것이라고 생각 월리) 즉시 전송을 지정하는 소켓 설정으로 주위를 재생할 수 있습니다 ASCII를 위해, 내가 이진 데이터를 보낼 때 나는 모든 통신이 (BlockType (2bytes) BlockLength (4bytes) 데이터 (nBytes)로 시작해야 함) 알려진 데이터 길이를 가지고있다.

수신 끝은 6 바이트 타입과 길이를 알아내는 것. 만약 내가 6 바이트 미만 내가 이전의 값 등 버퍼를 시도하고, 한 번 설립 된 크기는 데이터에서 읽을 때까지 BlockLength 바이트 (필요한 버퍼링)까지 시도합니다. 당신이 소켓의 분리가 있으면 내가 아스키 데이터로 작업하고있는 경우

, 당신은 ~~~ 내가 (전송되는 데이터의 블록 주위에 고유의 포장 방법을 사용하는 등

재개 또는 다시 시작하거나 처리 할 필요 .... ~) .... .... DATA .... (~~~ end ~~~) 당신이 (~~~ end ~~~) 때까지 문자열builder 또는 유사 콘텐츠를 버퍼링하고 다음 계속해서 작업.

희망이 있습니다.

+0

예 - 이것을 Nagle 알고리즘이라고합니다. 이 기능이 활성화되어 있으면 기본 스택이 데이터를 보내기 전에 수 밀리 초 동안 데이터를 "보류"합니다. 해당 시간대에 더 많은 데이터가 제공되면 모든 데이터를 단일 송신으로 전 송합니다. –

+1

당신이하고있는 일을 정확히 안다면 * Nagle을 비활성화하지 않는 것이 중요합니다. –

관련 문제