diff --git a/assets/bin/generate_proto.bat b/assets/bin/generate_proto.bat
new file mode 100644
index 0000000..3e7d5a7
--- /dev/null
+++ b/assets/bin/generate_proto.bat
@@ -0,0 +1,5 @@
+::Protocol Buffer version 3.11.4
+set CURRENT_PATH=%cd%
+set UNITY_PACKET_PATH=%CURRENT_PATH%\..\csharp
+protoc -I=%CURRENT_PATH%\..\protobuf --dart_out=%CURRENT_PATH%\..\..\lib\socket\packets %CURRENT_PATH%\..\protobuf\*.proto --plugin=protoc_plugin.exe
+protoc -I=%CURRENT_PATH%\..\protobuf --csharp_out=%UNITY_PACKET_PATH% %CURRENT_PATH%\..\protobuf\*.proto
\ No newline at end of file
diff --git a/assets/bin/protoc.exe b/assets/bin/protoc.exe
new file mode 100644
index 0000000..501b41c
Binary files /dev/null and b/assets/bin/protoc.exe differ
diff --git a/assets/bin/protoc_plugin.exe b/assets/bin/protoc_plugin.exe
new file mode 100644
index 0000000..43490d1
Binary files /dev/null and b/assets/bin/protoc_plugin.exe differ
diff --git a/assets/csharp/MessageCommon.cs b/assets/csharp/MessageCommon.cs
new file mode 100644
index 0000000..a78a427
--- /dev/null
+++ b/assets/csharp/MessageCommon.cs
@@ -0,0 +1,385 @@
+//
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: message_common.proto
+//
+#pragma warning disable 1591, 0612, 3021
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+/// Holder for reflection information generated from message_common.proto
+public static partial class MessageCommonReflection {
+
+ #region Descriptor
+ /// File descriptor for message_common.proto
+ public static pbr::FileDescriptor Descriptor {
+ get { return descriptor; }
+ }
+ private static pbr::FileDescriptor descriptor;
+
+ static MessageCommonReflection() {
+ byte[] descriptorData = global::System.Convert.FromBase64String(
+ string.Concat(
+ "ChRtZXNzYWdlX2NvbW1vbi5wcm90byI7CgpQYWNrZXRCYXNlEhAKCHR5cGVO",
+ "YW1lGAEgASgJEg0KBW5vbmNlGAIgASgFEgwKBGRhdGEYAyABKAwiLgoMVGVt",
+ "cGxhdGVEYXRhEg0KBWluZGV4GAEgASgFEg8KB21lc3NhZ2UYAiABKAliBnBy",
+ "b3RvMw=="));
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+ new pbr::FileDescriptor[] { },
+ new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::PacketBase), global::PacketBase.Parser, new[]{ "TypeName", "Nonce", "Data" }, null, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::TemplateData), global::TemplateData.Parser, new[]{ "Index", "Message" }, null, null, null, null)
+ }));
+ }
+ #endregion
+
+}
+#region Messages
+public sealed partial class PacketBase : pb::IMessage {
+ private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new PacketBase());
+ private pb::UnknownFieldSet _unknownFields;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public static pb::MessageParser Parser { get { return _parser; } }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public static pbr::MessageDescriptor Descriptor {
+ get { return global::MessageCommonReflection.Descriptor.MessageTypes[0]; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ pbr::MessageDescriptor pb::IMessage.Descriptor {
+ get { return Descriptor; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public PacketBase() {
+ OnConstruction();
+ }
+
+ partial void OnConstruction();
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public PacketBase(PacketBase other) : this() {
+ typeName_ = other.typeName_;
+ nonce_ = other.nonce_;
+ data_ = other.data_;
+ _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public PacketBase Clone() {
+ return new PacketBase(this);
+ }
+
+ /// Field number for the "typeName" field.
+ public const int TypeNameFieldNumber = 1;
+ private string typeName_ = "";
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public string TypeName {
+ get { return typeName_; }
+ set {
+ typeName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ }
+ }
+
+ /// Field number for the "nonce" field.
+ public const int NonceFieldNumber = 2;
+ private int nonce_;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public int Nonce {
+ get { return nonce_; }
+ set {
+ nonce_ = value;
+ }
+ }
+
+ /// Field number for the "data" field.
+ public const int DataFieldNumber = 3;
+ private pb::ByteString data_ = pb::ByteString.Empty;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public pb::ByteString Data {
+ get { return data_; }
+ set {
+ data_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public override bool Equals(object other) {
+ return Equals(other as PacketBase);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public bool Equals(PacketBase other) {
+ if (ReferenceEquals(other, null)) {
+ return false;
+ }
+ if (ReferenceEquals(other, this)) {
+ return true;
+ }
+ if (TypeName != other.TypeName) return false;
+ if (Nonce != other.Nonce) return false;
+ if (Data != other.Data) return false;
+ return Equals(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public override int GetHashCode() {
+ int hash = 1;
+ if (TypeName.Length != 0) hash ^= TypeName.GetHashCode();
+ if (Nonce != 0) hash ^= Nonce.GetHashCode();
+ if (Data.Length != 0) hash ^= Data.GetHashCode();
+ if (_unknownFields != null) {
+ hash ^= _unknownFields.GetHashCode();
+ }
+ return hash;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public override string ToString() {
+ return pb::JsonFormatter.ToDiagnosticString(this);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public void WriteTo(pb::CodedOutputStream output) {
+ if (TypeName.Length != 0) {
+ output.WriteRawTag(10);
+ output.WriteString(TypeName);
+ }
+ if (Nonce != 0) {
+ output.WriteRawTag(16);
+ output.WriteInt32(Nonce);
+ }
+ if (Data.Length != 0) {
+ output.WriteRawTag(26);
+ output.WriteBytes(Data);
+ }
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(output);
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public int CalculateSize() {
+ int size = 0;
+ if (TypeName.Length != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(TypeName);
+ }
+ if (Nonce != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeInt32Size(Nonce);
+ }
+ if (Data.Length != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeBytesSize(Data);
+ }
+ if (_unknownFields != null) {
+ size += _unknownFields.CalculateSize();
+ }
+ return size;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public void MergeFrom(PacketBase other) {
+ if (other == null) {
+ return;
+ }
+ if (other.TypeName.Length != 0) {
+ TypeName = other.TypeName;
+ }
+ if (other.Nonce != 0) {
+ Nonce = other.Nonce;
+ }
+ if (other.Data.Length != 0) {
+ Data = other.Data;
+ }
+ _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public void MergeFrom(pb::CodedInputStream input) {
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+ break;
+ case 10: {
+ TypeName = input.ReadString();
+ break;
+ }
+ case 16: {
+ Nonce = input.ReadInt32();
+ break;
+ }
+ case 26: {
+ Data = input.ReadBytes();
+ break;
+ }
+ }
+ }
+ }
+
+}
+
+public sealed partial class TemplateData : pb::IMessage {
+ private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new TemplateData());
+ private pb::UnknownFieldSet _unknownFields;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public static pb::MessageParser Parser { get { return _parser; } }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public static pbr::MessageDescriptor Descriptor {
+ get { return global::MessageCommonReflection.Descriptor.MessageTypes[1]; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ pbr::MessageDescriptor pb::IMessage.Descriptor {
+ get { return Descriptor; }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public TemplateData() {
+ OnConstruction();
+ }
+
+ partial void OnConstruction();
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public TemplateData(TemplateData other) : this() {
+ index_ = other.index_;
+ message_ = other.message_;
+ _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public TemplateData Clone() {
+ return new TemplateData(this);
+ }
+
+ /// Field number for the "index" field.
+ public const int IndexFieldNumber = 1;
+ private int index_;
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public int Index {
+ get { return index_; }
+ set {
+ index_ = value;
+ }
+ }
+
+ /// Field number for the "message" field.
+ public const int MessageFieldNumber = 2;
+ private string message_ = "";
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public string Message {
+ get { return message_; }
+ set {
+ message_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public override bool Equals(object other) {
+ return Equals(other as TemplateData);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public bool Equals(TemplateData other) {
+ if (ReferenceEquals(other, null)) {
+ return false;
+ }
+ if (ReferenceEquals(other, this)) {
+ return true;
+ }
+ if (Index != other.Index) return false;
+ if (Message != other.Message) return false;
+ return Equals(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public override int GetHashCode() {
+ int hash = 1;
+ if (Index != 0) hash ^= Index.GetHashCode();
+ if (Message.Length != 0) hash ^= Message.GetHashCode();
+ if (_unknownFields != null) {
+ hash ^= _unknownFields.GetHashCode();
+ }
+ return hash;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public override string ToString() {
+ return pb::JsonFormatter.ToDiagnosticString(this);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public void WriteTo(pb::CodedOutputStream output) {
+ if (Index != 0) {
+ output.WriteRawTag(8);
+ output.WriteInt32(Index);
+ }
+ if (Message.Length != 0) {
+ output.WriteRawTag(18);
+ output.WriteString(Message);
+ }
+ if (_unknownFields != null) {
+ _unknownFields.WriteTo(output);
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public int CalculateSize() {
+ int size = 0;
+ if (Index != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeInt32Size(Index);
+ }
+ if (Message.Length != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(Message);
+ }
+ if (_unknownFields != null) {
+ size += _unknownFields.CalculateSize();
+ }
+ return size;
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public void MergeFrom(TemplateData other) {
+ if (other == null) {
+ return;
+ }
+ if (other.Index != 0) {
+ Index = other.Index;
+ }
+ if (other.Message.Length != 0) {
+ Message = other.Message;
+ }
+ _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public void MergeFrom(pb::CodedInputStream input) {
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ switch(tag) {
+ default:
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+ break;
+ case 8: {
+ Index = input.ReadInt32();
+ break;
+ }
+ case 18: {
+ Message = input.ReadString();
+ break;
+ }
+ }
+ }
+ }
+
+}
+
+#endregion
+
+
+#endregion Designer generated code
diff --git a/assets/protobuf/message_common.proto b/assets/protobuf/message_common.proto
new file mode 100644
index 0000000..0aa0cc3
--- /dev/null
+++ b/assets/protobuf/message_common.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+message PacketBase {
+ string typeName = 1;
+ int32 nonce = 2;
+ bytes data = 3;
+}
+
+message TemplateData {
+ int32 index = 1;
+ string message = 2;
+}
\ No newline at end of file
diff --git a/lib/communicator.dart b/lib/communicator.dart
new file mode 100644
index 0000000..111e273
--- /dev/null
+++ b/lib/communicator.dart
@@ -0,0 +1,89 @@
+
+// ignore_for_file: prefer_final_fields
+
+import 'package:protobuf/protobuf.dart';
+
+abstract class Communicator
+{
+ Map _handlerDic = {};
+ late Map)> _instanceGenerator;
+
+ Communicator();
+
+ void initialize(Map)> instanceGenerator)
+ {
+ _instanceGenerator = instanceGenerator;
+ }
+
+ T Function(List) getGenerator(String type)
+ {
+ if(!_instanceGenerator.containsKey(type))
+ {
+ throw Exception('Must set protobuf packet creator before use it.');
+ }
+ return _instanceGenerator[type] as T Function(List);
+ }
+
+ Future send(T data);
+
+ void onReceivedData(String typeName, List data)
+ {
+ if( _handlerDic.containsKey(typeName) ) {
+ _handlerDic[typeName]?.onMessage(data);
+ }
+ }
+
+ void addListener(void Function(T) listener)
+ {
+ var type = T.toString();
+ if(!_handlerDic.containsKey(type))
+ {
+ _handlerDic[type] = DataHandler(getGenerator(type));
+ }
+ var handler = _handlerDic[type] as DataHandler;
+ handler.addListener(listener);
+ }
+
+ void removeListener(void Function(T) listener)
+ {
+ var type = T.toString();
+ if(_handlerDic.containsKey(type))
+ {
+ var handler = _handlerDic[type] as DataHandler;
+ handler.removeListener(listener);
+ }
+ }
+}
+
+abstract class IDataHandler
+{
+ void onMessage(List data);
+}
+
+class DataHandler implements IDataHandler
+{
+ T Function(List) _generator;
+ List _listeners = [];
+
+ DataHandler(this._generator);
+
+ @override
+ void onMessage(List data)
+ {
+ for(var listener in _listeners)
+ {
+ listener.call(_generator(data));
+ }
+ }
+
+ void addListener(void Function(T) handler)
+ {
+ removeListener(handler); //중복 리스너 불허
+ _listeners.add(handler);
+ }
+
+ void removeListener(void Function(T) handler)
+ {
+ _listeners.remove(handler);
+ }
+}
\ No newline at end of file
diff --git a/lib/socket/packets/message_common.pb.dart b/lib/socket/packets/message_common.pb.dart
new file mode 100644
index 0000000..e9ac75a
--- /dev/null
+++ b/lib/socket/packets/message_common.pb.dart
@@ -0,0 +1,160 @@
+//
+// Generated code. Do not modify.
+// source: message_common.proto
+//
+// @dart = 2.12
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class PacketBase extends $pb.GeneratedMessage {
+ factory PacketBase({
+ $core.String? typeName,
+ $core.int? nonce,
+ $core.List<$core.int>? data,
+ }) {
+ final $result = create();
+ if (typeName != null) {
+ $result.typeName = typeName;
+ }
+ if (nonce != null) {
+ $result.nonce = nonce;
+ }
+ if (data != null) {
+ $result.data = data;
+ }
+ return $result;
+ }
+ PacketBase._() : super();
+ factory PacketBase.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+ factory PacketBase.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+
+ static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'PacketBase', createEmptyInstance: create)
+ ..aOS(1, _omitFieldNames ? '' : 'typeName', protoName: 'typeName')
+ ..a<$core.int>(2, _omitFieldNames ? '' : 'nonce', $pb.PbFieldType.O3)
+ ..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'data', $pb.PbFieldType.OY)
+ ..hasRequiredFields = false
+ ;
+
+ @$core.Deprecated(
+ 'Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+ 'Will be removed in next major version')
+ PacketBase clone() => PacketBase()..mergeFromMessage(this);
+ @$core.Deprecated(
+ 'Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+ 'Will be removed in next major version')
+ PacketBase copyWith(void Function(PacketBase) updates) => super.copyWith((message) => updates(message as PacketBase)) as PacketBase;
+
+ $pb.BuilderInfo get info_ => _i;
+
+ @$core.pragma('dart2js:noInline')
+ static PacketBase create() => PacketBase._();
+ PacketBase createEmptyInstance() => create();
+ static $pb.PbList createRepeated() => $pb.PbList();
+ @$core.pragma('dart2js:noInline')
+ static PacketBase getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create);
+ static PacketBase? _defaultInstance;
+
+ @$pb.TagNumber(1)
+ $core.String get typeName => $_getSZ(0);
+ @$pb.TagNumber(1)
+ set typeName($core.String v) { $_setString(0, v); }
+ @$pb.TagNumber(1)
+ $core.bool hasTypeName() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearTypeName() => clearField(1);
+
+ @$pb.TagNumber(2)
+ $core.int get nonce => $_getIZ(1);
+ @$pb.TagNumber(2)
+ set nonce($core.int v) { $_setSignedInt32(1, v); }
+ @$pb.TagNumber(2)
+ $core.bool hasNonce() => $_has(1);
+ @$pb.TagNumber(2)
+ void clearNonce() => clearField(2);
+
+ @$pb.TagNumber(3)
+ $core.List<$core.int> get data => $_getN(2);
+ @$pb.TagNumber(3)
+ set data($core.List<$core.int> v) { $_setBytes(2, v); }
+ @$pb.TagNumber(3)
+ $core.bool hasData() => $_has(2);
+ @$pb.TagNumber(3)
+ void clearData() => clearField(3);
+}
+
+class TemplateData extends $pb.GeneratedMessage {
+ factory TemplateData({
+ $core.int? index,
+ $core.String? message,
+ }) {
+ final $result = create();
+ if (index != null) {
+ $result.index = index;
+ }
+ if (message != null) {
+ $result.message = message;
+ }
+ return $result;
+ }
+ TemplateData._() : super();
+ factory TemplateData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+ factory TemplateData.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+
+ static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TemplateData', createEmptyInstance: create)
+ ..a<$core.int>(1, _omitFieldNames ? '' : 'index', $pb.PbFieldType.O3)
+ ..aOS(2, _omitFieldNames ? '' : 'message')
+ ..hasRequiredFields = false
+ ;
+
+ @$core.Deprecated(
+ 'Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+ 'Will be removed in next major version')
+ TemplateData clone() => TemplateData()..mergeFromMessage(this);
+ @$core.Deprecated(
+ 'Using this can add significant overhead to your binary. '
+ 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+ 'Will be removed in next major version')
+ TemplateData copyWith(void Function(TemplateData) updates) => super.copyWith((message) => updates(message as TemplateData)) as TemplateData;
+
+ $pb.BuilderInfo get info_ => _i;
+
+ @$core.pragma('dart2js:noInline')
+ static TemplateData create() => TemplateData._();
+ TemplateData createEmptyInstance() => create();
+ static $pb.PbList createRepeated() => $pb.PbList();
+ @$core.pragma('dart2js:noInline')
+ static TemplateData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create);
+ static TemplateData? _defaultInstance;
+
+ @$pb.TagNumber(1)
+ $core.int get index => $_getIZ(0);
+ @$pb.TagNumber(1)
+ set index($core.int v) { $_setSignedInt32(0, v); }
+ @$pb.TagNumber(1)
+ $core.bool hasIndex() => $_has(0);
+ @$pb.TagNumber(1)
+ void clearIndex() => clearField(1);
+
+ @$pb.TagNumber(2)
+ $core.String get message => $_getSZ(1);
+ @$pb.TagNumber(2)
+ set message($core.String v) { $_setString(1, v); }
+ @$pb.TagNumber(2)
+ $core.bool hasMessage() => $_has(1);
+ @$pb.TagNumber(2)
+ void clearMessage() => clearField(2);
+}
+
+
+const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names');
+const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names');
diff --git a/lib/socket/packets/message_common.pbenum.dart b/lib/socket/packets/message_common.pbenum.dart
new file mode 100644
index 0000000..e8eea36
--- /dev/null
+++ b/lib/socket/packets/message_common.pbenum.dart
@@ -0,0 +1,11 @@
+//
+// Generated code. Do not modify.
+// source: message_common.proto
+//
+// @dart = 2.12
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
+
diff --git a/lib/socket/packets/message_common.pbjson.dart b/lib/socket/packets/message_common.pbjson.dart
new file mode 100644
index 0000000..386b6cb
--- /dev/null
+++ b/lib/socket/packets/message_common.pbjson.dart
@@ -0,0 +1,44 @@
+//
+// Generated code. Do not modify.
+// source: message_common.proto
+//
+// @dart = 2.12
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
+
+import 'dart:convert' as $convert;
+import 'dart:core' as $core;
+import 'dart:typed_data' as $typed_data;
+
+@$core.Deprecated('Use packetBaseDescriptor instead')
+const PacketBase$json = {
+ '1': 'PacketBase',
+ '2': [
+ {'1': 'typeName', '3': 1, '4': 1, '5': 9, '10': 'typeName'},
+ {'1': 'nonce', '3': 2, '4': 1, '5': 5, '10': 'nonce'},
+ {'1': 'data', '3': 3, '4': 1, '5': 12, '10': 'data'},
+ ],
+};
+
+/// Descriptor for `PacketBase`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List packetBaseDescriptor = $convert.base64Decode(
+ 'CgpQYWNrZXRCYXNlEhoKCHR5cGVOYW1lGAEgASgJUgh0eXBlTmFtZRIUCgVub25jZRgCIAEoBV'
+ 'IFbm9uY2USEgoEZGF0YRgDIAEoDFIEZGF0YQ==');
+
+@$core.Deprecated('Use templateDataDescriptor instead')
+const TemplateData$json = {
+ '1': 'TemplateData',
+ '2': [
+ {'1': 'index', '3': 1, '4': 1, '5': 5, '10': 'index'},
+ {'1': 'message', '3': 2, '4': 1, '5': 9, '10': 'message'},
+ ],
+};
+
+/// Descriptor for `TemplateData`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List templateDataDescriptor = $convert.base64Decode(
+ 'CgxUZW1wbGF0ZURhdGESFAoFaW5kZXgYASABKAVSBWluZGV4EhgKB21lc3NhZ2UYAiABKAlSB2'
+ '1lc3NhZ2U=');
+
diff --git a/lib/socket/packets/message_common.pbserver.dart b/lib/socket/packets/message_common.pbserver.dart
new file mode 100644
index 0000000..34528ee
--- /dev/null
+++ b/lib/socket/packets/message_common.pbserver.dart
@@ -0,0 +1,14 @@
+//
+// Generated code. Do not modify.
+// source: message_common.proto
+//
+// @dart = 2.12
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
+
+export 'message_common.pb.dart';
+
diff --git a/lib/socket/protobuf_client.dart b/lib/socket/protobuf_client.dart
new file mode 100644
index 0000000..a447850
--- /dev/null
+++ b/lib/socket/protobuf_client.dart
@@ -0,0 +1,126 @@
+
+// ignore_for_file: avoid_init_to_null, prefer_final_fields, avoid_print
+
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:dart_framework/utils/system_util.dart';
+import 'package:protobuf/protobuf.dart';
+import 'package:dart_framework/communicator.dart';
+import 'package:dart_framework/socket/packets/message_common.pb.dart';
+
+class ProtobufClient extends Communicator
+{
+ final int _headerSize = 4;
+ final Socket _socket;
+ int? _length = null;
+ late List _arrivedData = [];
+ List _onDisconnectListenerList = [];
+
+
+ ProtobufClient(this._socket)
+ {
+ print('Connected New Client');
+ super.initialize({
+ (TemplateData).toString() : TemplateData.fromBuffer
+ });
+ _socket.listen(onData, onError: onError)
+ .asFuture().then(onDisconnected);
+ }
+
+ void addDisconnectListener(void Function(ProtobufClient) handler)
+ {
+ if(!_onDisconnectListenerList.contains(handler))
+ {
+ _onDisconnectListenerList.add(handler);
+ }
+ }
+
+ void removeDisconnectListener(void Function(ProtobufClient) handler)
+ {
+ if(_onDisconnectListenerList.contains(handler))
+ {
+ _onDisconnectListenerList.remove(handler);
+ }
+ }
+
+ void onDisconnected(dynamic data)
+ {
+ //client disconnected
+ for (var item in _onDisconnectListenerList) {
+ item.call(this);
+ }
+ }
+
+ void onError(dynamic e)
+ {
+ print('=========> onError: $e');
+ }
+
+ void onData(Uint8List data) async
+ {
+ try
+ {
+ // printPacket('## Received', data);
+ if(_length == null)
+ {
+ var header = data.sublist(0, _headerSize);
+ _length = header.buffer.asByteData().getInt32(0);
+ _arrivedData.addAll(data.sublist(_headerSize));
+ }
+ else
+ {
+ _arrivedData.addAll(data);
+ }
+
+ if(_arrivedData.length == _length)
+ {
+ var common = PacketBase.fromBuffer(_arrivedData);
+ var nonce = common.nonce;
+ onReceivedData(common.typeName, common.data);
+ _length = null;
+ _arrivedData.clear();
+ }
+ } on Exception catch(e) {
+ print(e);
+ }
+ }
+
+ @override
+ Future send(T data) async {
+ List packet = [];
+ var base = PacketBase();
+ base.typeName = data.info_.qualifiedMessageName;
+ base.nonce = 0;
+ base.data = data.writeToBuffer();
+ var baseBytes = base.writeToBuffer();
+ int length = baseBytes.length + _headerSize;
+ var header = Uint8List(_headerSize)
+ ..buffer.asByteData().setInt32(0, length);
+ packet.addAll(header);
+ packet.addAll(baseBytes);
+
+ // printPacket('Send', packet);
+
+ _socket.add(packet);
+ await _socket.flush();
+ return simpleFuture;
+ }
+
+ void printPacket(String prefix, List packet)
+ {
+ var s = StringBuffer();
+ for(var item in packet)
+ {
+ s.write('$item, ');
+ }
+ print('$prefix: ${s.toString()}');
+ }
+
+ void dispose()
+ {
+ _onDisconnectListenerList.clear();
+ _arrivedData.clear();
+ _socket.close();
+ }
+}
diff --git a/lib/socket/protobuf_server.dart b/lib/socket/protobuf_server.dart
new file mode 100644
index 0000000..0cc3053
--- /dev/null
+++ b/lib/socket/protobuf_server.dart
@@ -0,0 +1,42 @@
+// ignore_for_file: avoid_print, prefer_final_fields
+
+import 'dart:io';
+import 'package:dart_framework/socket/packets/message_common.pb.dart';
+import 'package:dart_framework/socket/protobuf_client.dart';
+
+class ProtobufServer
+{
+ late ServerSocket _server;
+ List _clientList = [];
+ String _host;
+ int _port;
+
+ ProtobufServer(this._host, this._port);
+
+ Future start() async
+ {
+ _server = await ServerSocket.bind(_host, _port);
+ _server.listen((Socket socket) {
+ var client = ProtobufClient(socket);
+ client.addDisconnectListener(onDisconnectedClient);
+ client.addListener(onTestSocket);
+ _clientList.add(client);
+ });
+ }
+
+ void onDisconnectedClient(ProtobufClient client)
+ {
+ _clientList.remove(client);
+ client.dispose();
+ print('Client disconnected');
+ }
+
+ void onTestSocket(TemplateData data)
+ {
+ print(data.message);
+ var test = TemplateData();
+ test.index = 3125;
+ test.message = 'This message from flutter';
+ _clientList[0].send(test);
+ }
+}