vitorsousa pushed a commit to branch master. http://git.enlightenment.org/tools/examples.git/commit/?id=566b5c3ee732b1f76d873d0544638fc53ae24b73
commit 566b5c3ee732b1f76d873d0544638fc53ae24b73 Author: Vitor Sousa <vitorsousasi...@gmail.com> Date: Thu Mar 1 10:28:17 2018 -0300 net_mono: add net reference examples to the C# binding --- reference/csharp/net/meson.build | 10 ++ reference/csharp/net/src/meson.build | 22 +++ reference/csharp/net/src/net_io.cs | 269 ++++++++++++++++++++++++++++ reference/csharp/net/src/net_io_buffered.cs | 181 +++++++++++++++++++ reference/csharp/net/src/net_session.cs | 135 ++++++++++++++ 5 files changed, 617 insertions(+) diff --git a/reference/csharp/net/meson.build b/reference/csharp/net/meson.build new file mode 100644 index 0000000..18a8171 --- /dev/null +++ b/reference/csharp/net/meson.build @@ -0,0 +1,10 @@ +project( + 'efl-reference-net', 'c', + version : '0.0.1', + meson_version : '>= 0.38.0') + +efl_mono = dependency('efl-mono', version : '>=1.20.99') +efl_mono_libs = efl_mono.get_pkgconfig_variable('mono_libs') + +subdir('src') + diff --git a/reference/csharp/net/src/meson.build b/reference/csharp/net/src/meson.build new file mode 100644 index 0000000..9897ac6 --- /dev/null +++ b/reference/csharp/net/src/meson.build @@ -0,0 +1,22 @@ +deps = [efl_mono] + +executable('efl_reference_net_io', + files(['net_io.cs']), + dependencies : deps, + cs_args : efl_mono_libs, + install : true +) + +executable('efl_reference_net_io_buffered', + files(['net_io_buffered.cs']), + dependencies : deps, + cs_args : efl_mono_libs, + install : true +) + +executable('efl_reference_net_session', + files(['net_session.cs']), + dependencies : deps, + cs_args : efl_mono_libs, + install : true +) diff --git a/reference/csharp/net/src/net_io.cs b/reference/csharp/net/src/net_io.cs new file mode 100644 index 0000000..f797867 --- /dev/null +++ b/reference/csharp/net/src/net_io.cs @@ -0,0 +1,269 @@ +/* + * Efl.Net input/output examples. + * + * This example builds on the core_io example by connecting to a remote server + * using a dialer and a command queue. The response is printed to stdout. + */ + +using System; + +public class ExampleRunner +{ + private eina.List<efl.io.Copier> waiting = null; + private eina.List<string> commands = null; + private eina.Slice delimiter; + private efl.net.dialer.Tcp dialer = null; + private efl.io.Copier sender = null; + private efl.io.Copier receiver = null; + + public void Run() + { + efl.ui.Config.Run(); + } + + // call this method to cleanly shut down our example + public void Quit() + { + if (waiting != null) + { + Console.Error.WriteLine("ERROR: {0} operations were waiting!", waiting.Length); + waiting.Dispose(); + waiting = null; + } + + if (receiver != null) + { + receiver.Close(); + receiver.GetDestination().Dispose(); + receiver.Dispose(); + receiver = null; + } + + if (sender) + { + sender.Close(); + sender.GetSource().Dispose(); + source.Dispose(); + } + + if (dialer) + dialer.Dispose(); + + // efl_exit(retval); // TODO missing + efl.ui.Config.Exit(); + } + + // iterate through the commands to send through the dialler + public void CommandNext() + { + efl.io.Reader send_queue = sender.GetSource(); + if (commands != null) + { + send_queue.EosMark(); + return; + } + + string cmd = commands[0]; + // commands.RemoveAt(0); // TODO missing + + eina.Slice slice; + // slice = (Eina_Slice)EINA_SLICE_STR(cmd); // TODO missing + send_queue.Write(slice, null); + // Console.WriteLine("INFO: sent '{0}'", EINA_SLICE_STR_PRINT(slice)); // TODO missing + + // don't use delimiter directly, 'Len' may be changed! + slice = delimiter; + send_queue.Write(slice, null); + } + + void ReceiverData(efl.io.Queue sender, EventArgs e) + { + eina.Slice slice = sender.GetSlice(); + + // Can be caused when we issue efl.io.Queue.Clear() + if (slice.Len == 0) return; + + // If the server didn't send us the line terminator and closed the + // connection (ie: efl_io_reader_eos_get() == true) or if the buffer + // limit was reached then we may have a line without a trailing delimiter. + + // if (slice.EndsWith(delimiter)) // TODO missing + // slice.Len -= delimiter.Len; + + // Console.WriteLine("INFO: received '{0}'", EINA_SLICE_STR_PRINT(slice)); // TODO missing + + sender.Clear(); + CommandNext(); + } + + void DialerConnected(efl.net.dialer.Tcp sender, EventArgs e) + { + Console.WriteLine("INFO: connected to {0} ({1})", sender.GetAddressDial(), sender.GetAddressRemote()); + + CommandNext(); + } + + void CopierDone(efl.io.Copier sender, EventArgs e) + { + Console.WriteLine("INFO: {0} done", sender.GetName()); + + // waiting.Remove(sender); // TODO missing + if (waiting.Empty()) + Quit(EXIT_SUCCESS); + } + + void CopierError(efl.io.Copier sender, eina.Error perr) + { + Console.Error.WriteLine(stderr, "INFO: {0} error: #{1} '{2}'", sender.GetName(), perr, perr.Message); + + Quit(EXIT_FAILURE); + } + + private static void SetCopierCbs(efl.io.Copier copier) + { + copier.DONE += CopierDone; + copier.ERROR += CopierError; + } + + + public ExampleRunner() + { + string address = "example.com:80"; + ulong buffer_limit = 128; + efl.io.Queue send_queue, receive_queue; + + commands = new eina.List<string>(); + commands.Append("HEAD / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n"); + // delimiter = (Eina_Slice)EINA_SLICE_STR("\r\n"); // TODO missing + + // Without a send_queue we'd have to manually implement an + // efl.io.Reader object that would provide partial data when + // efl.io.Reader.read() is called by efl.io.Copier. This is + // cumbersome... we just want to write a full command and have the + // queue to handle that for us. + // + // Our example's usage is to write each command at once followed by + // the line delimiter, then wait for a reply from the server, then + // write another. + + try + { + send_queue = new efl.io.QueueConcrete(null, (efl.io.Queue equeue) => { + equeue.SetName("send_queue"); + equeue.SetLimit(buffer_limit); + }); + } + catch + { + Console.Error.WriteLine("ERROR: could not create efl.io.Queue (send)"); + Quit(EXIT_FAILURE); + throw; + } + + // Without a receive_queue we'd have to manually implement an + // efl.io.Writer object that would handle write of partial data + // with efl.io.Writer.write() is called by efl.io.Copier. + // + // For output we could have another solution as well: use null + // destination and handle "line" or "data" events manually, + // stealing the buffer so it doesn't grow. + // + // Our example's usage is to peek its data with GetSlice() then + // Clear(). + try + { + receive_queue = new efl.io.QueueConcrete(null, (efl.io.Queue equeue) => { + equeue.SetName("receive_queue"); + equeue.SetLimit(buffer_limit); + }); + receive_queue.SLICE_CHANGED += ReceiverData; + } + catch + { + Console.Error.WriteLine("ERROR: could not create efl.io.Queue (receive)"); + Quit(EXIT_FAILURE); + throw; + } + + // some objects such as the Efl.Io.Copier and Efl.Net.Dialer.Tcp + // depend on main loop, thus their parent must be a loop + // provider. We use the loop passed to our main method. + // efl.Loop loop = ev->object; // TODO missing + + // The TCP client to use to send/receive network data + try + { + dialer = new efl.net.dialer.TcpConcrete(loop, (efl.net.dialer.Tcp edialer) => { + edialer.SetName("dialer"); + }); + dialer.CONNECTED += DialerConnected; + } + catch + { + Console.Error.WriteLine("ERROR: could not create efl.net.dialer.Tcp"); + Quit(EXIT_FAILURE); + throw; + } + + // sender: send_queue->network + try + { + sender = new efl.io.CopierConcrete(loop, (efl.io.Copier esender) => { + esender.SetName("sender"); + esender.SetLineDelimiter(delimiter); + esender.SetSource(send_queue); + esender.SetDestination(dialer); + }); + SetCopierCbs(sender); + } + catch + { + Console.Error.WriteLine("ERROR: could not create efl.io.Copier (sender)"); + Quit(EXIT_FAILURE); + throw; + } + + // receiver: network->receive_queue + try + { + receiver = new efl.io.CopierConcrete(loop, (efl.io.Copier ereceiver) => { + ereceiver.SetName("receiver"); + ereceiver.SetLineDelimiter(delimiter); + ereceiver.SetSource(dialer); + ereceiver.SetDestination(send_queue); + }); + SetCopierCbs(receiver); + } + catch + { + Console.Error.WriteLine("ERROR: could not create Efl_Io_Copier (receiver)"); + Quit(EXIT_FAILURE); + throw; + } + + eina.Error err = dialer.Dial(address); + if (err != eina.Error.NO_ERROR) + { + var msg = $"ERROR: could not dial {address}: {err.Message}"; + Console.Error.WriteLine(msg); + Quit(EXIT_FAILURE); + throw new SEHException(msg); + } + + waiting.Append(sender); + waiting.Append(receiver); + } +} + +public static class Example +{ + public static void Main() + { + efl.All.Init(efl.Components.Basic); + + var exr = new ExampleRunner(); + exr.Run(); + + efl.All.Shutdown(); + } +} diff --git a/reference/csharp/net/src/net_io_buffered.cs b/reference/csharp/net/src/net_io_buffered.cs new file mode 100644 index 0000000..3a159e7 --- /dev/null +++ b/reference/csharp/net/src/net_io_buffered.cs @@ -0,0 +1,181 @@ +/* + * Efl.Net buffered input/output examples. + * + * This example builds on the net_io example by using a buffered_stream to + * simplify the logic. This helpfully provides the input and output queues + * and a copier internally. They can be accessed from the buffered stream + * if required but as demonstrated here that is likely not necessary. + */ + +using System; + +public class ExampleRunner +{ + private eina.List<string> commands = null; + private eina.Slice delimiter; + private efl.net.dialer.Tcp dialer = null; + private efl.io.buffered.Stream stream = null; + + public void Run() + { + efl.ui.Config.Run(); + } + + public void Quit(int retval) + { + if (stream != null) + { + stream.Close(); + stream.Dispose(); + stream = null; + } + + if (dialer != null) + { + dialer.Dispose(); + dialer = null; + } + + // efl_exit(retval); TODO missing + efl.ui.Config.Exit(); + } + + public void CommandNext() + { + if (commands.Empty()) + { + stream.EosMark(); + return; + } + + string cmd = commands[0]; + // commands.RemoveAt(0); // TODO missing + + // eina.Slice slice = (Eina_Slice)EINA_SLICE_STR(cmd); // TODO missing + stream.Write(slice, null); + // Console.Error.WriteLine("INFO: sent '{0}'", EINA_SLICE_STR_PRINT(slice)); // TODO missing + + // don't use delimiter directly, 'Len' may be changed! + slice = delimiter; + stream.Write(slice, null); + } + + void StreamLine(efl.io.buffered.Stream sender, EventArgs e) + { + eina.Slice slice = sender.GetSlice(); + + // Can be caused when we issue efl.io.buffered.Stream.Clear() + if (slice.Len == 0) return; + + // If the server didn't send us the line terminator and closed the + // connection (ie: efl_io_reader_eos_get() == true) or if the buffer + // limit was reached then we may have a line without a trailing delimiter. + // if (slice.EndsWith(delimiter)) // TODO missing + // slice.Len -= delimiter.Len; + + // Console.WriteLine("INFO: received '{0}'", EINA_SLICE_STR_PRINT(slice)); // TODO missing + + sender.Clear(); + CommandNext(); + } + + void DialerConnected(efl.net.dialer.Tcp sender, EventArgs e) + { + Console.WriteLine("INFO: connected to {0} ({1})", sender.GetAddressDial(), sender.GetAddressRemote()); + + CommandNext(); + } + + void StreamDone(efl.io.buffered.Stream sender, EventArgs e) + { + Console.WriteLine("INFO: {0} done", sender.GetName()); + + Quit(EXIT_SUCCESS); + } + + void StreamError(efl.io.buffered.Stream sender, eina.Error err) + { + Console.Error.WriteLine("INFO: {0} error: #{1} '{2}'", sender;GetName(), err, err.Message); + + Quit(EXIT_FAILURE); + } + + private static void SetStreamCbs(efl.io.buffered.Stream s) + { + s.LINE += StreamLine; + s.EOS += StreamDone; + s.ERROR += StreamError; + } + + public ExampleRunner() + { + string address = "example.com:80"; + ulong bufferLimit = 128; + + commands = new eina.List<string>(); + commands.Append("HEAD / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n"); + // delimiter = (Eina_Slice)EINA_SLICE_STR("\r\n"); // TODO missing + + // some objects such as the Efl.Io.Copier and Efl.Net.Dialer.Tcp + // depend on main loop, thus their parent must be a loop + // provider. We use the loop passed to our main method. + // efl.Loop loop = ev->object; // TODO missing + + // The TCP client to use to send/receive network data + try + { + dialer = new efl.net.dialer.TcpConcrete(loop, (efl.net.dialer.Tcp edialer) => { + edialer.SetName("dialer"); + }); + dialer.CONNECTED += DialerConnected; + } + catch + { + Console.Error.WriteLine("ERROR: could not create efl.net.dialer.Tcp"); + Quit(EXIT_FAILURE); + throw; + } + + + // Without the buffered stream we'd have to create two Efl.Io.Queue + // ourselves, as well as two Efl.Io.Copier to link them with the + // dialer. + // + // Our example's usage is to write each command at once followed by + // the line delimiter, then wait for a reply from the server, then + // write another. + // + // On incoming data we peek at it with GetSlice() and then Clear(). + + stream = new efl.io.buffered.StreamConcrete(loop, (efl.io.buffered.Stream estream) => { + estream.SetName("stream"); + estream.SetInnerIo(dialer); + estream.SetDelimiter(delimiter); + estream.SetMaxQueueSizeInput(bufferLimit); + estream.SetMaxQueueSizeOutput(bufferLimit); + }); + SetStreamCbs(stream); + + eina.Error err = dialer.Dial(address); + if (err != eina.Error.NO_ERROR) + { + var msg = $"ERROR: could not dial {address}: {err.Message}"; + Console.Error.WriteLine(msg); + Quit(EXIT_FAILURE); + throw new SEHException(msg); + } + } +} + +public class Example +{ + public static void Main() + { + efl.All.Init(efl.Components.Basic); + + var exr = new ExampleRunner(); + exr.Run(); + + efl.All.Shutdown(); + } +} diff --git a/reference/csharp/net/src/net_session.cs b/reference/csharp/net/src/net_session.cs new file mode 100644 index 0000000..2c70397 --- /dev/null +++ b/reference/csharp/net/src/net_session.cs @@ -0,0 +1,135 @@ +/* + * Efl.Net session/connectivity examples. + * + * NOTE: This example currently requires the Connman backend to be running. + * + * This example uses the Efl.Net.Session APIs to get connectivity information + * about the current networking setup. It then sets up a callback for network + * changes and will print the details on any change. + */ + +using System; + +public class Example +{ + // Convert a session state to a string for printing. + public static string StateName(efl.net.session.State state) + { + switch (state) + { + case efl.net.session.State.Offline: + return "offline"; + case efl.net.session.State.Local: + return "local"; + case efl.net.session.State.Online: + return "online"; + default: + return "???"; + } + } + + private static readonly Dictionary<efl.net.session.Technology, string> names = + new Dictionary<efl.net.session.Technology, string> { + { efl.net.session.Technology.Unknown, "unknown" }, + { efl.net.session.Technology.Ethernet, "ethernet" }, + { efl.net.session.Technology.Wifi, "wifi" }, + { efl.net.session.Technology.Bluetooth, "bluetooth" }, + { efl.net.session.Technology.Cellular, "cellular" }, + { efl.net.session.Technology.Vpn, "vpn" }, + { efl.net.session.Technology.Gadget, "gadget" } + }; + + // Convert a session technology to a string for printing. + public static string TechnologyName(efl.net.session.Technology tech) + { + string name; + if (!names.TryGetValue(tech, out name)) + return "???"; + return name; + } + + // Tthe callback that indicates some connectivity changed within the session. + // Print information about the session to the console. + static void SessionChanged(efl.net.Session session, EventArgs e) + { + Console.WriteLine("Session changed:"); + Console.WriteLine(" name: '{0}'", session.GetName()); + Console.WriteLine(" state: {0}", StateName(session.GetState())); + Console.WriteLine(" technology: {0}", TechnologyName(session.GetTechnology())); + Console.WriteLine(" interface: '{0}'", session.GetInterface()); + + // print out additional information if we have an IPv4 session + string ip, netmask, gateway; + session.GetIpv4(out ip, out netmask, out gateway); + if (ip != null) + { + Console.WriteLine($" IPv4: {ip}, gateway={gateway}, netmask={netmask}"); + } + + // print out additional information if we have an IPv6 session + byte prefix; + session.GetIpv6(out ip, out prefix, out netmask, out gateway); + if (ip != null) + { + Console.WriteLine($" IPv6: {ip}/{prefix}, gateway={gateway}, netmask={netmask}"); + } + } + + // Quit the app after a timer tick. + static void QuitCb(object sender, EventArgs e) + { + // efl_exit(0); // TODO missing + efl.ui.Config.Exit(); + } + + public static void Main() + { + bool doConnect = true; + bool requireOnline = false; + + efl.All.Init(efl.Components.Basic); + + // efl.Loop loop = ev->object; // TODO missing + + // create a session that watches specifically for ethernet, wifi and bluetooth + int technologies = efl.net.session.Technology.Ethernet | + efl.net.session.Technology.Wifi | efl.net.session.Technology.Bluetooth; + + try + { + efl.net.Session session = new efl.net.SessionConcrete(loop, (efl.net.Session esession) => { + esession.SetName("Example Session"); + // register the change callback for network state + esession.CHANGED += SessionChanged; + }); + } + catch + { + eina.Log.Error("Could not create Efl.Net.Session object.\n"); + // efl_exit(EXIT_FAILURE); // TODO missing + efl.ui.Config.Exit(); + throw; + } + + if (doConnect) + { + Console.WriteLine("Requesting a {0} connection.", requireOnline ? "online" : "local"); + session.Connect(requireOnline, technologies); + } + + Console.WriteLine("The session will remain active while this application runs."); + Console.WriteLine("Use ^C (Control + C) to close it"); + + // Wait for 10 seconds before exiting this example + new efl.loop.TimerConcrete(loop, (efl.loop.Timer etimer) => { + etimer.SetInterval(10.0); + etimer.TICK += QuitCb; + }); + + // start the main loop + efl.ui.Config.Run(); + + efl.All.Shutdown(); + } +} + --