协议缓冲区从原始消息中检测类型

时间:2012-02-02 23:29:35

标签: c# .net protocol-buffers protobuf-net

是否可以检测原始协议缓冲区消息的类型(在byte [])

我遇到端点可以接收不同消息的情况,我需要能够在反序列化之前检测到类型。

我正在使用protobuf-net

4 个答案:

答案 0 :(得分:15)

您不能孤立地检测类型,因为protobuf规范不会为此向流添加任何数据;但是,根据具体情况,有很多方法可以简化这一过程:

  • 联合类型(如Jon所述)涵盖了一系列场景
  • 继承(protobuf-net specific)可以是多功能的 - 你可以有一个基本消息类型,以及任意数量的具体消息类型
  • 您可以使用前缀来指示传入类型

在原始TCP流的情况下,最后一种方法实际上非常有价值;这是在线上与union类型相同,但具有不同的实现;通过预先确定1 = Foo,2 = Bar等(与联合类型方法完全相同),您可以使用SerializeWithLengthPrefix来编写(指定1/2 / etc作为字段编号),以及要读取的非泛型TryDeserializeWithLengthPrefix(这是在v1 API中的Serializer.NonGeneric下,或者在v2 API中的TypeModel上),您可以提供一个类型映射,将数字解析回类型,因此反序列化正确的类型。并预先解决“为什么这对TCP流有用?” - 因为:在正在进行的TCP流中,您需要使用WithLengthPrefix方法无论如何,以避免过度阅读流;所以你不妨免费获得类型标识符!

摘要:

  • 联合类型:易于实施;只有下方必须检查哪些属性是非空的
  • 继承:易于实现;可以使用多态或判别器来处理“现在怎么办?”
  • 类型前缀:实现起来有点繁琐,但允许更多的灵活性,并且在TCP流上没有任何开销

答案 1 :(得分:12)

一个典型的选择是让包装器消息充当“选项类型”或区别联合。您可以有一个枚举(每个消息类型一个)和一个包含消息类型的字段的消息,然后是每个消息类型一个可选字段。

这在Protobuf文档中描述为"union type"

答案 2 :(得分:3)

你可以像这样包装它。数据保存实际信息的地方。

message MyCustomProtocol {
  required int32 protocolVersion = 1;
  required int32 messageType = 2;
  bytes data = 3;
}

协议的一般规则是包含协议版本。一旦有了新老客户,您将非常乐意拥有它。

答案 3 :(得分:1)

您可以使用称为Self Describing Messages的技术。它可以用来生成一组.proto文件,这些文件描述包装器中编码为“ any”的每种消息类型。文档中的示例:

public class MainActivity extends AppCompatActivity implements VideoRendererEventListener {

private static final String TAG = "MainActivity";
private SimpleExoPlayerView simpleExoPlayerView;
private SimpleExoPlayer player;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

    BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
    TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
    TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);

    LoadControl loadControl = new DefaultLoadControl();

    player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);
    simpleExoPlayerView = new SimpleExoPlayerView(this);
    simpleExoPlayerView = findViewById(R.id.player_view);

    //Set media controller
    simpleExoPlayerView.setUseController(true);
    simpleExoPlayerView.requestFocus();

    // Bind the player to the view.
    simpleExoPlayerView.setPlayer(player);

    /// ========================== load file from url
    Uri mp4VideoUri = Uri.parse("http://playlist.m3u8");

    DefaultBandwidthMeter bandwidthMeterA = new DefaultBandwidthMeter();
    //Produces DataSource instances through which media data is loaded.
    DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, "exoplayer2example"), bandwidthMeterA);

    //FOR LIVESTREAM LINK:
    MediaSource videoSource = new HlsMediaSource(mp4VideoUri, dataSourceFactory, 1, null, null);
    final LoopingMediaSource loopingSource = new LoopingMediaSource(videoSource);

    // Prepare the player with the source.
    player.prepare(loopingSource);

    player.addListener(new ExoPlayer.EventListener() {
        @Override
        public void onTimelineChanged(Timeline timeline, Object manifest) {
            Log.v(TAG, "Listener-onTimelineChanged...");
        }

        @Override
        public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
            Log.v(TAG, "Listener-onTracksChanged...");
        }

        @Override
        public void onLoadingChanged(boolean isLoading) {
            Log.v(TAG, "Listener-onLoadingChanged...isLoading:" + isLoading);
        }

        @Override
        public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
            Log.v(TAG, "Listener-onPlayerStateChanged..." + playbackState);
        }

        @Override
        public void onRepeatModeChanged(int repeatMode) {
            Log.v(TAG, "Listener-onRepeatModeChanged...");
        }

        @Override
        public void onPlayerError(ExoPlaybackException error) {
            Log.v(TAG, "Listener-onPlayerError...");

            player.stop();
            player.prepare(loopingSource);
            player.setPlayWhenReady(true);


        }

        @Override
        public void onPositionDiscontinuity() {
            Log.v(TAG, "Listener-onPositionDiscontinuity...");
        }

        @Override
        public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
            Log.v(TAG, "Listener-onPlaybackParametersChanged...");
        }
    });

    player.setPlayWhenReady(true);
    player.setVideoDebugListener(this);
}

@Override
public void onVideoEnabled(DecoderCounters counters) {

}

@Override
public void onVideoDecoderInitialized(String decoderName, long initializedTimestampMs, long initializationDurationMs) {

}

@Override
public void onVideoInputFormatChanged(Format format) {

}

@Override
public void onDroppedFrames(int count, long elapsedMs) {

}

@Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
    Log.v(TAG, "onVideoSizeChanged [" + " width: " + width + " height: " + height + "]");

}

@Override
public void onRenderedFirstFrame(Surface surface) {

}

@Override
public void onVideoDisabled(DecoderCounters counters) {

}


@Override
protected void onStop() {
    super.onStop();
    Log.v(TAG, "onStop()...");
}

@Override
protected void onStart() {
    super.onStart();
}

@Override
protected void onResume() {
    super.onResume();
    Log.v(TAG, "onResume()...");
}

@Override
protected void onPause() {
    super.onPause();
    Log.v(TAG, "onPause()...");
}

@Override
protected void onDestroy() {
    super.onDestroy();
    Log.v(TAG, "onDestroy()...");
    player.release();
}

}

应注意,在编写此响应时,仅对这些消息提供本机支持。