270 lines
8.1 KiB
C#
270 lines
8.1 KiB
C#
/*
|
|
* 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();
|
|
}
|
|
}
|