2017-04-13 8 views
-1

나는 형식이다 내가 직렬화하고 TCP messages.The 클래스를 보낼 BinaryFormatter.Serialize 방법을 사용하고 끝을 :경우 BinaryFormatter는 TCP 메시지를 보내 - 확인 메시지

[Serializable] 
public class Message { 
     public int senderId; 
     public int metaData; 
     public foo moreMetaData; 
     public object[] message; 
} 

내가 그, 일반적으로 알고 모든 메시지의 끝을 결정하는 방법은 세 가지가있다 :

  • 앞 부분의 크기를 바이트
  • 메시지
  • 추가] 단부
  • 고정 메시지 길이 바이트

세 번째 옵션은 끔찍한 생각처럼 보입니다. 두 번째 옵션을 사용하면 어떻게 스트림에 바이트를 추가 할 수 있으며 여전히 수신 측에서 BinaryFormatter.deserialize를 호출 할 수 있습니까? 첫 번째 옵션 (역방향으로 진행하는 것에 대해 유감스럽게 생각합니다)을 사용하면 옵션 2와 동일한 문제가 발생합니다 (선행을 제외하고). 직렬화하기 전에 직렬화 크기를 결정할 추가 문제가 있습니다. 두 번 직렬화하지 않고는 불가능합니다. 한 번은 더미 변수에 크기를 결정한 다음 다시 실제 스트림 버퍼에 넣습니다. 여기서 일반적으로 수행되는 작업은 무엇입니까?

답변

1

BinaryFormatter는 이미 "Prepend size byte"를 내부적으로 구현합니다. 개체를 BinaryFormatter.Deserialize 메서드로 전달하면됩니다. 독자가 읽을 수있는 바이트 수를 자체적으로 파악할 수 있습니다.

참고 : BinaryFormatter는 어셈블리에서 버전 diffrences에 매우 민감합니다. 한쪽에 한 버전의 프로그램이 있고 다른 한쪽에 약간 오래된 버전이있는 경우 두 끝이 서로 이야기하지 못할 수 있습니다. 모델을 어셈블리 버전 번호에 묶지 않는 이진 serializer를 사용하는 것이 좋습니다. ProtoBuf-net은 대신 사용할 좋은 라이브러리입니다. 편집

: 여기 당신은 내가 `바이트 [] 버피 = 새로운 바이트를 사용하고

private async Task MessageLoop(NetworkStream networkStream) 
{ 
    //Lets pretend our protocall sends a byte with: 
    // - 1 if the next object will be a Foo, 
    // - 2 if the next object will be a Bar 
    // - 3 if the next object will be a Int32. 

    var formatter = new BinaryFormatter(); 
    byte[] buffer = new byte[1024]; 

    while (true) 
    { 
     var read = await networkStream.ReadAsync(buffer, 0, 1).ConfigureAwait(false); 
     if (read < 0) 
     { 
      await LogStreamDisconnectAsync(); 
     } 

     switch (buffer[0]) 
     { 
      case 1: 
       //If we are on a SynchronizationContext run the deseralize function on a new thread because that call will block. 
       Func<Foo> desearalize =()=> (Foo)formatter.Deserialize(networkStream); 
       Foo foo; 
       if (SynchronizationContext.Current != null) 
       { 
        foo = await Task.Run(desearalize).ConfigureAwait(false); 
       } 
       else 
       { 
        foo = desearalize(); 
       } 

       await ProcessFooAsync(foo).ConfigureAwait(false); 
       break; 
      case 2: 
       var bar = await Task.Run(() => (Bar)formatter.Deserialize(networkStream)).ConfigureAwait(false); 
       await ProcessBarAsync(bar).ConfigureAwait(false); 
       break; 
      case 3: 

       //We have to loop on Read because we may not get 4 bytes back when we do the call, so we keep calling till we fill our buffer. 
       var bytesRead = 0; 
       while (bytesRead < 4) 
       { 
        //We don't want to overwrite the buffer[0] so we can see the value in the debugger if we want, so we do 1 + bytesRead as the offset. 
        bytesRead += await networkStream.ReadAsync(buffer, 1 + bytesRead, 4 - bytesRead).ConfigureAwait(false); 
       } 

       //This assumes both ends have the same value for BitConverter.IsLittleEndian 
       int num = BitConverter.ToInt32(buffer, 1); 

       await DoSomethingWithANumberAsync(num).ConfigureAwait(false); 

       return; 
      default: 
       await LogInvaidRequestTypeAsync(buffer[0]).ConfigureAwait(false); 
       return; 
     } 
    } 

} 
+0

그것을 할 수있는 방법의 예입니다 [1024]; buffer에서 내 메시지를 읽으려면 networkStream.ReadAsync (buf, 0, buf.Length); ' 을 기다려야합니다. BinaryFormatter.deserialize를 호출 할시기를 어떻게 결정합니까? – WreckFish

+0

@WreckFish 다음 메시지 유형이 무엇인지 알려면 ReadAsync로만 읽은 다음 스트림을 Desearalize 함수에 전달합니다. 내 대답을 예제로 업데이트했습니다. –

+0

감사합니다. 점점 더 명확 해지고 있습니다. 그러나 나는 그 예제에 대해 몇 가지 질문을 가지고있다. 'var read = await networkStream.ReadAsync (buffer, 0, 1) .ConfigureAwait (false); '후에 나머지 데이터가 수신되는 시점은 어떻게 결정됩니까? 나는 이것이'SynchronizationContext.Current'와'ConfigureAwait (false)'와 관련이 있다고 가정하고 있지만 이것들이 무엇인지 또는 무엇인지 이해하지 못합니다. 또한 왜 비동기 호출이 너무 많이 발생합니까? 일단 데이터가 수신되면 deserialize() 및 processFoo()와 같은 호출을 동 기적으로 수행 할 여유가 없습니까? – WreckFish