diff --git a/Examples/PacketEchoClient/.editorconfig b/Examples/PacketEchoClient/.editorconfig new file mode 100644 index 0000000..67a80f6 --- /dev/null +++ b/Examples/PacketEchoClient/.editorconfig @@ -0,0 +1,124 @@ +# .editorconfig에 대해 자세히 알아보려면 다음을 참조하세요. https://aka.ms/editorconfigdocs +############################### +# Core EditorConfig Options # +############################### +root = true +# All files +[*] +indent_style = space +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +insert_final_newline = true +charset = utf-8-bom +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = true +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +############################### +# VB Coding Conventions # +############################### +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion diff --git a/Examples/PacketEchoClient/PacketEchoClient.csproj b/Examples/PacketEchoClient/PacketEchoClient.csproj index edebcc8..c997b9b 100644 --- a/Examples/PacketEchoClient/PacketEchoClient.csproj +++ b/Examples/PacketEchoClient/PacketEchoClient.csproj @@ -47,6 +47,7 @@ + diff --git a/Examples/PacketEchoServer/.editorconfig b/Examples/PacketEchoServer/.editorconfig new file mode 100644 index 0000000..67a80f6 --- /dev/null +++ b/Examples/PacketEchoServer/.editorconfig @@ -0,0 +1,124 @@ +# .editorconfig에 대해 자세히 알아보려면 다음을 참조하세요. https://aka.ms/editorconfigdocs +############################### +# Core EditorConfig Options # +############################### +root = true +# All files +[*] +indent_style = space +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +insert_final_newline = true +charset = utf-8-bom +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = true +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +############################### +# VB Coding Conventions # +############################### +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion diff --git a/Examples/PacketEchoServer/PacketEchoServer.csproj b/Examples/PacketEchoServer/PacketEchoServer.csproj index 8baa008..cda8f7f 100644 --- a/Examples/PacketEchoServer/PacketEchoServer.csproj +++ b/Examples/PacketEchoServer/PacketEchoServer.csproj @@ -47,6 +47,7 @@ + diff --git a/SimpleTcp/Client/BaseTcpClient.cs b/SimpleTcp/Client/BaseTcpClient.cs index ab1abb0..8e828fa 100644 --- a/SimpleTcp/Client/BaseTcpClient.cs +++ b/SimpleTcp/Client/BaseTcpClient.cs @@ -121,8 +121,12 @@ public void Disconnect() { lock(syncObject) { - tcpClient?.Close(); - tcpClient = null; + try + { + tcpClient?.Close(); + tcpClient = null; + } + catch { } buffer = null; ringBuffer = null; diff --git a/SimpleTcp/Server/BaseTcpServer.cs b/SimpleTcp/Server/BaseTcpServer.cs index 501f4f7..67d1ade 100644 --- a/SimpleTcp/Server/BaseTcpServer.cs +++ b/SimpleTcp/Server/BaseTcpServer.cs @@ -9,29 +9,29 @@ namespace SimpleTcp.Server { - public abstract class BaseTcpServer : IDisposable - { - #region Properties - /// + public abstract class BaseTcpServer : IDisposable + { + #region Properties + /// /// Server is started /// - public bool IsStarted { get => (tcpListener != null); } + public bool IsStarted { get => (tcpListener != null); } - /// + /// /// Connected clients /// - public IClient[] Clients - { - get - { - lock (syncObject) - { - return connections.ToArray(); - } - } - } - - /// + public IClient[] Clients + { + get + { + lock (syncObject) + { + return connections.ToArray(); + } + } + } + + /// /// Opened Server Port /// public int Port { get => (tcpListener?.LocalEndpoint as IPEndPoint)?.Port ?? -1; } @@ -45,23 +45,23 @@ public IClient[] Clients /// Get Total sended bytes count /// public long TotalSendedBytes { get; private set; } - #endregion + #endregion - #region Private Member - private object syncObject = new object(); - private TcpListener tcpListener = null; - private List connections = new List(); - #endregion + #region Private Member + private object syncObject = new object(); + private TcpListener tcpListener = null; + private List connections = new List(); + #endregion + + #region Protected Member - #region Protected Member - - #endregion + #endregion #region Public Member /// /// Client connected event handler /// - public event ClientConnectedHandler ClientConnected; + public event ClientConnectedHandler ClientConnected; /// /// Client disconnected event handler @@ -78,7 +78,7 @@ public IClient[] Clients /// If you specify a valid port, the server starts immediately. public BaseTcpServer(int port = -1) { - if(port > 0) + if (port > 0) { Start(port); } @@ -90,7 +90,7 @@ public BaseTcpServer(int port = -1) /// /// Server port public void Start(int port) - { + { lock (syncObject) { if (IsStarted) @@ -111,148 +111,156 @@ public void Start(int port) throw e; } } - } + } /// /// Stop tcp server /// public void Stop() - { + { lock (syncObject) { - tcpListener?.Stop(); - tcpListener = null; + try + { + connections?.ForEach(client => client.TcpClient?.Client?.Disconnect(false)); + connections?.Clear(); + } + catch { } - connections?.ForEach(client => client.TcpClient?.Client?.Disconnect(false)); - connections?.Clear(); + try + { + tcpListener?.Stop(); + tcpListener = null; + } + catch { } } - } - - public virtual void Dispose() - { - Stop(); - } - - protected IClient GetClient(TcpClient tcpClient) - { - lock(syncObject) - { - return connections.First(connection => connection.TcpClient == tcpClient); - } - } - - protected void WriteToAllClients(byte[] buffer, int offset, int count) - { - Parallel.ForEach(connections, connection => - { - connection.Write(buffer, offset, count); - }); - } - #endregion - - #region Protected Methods - protected virtual void OnDataReceived(IClient client, int receivedSize) { } - protected virtual void OnClientConnected(IClient client) { } - protected virtual void OnClientDisconnected(IClient client) { } - #endregion - - #region Private Methods - private void AcceptTcpClientCallback(IAsyncResult ar) - { - if(ar.AsyncState is TcpListener tcpListener) - { - TcpClient tcpClient = tcpListener.EndAcceptTcpClient(ar); - if(tcpClient != null) // new client connected - { - Connection connection = new Connection(this, tcpClient); - lock(syncObject) - { - connections.Add(connection); - } - - try - { - connection.BeginRead( - new Connection.DataReceivedCallback(DataReceivedCallback), - new Connection.DisconnectedCallback(DisconnectedCallback)); - OnClientConnected(connection); + } + + public virtual void Dispose() + { + Stop(); + } + + protected IClient GetClient(TcpClient tcpClient) + { + lock (syncObject) + { + return connections.First(connection => connection.TcpClient == tcpClient); + } + } + + protected void WriteToAllClients(byte[] buffer, int offset, int count) + { + Parallel.ForEach(connections, connection => + { + connection.Write(buffer, offset, count); + }); + } + #endregion + + #region Protected Methods + protected virtual void OnDataReceived(IClient client, int receivedSize) { } + protected virtual void OnClientConnected(IClient client) { } + protected virtual void OnClientDisconnected(IClient client) { } + #endregion + + #region Private Methods + private void AcceptTcpClientCallback(IAsyncResult ar) + { + if (ar.AsyncState is TcpListener tcpListener) + { + TcpClient tcpClient = tcpListener.EndAcceptTcpClient(ar); + if (tcpClient != null) // new client connected + { + Connection connection = new Connection(this, tcpClient); + lock (syncObject) + { + connections.Add(connection); + } + + try + { + connection.BeginRead( + new Connection.DataReceivedCallback(DataReceivedCallback), + new Connection.DisconnectedCallback(DisconnectedCallback)); + OnClientConnected(connection); ClientConnected?.Invoke(this, new ClientConnectedEventArgs(connection)); - } - catch - { - if (tcpClient.Connected) - { - tcpClient.Close(); - } - - lock (syncObject) - { - connections.Remove(connection); - } - } - - tcpListener.BeginAcceptTcpClient(new AsyncCallback(AcceptTcpClientCallback), this.tcpListener); - } - } - } - - private void DisconnectedCallback(Connection connection) - { - lock (syncObject) - { - if (connections.Contains(connection)) - { - connections.Remove(connection); - } - } - OnClientDisconnected(connection); + } + catch + { + if (tcpClient.Connected) + { + tcpClient.Close(); + } + + lock (syncObject) + { + connections.Remove(connection); + } + } + + tcpListener.BeginAcceptTcpClient(new AsyncCallback(AcceptTcpClientCallback), this.tcpListener); + } + } + } + + private void DisconnectedCallback(Connection connection) + { + lock (syncObject) + { + if (connections.Contains(connection)) + { + connections.Remove(connection); + } + } + OnClientDisconnected(connection); ClientDisconnected?.Invoke(this, new ClientDisconnectedEventArgs(connection)); - - } - private void DataReceivedCallback(Connection connection, int receivedSize) - { + } + + private void DataReceivedCallback(Connection connection, int receivedSize) + { TotalReceivedBytes += receivedSize; - OnDataReceived(connection, receivedSize); - } - #endregion - - protected class Connection : IClient - { - #region Properties - public TcpClient TcpClient { get; private set; } - public IPEndPoint IPEndPoint { get => TcpClient?.Client?.RemoteEndPoint as IPEndPoint; } - - public int BytesToRead { get => _ringBuffer.Count; } - public long DropBytes { get; private set; } = 0; + OnDataReceived(connection, receivedSize); + } + #endregion + + protected class Connection : IClient + { + #region Properties + public TcpClient TcpClient { get; private set; } + public IPEndPoint IPEndPoint { get => TcpClient?.Client?.RemoteEndPoint as IPEndPoint; } + + public int BytesToRead { get => _ringBuffer.Count; } + public long DropBytes { get; private set; } = 0; public long SendedBytes { get; private set; } = 0; public long ReceivedBytes { get; private set; } = 0; - #endregion + #endregion - public delegate void DataReceivedCallback(Connection connection, int receivedSize); - public delegate void DisconnectedCallback(Connection connection); + public delegate void DataReceivedCallback(Connection connection, int receivedSize); + public delegate void DisconnectedCallback(Connection connection); - #region Private Members - private object syncObject = new object(); - private byte[] _buffer; - private RingBuffer _ringBuffer; - private DataReceivedCallback _dataReceived; - private DisconnectedCallback _disconnected; + #region Private Members + private object syncObject = new object(); + private byte[] _buffer; + private RingBuffer _ringBuffer; + private DataReceivedCallback _dataReceived; + private DisconnectedCallback _disconnected; private BaseTcpServer _baseTcpServer; - #endregion + #endregion - public Connection(BaseTcpServer baseTcpServer, TcpClient tcpClient) - { - TcpClient = tcpClient; - _buffer = new byte[tcpClient.ReceiveBufferSize]; - _ringBuffer = new RingBuffer(tcpClient.ReceiveBufferSize); + public Connection(BaseTcpServer baseTcpServer, TcpClient tcpClient) + { + TcpClient = tcpClient; + _buffer = new byte[tcpClient.ReceiveBufferSize]; + _ringBuffer = new RingBuffer(tcpClient.ReceiveBufferSize); _baseTcpServer = baseTcpServer; - } + } - public void BeginRead(DataReceivedCallback dataReceivedCallback, DisconnectedCallback disconnectedCallback) - { - _dataReceived = dataReceivedCallback; - _disconnected = disconnectedCallback; + public void BeginRead(DataReceivedCallback dataReceivedCallback, DisconnectedCallback disconnectedCallback) + { + _dataReceived = dataReceivedCallback; + _disconnected = disconnectedCallback; try { TcpClient?.GetStream()?.BeginRead(_buffer, 0, _buffer.Length, new AsyncCallback(ReadCallback), this); @@ -261,54 +269,54 @@ public void BeginRead(DataReceivedCallback dataReceivedCallback, DisconnectedCal { _disconnected?.Invoke(this); } - } - - private void ReadCallback(IAsyncResult ar) - { - if (ar.AsyncState is Connection connection) - { - int readSize; - try - { - readSize = connection.TcpClient.GetStream()?.EndRead(ar) ?? 0; - } - catch { readSize = 0; } - - if (readSize == 0) // client disconnected when readSize is zero - { - _disconnected?.Invoke(this); - } - else - { - int writeBytes = _ringBuffer.Write(_buffer, 0, readSize); - if(writeBytes < readSize) - { - DropBytes += (readSize - writeBytes); - } + } + + private void ReadCallback(IAsyncResult ar) + { + if (ar.AsyncState is Connection connection) + { + int readSize; + try + { + readSize = connection.TcpClient.GetStream()?.EndRead(ar) ?? 0; + } + catch { readSize = 0; } + + if (readSize == 0) // client disconnected when readSize is zero + { + _disconnected?.Invoke(this); + } + else + { + int writeBytes = _ringBuffer.Write(_buffer, 0, readSize); + if (writeBytes < readSize) + { + DropBytes += (readSize - writeBytes); + } ReceivedBytes += readSize; - _dataReceived?.Invoke(this, readSize); - BeginRead(_dataReceived, _disconnected); - } - } - } - - public int Read(byte[] buffer, int offset, int count) - { - return _ringBuffer.Read(buffer, offset, count); - } - - public byte[] ReadExisting() - { + _dataReceived?.Invoke(this, readSize); + BeginRead(_dataReceived, _disconnected); + } + } + } + + public int Read(byte[] buffer, int offset, int count) + { + return _ringBuffer.Read(buffer, offset, count); + } + + public byte[] ReadExisting() + { return _ringBuffer.ReadExisting(); - } + } - public int ReadByte() - { - return _ringBuffer.ReadByte(); - } + public int ReadByte() + { + return _ringBuffer.ReadByte(); + } - public void Write(byte[] buffer, int offset, int count) - { + public void Write(byte[] buffer, int offset, int count) + { try { lock (syncObject) @@ -327,12 +335,12 @@ public void Write(byte[] buffer, int offset, int count) { _disconnected?.Invoke(this); } - } - - public override string ToString() - { - return IPEndPoint?.ToString(); - } - } - } + } + + public override string ToString() + { + return IPEndPoint?.ToString(); + } + } + } } diff --git a/SimpleTcp/SimpleTcp.csproj b/SimpleTcp/SimpleTcp.csproj index 610ab08..3f76e5a 100644 --- a/SimpleTcp/SimpleTcp.csproj +++ b/SimpleTcp/SimpleTcp.csproj @@ -9,9 +9,9 @@ https://github.com/akon47/SimpleTcp SimpleTcp Tcp Client Server Socket Write a class summary, fixed some bug - 1.0.3.0 - 1.0.3.0 - 1.0.3 + 1.0.4.0 + 1.0.4.0 + 1.0.4 logo.png Kim Hwan