diff --git a/SimpleTCP.Tests/CommTest.cs b/SimpleTCP.Tests/CommTest.cs index 37040a3..357ab81 100644 --- a/SimpleTCP.Tests/CommTest.cs +++ b/SimpleTCP.Tests/CommTest.cs @@ -18,16 +18,18 @@ public class CommTest public void SimpleCommTest() { SimpleTcpServer server = new SimpleTcpServer().Start(8910); - SimpleTcpClient client = new SimpleTcpClient().Connect(server.GetListeningIPs().FirstOrDefault(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).ToString(), 8910); + SimpleTcpClient client = new SimpleTcpClient(new SimpleTcpParam{Name = "Alex"}).Connect(server.GetListeningIPs().FirstOrDefault(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).ToString(), 8910); - server.DelimiterDataReceived += (sender, msg) => { + server.DelimiterDataReceived += (sender, msg) => + { _serverRx.Add(msg.MessageString); string serverReply = Guid.NewGuid().ToString(); msg.ReplyLine(serverReply); _serverTx.Add(serverReply); }; - client.DelimiterDataReceived += (sender, msg) => { + client.DelimiterDataReceived += (sender, msg) => + { _clientRx.Add(msg.MessageString); }; @@ -69,7 +71,7 @@ public void SimpleCommTest() Assert.IsTrue(true); - + } } } diff --git a/SimpleTCP.Tests/ServerTests.cs b/SimpleTCP.Tests/ServerTests.cs index feea838..9e6619c 100644 --- a/SimpleTCP.Tests/ServerTests.cs +++ b/SimpleTCP.Tests/ServerTests.cs @@ -14,9 +14,10 @@ public class ServerTests : IDisposable public ServerTests() { _server = new SimpleTcpServer().Start(_serverPort); - } + } + - public void Dispose() + public void Dispose() { if (_server.IsStarted) _server.Stop(); diff --git a/SimpleTCP/MessagemUdp.cs b/SimpleTCP/MessagemUdp.cs new file mode 100644 index 0000000..fb1841a --- /dev/null +++ b/SimpleTCP/MessagemUdp.cs @@ -0,0 +1,53 @@ +using System.Linq; +using System.Net.Sockets; +using System.Text; + +namespace SimpleTCP +{ + public class MessagemUdp + { + private UdpClient _udpClient; + private Encoding _encoder; + private byte _writeLineDelimiter; + private bool _autoTrim; + + public byte[] Data { get; } + + public UdpClient TcpClient => _udpClient; + + internal MessagemUdp(byte[] data, UdpClient udpClient, Encoding stringEncoder, byte lineDelimiter, bool autoTrim) + { + Data = data; + _udpClient = udpClient; + _encoder = stringEncoder; + _writeLineDelimiter = lineDelimiter; + _autoTrim = autoTrim; + } + + public string MessageString => _autoTrim ? _encoder.GetString(Data).Trim() : _encoder.GetString(Data); + + public void Reply(byte[] data) + { + _udpClient.Send(data, data.Length); + } + + public void Reply(string data) + { + if (string.IsNullOrEmpty(data)) { return; } + Reply(_encoder.GetBytes(data)); + } + + public void ReplyLine(string data) + { + if (string.IsNullOrEmpty(data)) { return; } + if (data.LastOrDefault() != _writeLineDelimiter) + { + Reply(data + _encoder.GetString(new[] { _writeLineDelimiter })); + } + else + { + Reply(data); + } + } + } +} \ No newline at end of file diff --git a/SimpleTCP/Server/ConnectedClient.cs b/SimpleTCP/Server/ConnectedClient.cs index c1704a2..65179f4 100644 --- a/SimpleTCP/Server/ConnectedClient.cs +++ b/SimpleTCP/Server/ConnectedClient.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; +using System.Net; using System.Net.Sockets; -using System.Text; -using System.Threading.Tasks; namespace SimpleTCP { diff --git a/SimpleTCP/Server/ServerListener.cs b/SimpleTCP/Server/ServerListener.cs index 7f39d97..f0ad15b 100644 --- a/SimpleTCP/Server/ServerListener.cs +++ b/SimpleTCP/Server/ServerListener.cs @@ -1,14 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using System.Net; using System.Net.Sockets; -using System.Text; using System.Threading; -using System.Threading.Tasks; namespace SimpleTCP.Server { + internal class ServerListener { private TcpListenerEx _listener = null; @@ -104,7 +101,7 @@ private void RunLoopStep() if (_listener.Pending()) { - var newClient = _listener.AcceptTcpClient(); + var newClient = _listener.AcceptTcpClient(); _connectedClients.Add(newClient); _parent.NotifyClientConnected(this, newClient); } diff --git a/SimpleTCP/Server/TcpListenerEx.cs b/SimpleTCP/Server/TcpListenerEx.cs index 1811c48..83d4f89 100644 --- a/SimpleTCP/Server/TcpListenerEx.cs +++ b/SimpleTCP/Server/TcpListenerEx.cs @@ -19,8 +19,19 @@ public class TcpListenerEx : TcpListener /// /// An that represents the local endpoint to which to bind the listener . is null. public TcpListenerEx(IPEndPoint localEP) : base(localEP) - { - } + { + AcceptTcpClient(); + + + } + + + public TcpClient AcceptTcpClientX() + { + var a = this.AcceptTcpClient(); + + return a; + } /// /// Initializes a new instance of the class that listens for incoming connection attempts on the specified local IP address and port number. @@ -28,9 +39,9 @@ public TcpListenerEx(IPEndPoint localEP) : base(localEP) /// An that represents the local IP address. The port on which to listen for incoming connection attempts. is null. is not between and . public TcpListenerEx(IPAddress localaddr, int port) : base(localaddr, port) { - } + } - public new bool Active + public new bool Active { get { return base.Active; } } diff --git a/SimpleTCP/SimpleTCP.csproj b/SimpleTCP/SimpleTCP.csproj index bc5ae86..9fb5462 100644 --- a/SimpleTCP/SimpleTCP.csproj +++ b/SimpleTCP/SimpleTCP.csproj @@ -42,12 +42,15 @@ + + + diff --git a/SimpleTCP/SimpleTcpClient.cs b/SimpleTCP/SimpleTcpClient.cs index 5c66a93..36fd36c 100644 --- a/SimpleTCP/SimpleTcpClient.cs +++ b/SimpleTCP/SimpleTcpClient.cs @@ -3,21 +3,29 @@ using System.Diagnostics; using System.Linq; using System.Net.Sockets; -using System.Text; using System.Threading; -using System.Threading.Tasks; namespace SimpleTCP { - public class SimpleTcpClient : IDisposable + public class SimpleTcpClient : IDisposable { - public SimpleTcpClient() + private readonly SimpleTcpParam _param; + + public SimpleTcpClient() { StringEncoder = System.Text.Encoding.UTF8; ReadLoopIntervalMs = 10; Delimiter = 0x13; } + public SimpleTcpClient(SimpleTcpParam param) + { + _param = param; + StringEncoder = System.Text.Encoding.UTF8; + ReadLoopIntervalMs = 10; + Delimiter = 0x13; + } + private Thread _rxThread = null; private List _queuedMsg = new List(); public byte Delimiter { get; set; } @@ -38,8 +46,8 @@ public SimpleTcpClient Connect(string hostNameOrIpAddress, int port) throw new ArgumentNullException("hostNameOrIpAddress"); } - _client = new TcpClient(); - _client.Connect(hostNameOrIpAddress, port); + _client = new TcpClient(); + _client.Connect(hostNameOrIpAddress, port); StartRxThread(); @@ -145,7 +153,7 @@ private void NotifyEndTransmissionRx(TcpClient client, byte[] msg) public void Write(byte[] data) { if (_client == null) { throw new Exception("Cannot send data to a null TcpClient (check to see if Connect was called)"); } - _client.GetStream().Write(data, 0, data.Length); + _client.GetStream().Write(data, 0, data.Length); } public void Write(string data) @@ -184,6 +192,24 @@ public Message WriteLineAndGetReply(string data, TimeSpan timeout) return mReply; } + public Message WriteLineAndGetReply(byte[] data, TimeSpan timeout) + { + Message mReply = null; + this.DataReceived += (s, e) => { mReply = e; }; + Write(data); + + Stopwatch sw = new Stopwatch(); + sw.Start(); + + while (mReply == null && sw.Elapsed < timeout) + { + System.Threading.Thread.Sleep(10); + } + + return mReply; + } + + #region IDisposable Support private bool disposedValue = false; // To detect redundant calls diff --git a/SimpleTCP/SimpleTcpParam.cs b/SimpleTCP/SimpleTcpParam.cs new file mode 100644 index 0000000..bb2333d --- /dev/null +++ b/SimpleTCP/SimpleTcpParam.cs @@ -0,0 +1,7 @@ +namespace SimpleTCP +{ + public class SimpleTcpParam + { + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/SimpleTCP/SimpleTcpServer.cs b/SimpleTCP/SimpleTcpServer.cs index bdafc3e..4f21057 100644 --- a/SimpleTCP/SimpleTcpServer.cs +++ b/SimpleTCP/SimpleTcpServer.cs @@ -1,13 +1,10 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; -using System.Text; using System.Threading; -using System.Threading.Tasks; namespace SimpleTCP { @@ -26,6 +23,7 @@ public SimpleTcpServer() public event EventHandler ClientConnected; public event EventHandler ClientDisconnected; + public event EventHandler DelimiterDataReceived; public event EventHandler DataReceived; @@ -33,9 +31,9 @@ public IEnumerable GetIPAddresses() { List ipAddresses = new List(); - IEnumerable enabledNetInterfaces = NetworkInterface.GetAllNetworkInterfaces() - .Where(nic => nic.OperationalStatus == OperationalStatus.Up); - foreach (NetworkInterface netInterface in enabledNetInterfaces) + IEnumerable enabledNetInterfaces = NetworkInterface.GetAllNetworkInterfaces() + .Where(nic => nic.OperationalStatus == OperationalStatus.Up); + foreach (NetworkInterface netInterface in enabledNetInterfaces) { IPInterfaceProperties ipProps = netInterface.GetIPProperties(); foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses) @@ -64,10 +62,10 @@ public List GetListeningIPs() return listenIps.OrderByDescending(ip => RankIpAddress(ip)).ToList(); } - + public void Broadcast(byte[] data) { - foreach(var client in _listeners.SelectMany(x => x.ConnectedClients)) + foreach (var client in _listeners.SelectMany(x => x.ConnectedClients)) { client.GetStream().Write(data, 0, data.Length); } @@ -150,28 +148,28 @@ private static IEnumerable TryGetCurrentNetworkInterfaces() public SimpleTcpServer Start(int port, bool ignoreNicsWithOccupiedPorts = true) { var ipSorted = GetIPAddresses(); - bool anyNicFailed = false; + bool anyNicFailed = false; foreach (var ipAddr in ipSorted) { - try - { - Start(ipAddr, port); - } - catch (SocketException ex) - { - DebugInfo(ex.ToString()); - anyNicFailed = true; - } + try + { + Start(ipAddr, port); + } + catch (SocketException ex) + { + DebugInfo(ex.ToString()); + anyNicFailed = true; + } } - if (!IsStarted) - throw new InvalidOperationException("Port was already occupied for all network interfaces"); + if (!IsStarted) + throw new InvalidOperationException("Port was already occupied for all network interfaces"); - if (anyNicFailed && !ignoreNicsWithOccupiedPorts) - { - Stop(); - throw new InvalidOperationException("Port was already occupied for one or more network interfaces."); - } + if (anyNicFailed && !ignoreNicsWithOccupiedPorts) + { + Stop(); + throw new InvalidOperationException("Port was already occupied for one or more network interfaces."); + } return this; } @@ -191,9 +189,9 @@ public SimpleTcpServer Start(int port, AddressFamily addressFamilyFilter) return this; } - public bool IsStarted { get { return _listeners.Any(l => l.Listener.Active); } } + public bool IsStarted { get { return _listeners.Any(l => l.Listener.Active); } } - public SimpleTcpServer Start(IPAddress ipAddress, int port) + public SimpleTcpServer Start(IPAddress ipAddress, int port) { Server.ServerListener listener = new Server.ServerListener(this, ipAddress, port); _listeners.Add(listener); @@ -203,16 +201,18 @@ public SimpleTcpServer Start(IPAddress ipAddress, int port) public void Stop() { - _listeners.All(l => l.QueueStop = true); - while (_listeners.Any(l => l.Listener.Active)){ - Thread.Sleep(100); - }; + _listeners.All(l => l.QueueStop = true); + while (_listeners.Any(l => l.Listener.Active)) + { + Thread.Sleep(100); + }; _listeners.Clear(); } public int ConnectedClientsCount { - get { + get + { return _listeners.Sum(l => l.ConnectedClientsCount); } } @@ -226,6 +226,7 @@ internal void NotifyDelimiterMessageRx(Server.ServerListener listener, TcpClient } } + internal void NotifyEndTransmissionRx(Server.ServerListener listener, TcpClient client, byte[] msg) { if (DataReceived != null) @@ -251,20 +252,22 @@ internal void NotifyClientDisconnected(Server.ServerListener listener, TcpClient } } - #region Debug logging - - [System.Diagnostics.Conditional("DEBUG")] - void DebugInfo(string format, params object[] args) - { - if (_debugInfoTime == null) - { - _debugInfoTime = new System.Diagnostics.Stopwatch(); - _debugInfoTime.Start(); - } - System.Diagnostics.Debug.WriteLine(_debugInfoTime.ElapsedMilliseconds + ": " + format, args); - } - System.Diagnostics.Stopwatch _debugInfoTime; - - #endregion Debug logging - } + + + #region Debug logging + + [System.Diagnostics.Conditional("DEBUG")] + void DebugInfo(string format, params object[] args) + { + if (_debugInfoTime == null) + { + _debugInfoTime = new System.Diagnostics.Stopwatch(); + _debugInfoTime.Start(); + } + System.Diagnostics.Debug.WriteLine(_debugInfoTime.ElapsedMilliseconds + ": " + format, args); + } + System.Diagnostics.Stopwatch _debugInfoTime; + + #endregion Debug logging + } } diff --git a/SimpleTCP/SimpleUdpClient.cs b/SimpleTCP/SimpleUdpClient.cs new file mode 100644 index 0000000..cbbe27a --- /dev/null +++ b/SimpleTCP/SimpleUdpClient.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; + +namespace SimpleTCP +{ + public class SimpleUdpClient : IDisposable + { + private Thread _thread; + + internal bool QueueStop { get; set; } + + public bool Connected { get; private set; } + + public byte Delimiter { get; set; } + + public UdpClient UdpClient { get; private set; } + + public IPEndPoint EndPoint { get; set; } + + public SimpleUdpClient() + { + Connected = false; + Delimiter = 0x13; + } + + public event EventHandler DataReceived; + + public void Connect(string hostName, int port) + { + try + { + EndPoint = new IPEndPoint(IPAddress.Parse(hostName), port); + + UdpClient = new UdpClient(hostName, port); + UdpClient.Connect(hostName, port); + + Connected = UdpClient.Client.Connected; + + if (!Connected) return; + if (_thread != null) return; + + _thread = new Thread(ListLoop) { IsBackground = true }; + _thread.Start(); + + } + catch (Exception erro) + { + throw new Exception(erro.Message); + } + } + + public void Write(byte[] dados) + { + try + { + if (UdpClient?.Client == null) return; + if (!Connected) return; + if (!UdpClient.Client.Connected) return; + UdpClient?.Send(dados, dados.Length); + } + catch (Exception erro) + { + throw new Exception(erro.Message); + } + } + + public void Disconnect() + { + try + { + QueueStop = false; + Connected = false; + UdpClient?.Close(); + } + catch (Exception erro) + { + throw new Exception(erro.Message); + } + } + + private void ListLoop() + { + while (!QueueStop) + { + try + { + RunLoopStep(); + } + catch + { + // + } + + Thread.Sleep(10); + } + + _thread = null; + } + + private void RunLoopStep() + { + if (UdpClient == null) { return; } + if (UdpClient.Client == null) { return; } + if (UdpClient.Client.Connected == false) { return; } + var c = UdpClient; + + var bytesAvailable = c.Available; + if (bytesAvailable == 0) + { + Thread.Sleep(10); + return; + } + + var bytesReceived = new List(); + + var rec = EndPoint; + + while (c.Available > 0 && UdpClient.Client.Connected) + { + bytesReceived.AddRange(c.Receive(ref rec)); + } + + if (bytesReceived.Count > 0) + { + NotifyEndTransmissionRx(c, bytesReceived.ToArray()); + } + } + + private void NotifyEndTransmissionRx(UdpClient client, byte[] msg) + { + if (DataReceived == null) return; + var m = new MessagemUdp(msg, client, Encoding.ASCII, Delimiter, false); + DataReceived(this, m); + } + + public void Dispose() + { + QueueStop = false; + UdpClient.Close(); + ((IDisposable)UdpClient)?.Dispose(); + } + + protected virtual void OnDataReceived(MessagemUdp e) + { + DataReceived?.Invoke(this, e); + } + } +}