2019-02-13 02:58:33 -08:00
# define EFL_BETA_API_SUPPORT
2018-01-09 15:43:09 -08:00
# include <Efl_Net.h>
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
# include <Ecore_Getopt.h>
# include <fcntl.h>
# include <ctype.h>
static Eina_Bool do_read = EINA_FALSE ;
static Eina_Bool do_discard = EINA_FALSE ;
static Eina_Slice line_delm_slice ;
static void
_connected ( void * data EINA_UNUSED , const Efl_Event * event )
{
fprintf ( stderr ,
" INFO: connected to '%s' (%s) \n "
" INFO: - local address=%s \n "
" INFO: - do read=%u \n "
" INFO: - do discard=%u \n " ,
efl_net_dialer_address_dial_get ( event - > object ) ,
efl_net_socket_address_remote_get ( event - > object ) ,
efl_net_socket_address_local_get ( event - > object ) ,
do_read , do_discard ) ;
}
static void
_eos ( void * data EINA_UNUSED , const Efl_Event * event )
{
fprintf ( stderr , " INFO: end of stream. \n " ) ;
/* on _error() we close it, then do not read as it has nothing */
if ( efl_io_closer_closed_get ( event - > object ) )
return ;
2016-12-19 08:46:37 -08:00
fprintf ( stderr ,
" -- BEGIN RECEIVED DATA -- \n "
EINA_SLICE_STR_FMT
" -- END RECEIVED DATA-- \n " ,
EINA_SLICE_STR_PRINT ( efl_io_buffered_stream_slice_get ( event - > object ) ) ) ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
}
static void
_can_read ( void * data EINA_UNUSED , const Efl_Event * event )
{
Eina_Bool can_read = efl_io_reader_can_read_get ( event - > object ) ;
/*
* Since we have more high level events , such as " slice,changed "
* and " line " , it ' s not that interesting to monitor
* " can_read,changed " anymore . We do and print out , but no actual
* reads as we print from _line ( ) or _eos ( ) .
*
* But reads can be done as usual , see the ' # if ' block below .
*/
fprintf ( stderr , " INFO: can read=%d \n " , can_read ) ;
#if 0
if ( ( can_read ) & & ( do_read ) )
{
char buf [ 4 ] ;
Eina_Rw_Slice rw_slice = EINA_SLICE_ARRAY ( buf ) ;
Eina_Error err ;
do
{
err = efl_io_reader_read ( event - > object , & rw_slice ) ;
if ( err )
{
if ( err = = EAGAIN )
{
fprintf ( stderr , " ERROR: read all available data \n " ) ;
break ;
}
fprintf ( stderr , " ERROR: could not read: %s \n " , eina_error_msg_get ( err ) ) ;
2018-01-09 15:43:09 -08:00
efl_loop_quit ( efl_loop_get ( event - > object ) , eina_value_int_init ( EXIT_FAILURE ) ) ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
return ;
}
fprintf ( stderr , " INFO: read ' " EINA_SLICE_STR_FMT " ' \n " , EINA_SLICE_STR_PRINT ( rw_slice ) ) ;
}
while ( err = = 0 ) ;
}
# endif
}
static void
_line ( void * data EINA_UNUSED , const Efl_Event * event )
{
const Eina_Slice slice = * ( const Eina_Slice * ) event - > info ;
if ( ! eina_slice_endswith ( slice , line_delm_slice ) )
{
fprintf ( stderr , " WARNING: received without line-delimiter ' "
EINA_SLICE_STR_FMT " ' \n " ,
EINA_SLICE_STR_PRINT ( slice ) ) ;
}
else
{
Eina_Slice s = slice ;
s . len - = line_delm_slice . len ;
fprintf ( stderr , " INFO: received ' " EINA_SLICE_STR_FMT " ' \n " ,
EINA_SLICE_STR_PRINT ( s ) ) ;
}
/*
* If you used the line and it ' s not interesting anymore , then you
* can discard it .
*
* It has the same effect as calling efl_io_reader_read ( ) as per
* # if block below
*/
if ( do_discard )
{
# if 1
efl_io_buffered_stream_discard ( event - > object , slice . len ) ;
# else
{
/* efl_io_buffered_stream_discard() paired with
* efl_io_buffered_stream_slice_get ( ) + ' slice , changed ' or
* ' line ' events is a faster alternative than reading ,
* since it doesn ' t copy the data .
*/
char * buf = malloc ( slice . len ) ;
Eina_Rw_Slice rw_slice = {
. mem = buf ,
. len = slice . len ,
} ;
Eina_Error err = efl_io_reader_read ( event - > object , & rw_slice ) ;
fprintf ( stderr , " INFO: read error=%s ' " EINA_SLICE_FMT " ' \n " , eina_error_msg_get ( err ) ? eina_error_msg_get ( err ) : " success " , EINA_SLICE_PRINT ( rw_slice ) ) ;
free ( buf ) ;
}
# endif
}
}
static void
_resolved ( void * data EINA_UNUSED , const Efl_Event * event )
{
fprintf ( stderr , " INFO: resolved %s => %s \n " ,
efl_net_dialer_address_dial_get ( event - > object ) ,
efl_net_socket_address_remote_get ( event - > object ) ) ;
}
static void
_error ( void * data EINA_UNUSED , const Efl_Event * event )
{
const Eina_Error * perr = event - > info ;
fprintf ( stderr , " INFO: error: %d '%s' \n " , * perr , eina_error_msg_get ( * perr ) ) ;
if ( ! efl_io_closer_closed_get ( event - > object ) )
efl_io_closer_close ( event - > object ) ;
2018-01-09 15:43:09 -08:00
efl_loop_quit ( efl_loop_get ( event - > object ) , eina_value_int_init ( EXIT_FAILURE ) ) ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
}
static void
2018-01-09 15:43:09 -08:00
_done_sending ( void * data EINA_UNUSED , const Efl_Event * event )
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
{
fprintf ( stderr , " INFO: done sending \n " ) ;
if ( ! do_read )
2018-01-09 15:43:09 -08:00
efl_loop_quit ( efl_loop_get ( event - > object ) , EINA_VALUE_EMPTY ) ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
}
static void
_done_receiving ( void * data EINA_UNUSED , const Efl_Event * event EINA_UNUSED )
{
fprintf ( stderr , " INFO: done receiving \n " ) ;
}
static void
2018-01-09 15:43:09 -08:00
_done ( void * data EINA_UNUSED , const Efl_Event * event )
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
{
fprintf ( stderr , " INFO: done sending and receiving \n " ) ;
2018-01-09 15:43:09 -08:00
efl_loop_quit ( efl_loop_get ( event - > object ) , EINA_VALUE_EMPTY ) ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
}
EFL_CALLBACKS_ARRAY_DEFINE ( dialer_cbs ,
{ EFL_NET_DIALER_EVENT_CONNECTED , _connected } , /* optional */
{ EFL_NET_DIALER_EVENT_RESOLVED , _resolved } , /* optional */
{ EFL_IO_READER_EVENT_CAN_READ_CHANGED , _can_read } , /* optional, can be used to read data, here just for monitoring */
{ EFL_IO_READER_EVENT_EOS , _eos } , /* recommended, notifies no more incoming data */
{ EFL_IO_BUFFERED_STREAM_EVENT_LINE , _line } , /* optional, could use 'slice,changed' or 'can_read,changed' instead */
{ EFL_IO_BUFFERED_STREAM_EVENT_ERROR , _error } , /* recommended */
{ EFL_IO_BUFFERED_STREAM_EVENT_WRITE_FINISHED , _done_sending } , /* optional */
{ EFL_IO_BUFFERED_STREAM_EVENT_READ_FINISHED , _done_receiving } , /* optional, same as 'eos' */
{ EFL_IO_BUFFERED_STREAM_EVENT_FINISHED , _done } ) ; /* recommended, notifies both send and receive are finished */
static char *
_unescape ( const char * str )
{
char * ret = strdup ( str ) ;
char * c , * w ;
Eina_Bool escaped = EINA_FALSE ;
for ( c = ret , w = ret ; * c ! = ' \0 ' ; c + + )
{
if ( escaped )
{
escaped = EINA_FALSE ;
switch ( * c )
{
case ' n ' : * w = ' \n ' ; break ;
case ' r ' : * w = ' \r ' ; break ;
case ' t ' : * w = ' \t ' ; break ;
default : w + + ; /* no change */
}
w + + ;
}
else
{
if ( * c = = ' \\ ' )
escaped = EINA_TRUE ;
else
w + + ;
}
}
* w = ' \0 ' ;
return ret ;
}
static const char * protocols [ ] = {
" tcp " ,
" udp " ,
" ssl " ,
# ifndef _WIN32
" unix " ,
# endif
NULL
} ;
static const Ecore_Getopt options = {
" efl_net_dialer_simple_example " , /* program name */
NULL , /* usage line */
" 1 " , /* version */
" (C) 2016 Enlightenment Project " , /* copyright */
" BSD 2-Clause " , /* license */
/* long description, may be multiline and contain \n */
" Example of Efl_Net_Dialer_Simple usage, sending a message and receiving a reply \n " ,
EINA_FALSE ,
{
ECORE_GETOPT_STORE_TRUE ( ' r ' , " read " , " Wait for data to be read. " ) ,
ECORE_GETOPT_STORE_TRUE ( ' D ' , " discard-lines " , " Lines that are read are discarded from final output. " ) ,
ECORE_GETOPT_APPEND ( ' s ' , " send " , " send the given string to the server once connected. " , ECORE_GETOPT_TYPE_STR ) ,
ECORE_GETOPT_STORE_STR ( ' d ' , " line-delimiter " ,
" If set will define a line delimiter for copy operation, instead of a fixed chunk size. This will trigger line events. " ) ,
ECORE_GETOPT_STORE_ULONG ( ' l ' , " buffer-limit " ,
" If set will limit buffer size to this limit of bytes. If used alongside with --line-delimiter and that delimiter was not found but bffer limit was reached, the line event will be triggered without the delimiter at the end. " ) ,
ECORE_GETOPT_STORE_ULONG ( ' c ' , " read-chunk-size " ,
" If set will change the base chunk size used while reading. " ) ,
ECORE_GETOPT_STORE_DOUBLE ( ' i ' , " inactivity-timeout " ,
" If greater than zero, specifies the number of seconds without any reads or writes that the dialer will be timed out. " ) ,
ECORE_GETOPT_STORE_DOUBLE ( ' t ' , " connect-timeout " ,
" If greater than zero, specifies the number of seconds without any reads or writes that the dialer will be timed out. " ) ,
ECORE_GETOPT_VERSION ( ' V ' , " version " ) ,
ECORE_GETOPT_COPYRIGHT ( ' C ' , " copyright " ) ,
ECORE_GETOPT_LICENSE ( ' L ' , " license " ) ,
ECORE_GETOPT_HELP ( ' h ' , " help " ) ,
ECORE_GETOPT_CHOICE_METAVAR ( 0 , NULL , " The dialer protocol. " , " protocol " ,
protocols ) ,
ECORE_GETOPT_STORE_METAVAR_STR ( 0 , NULL ,
" The address (URL) to dial " , " address " ) ,
ECORE_GETOPT_SENTINEL
}
} ;
2018-01-09 15:43:09 -08:00
static Eo * dialer = NULL ;
EAPI_MAIN void
efl_pause ( void * data EINA_UNUSED ,
const Efl_Event * ev EINA_UNUSED )
{
}
EAPI_MAIN void
efl_resume ( void * data EINA_UNUSED ,
const Efl_Event * ev EINA_UNUSED )
{
}
EAPI_MAIN void
efl_terminate ( void * data EINA_UNUSED ,
const Efl_Event * ev EINA_UNUSED )
{
/* FIXME: For the moment the main loop doesn't get
properly destroyed on shutdown which disallow
relying on parent destroying their children */
if ( dialer )
{
efl_del ( dialer ) ;
dialer = NULL ;
}
fprintf ( stderr , " INFO: main loop finished. \n " ) ;
}
EAPI_MAIN void
efl_main ( void * data EINA_UNUSED ,
const Efl_Event * ev )
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
{
const Efl_Class * cls ;
Eina_List * to_send = NULL ;
char * str ;
char * line_delimiter_str = NULL ;
char * address = NULL ;
char * protocol = NULL ;
unsigned long buffer_limit = 0 ;
unsigned long read_chunk_size = 0 ;
2016-12-11 07:19:46 -08:00
double timeout_inactivity = 0.0 ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
double connect_timeout = 0.0 ;
Eina_Bool quit_option = EINA_FALSE ;
Ecore_Getopt_Value values [ ] = {
ECORE_GETOPT_VALUE_BOOL ( do_read ) ,
ECORE_GETOPT_VALUE_BOOL ( do_discard ) ,
ECORE_GETOPT_VALUE_LIST ( to_send ) ,
ECORE_GETOPT_VALUE_STR ( line_delimiter_str ) ,
ECORE_GETOPT_VALUE_ULONG ( buffer_limit ) ,
ECORE_GETOPT_VALUE_ULONG ( read_chunk_size ) ,
2016-12-11 07:19:46 -08:00
ECORE_GETOPT_VALUE_DOUBLE ( timeout_inactivity ) ,
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
ECORE_GETOPT_VALUE_DOUBLE ( connect_timeout ) ,
/* standard block to provide version, copyright, license and help */
ECORE_GETOPT_VALUE_BOOL ( quit_option ) , /* -V/--version quits */
ECORE_GETOPT_VALUE_BOOL ( quit_option ) , /* -C/--copyright quits */
ECORE_GETOPT_VALUE_BOOL ( quit_option ) , /* -L/--license quits */
ECORE_GETOPT_VALUE_BOOL ( quit_option ) , /* -h/--help quits */
/* positional argument */
ECORE_GETOPT_VALUE_STR ( protocol ) ,
ECORE_GETOPT_VALUE_STR ( address ) ,
ECORE_GETOPT_VALUE_NONE /* sentinel */
} ;
int args ;
2018-01-09 15:43:09 -08:00
Eo * loop ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
Eina_Error err ;
2018-01-09 15:43:09 -08:00
args = ecore_getopt_parse ( & options , values , 0 , NULL ) ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
if ( args < 0 )
{
fputs ( " ERROR: Could not parse command line options. \n " , stderr ) ;
goto end ;
}
if ( quit_option ) goto end ;
2018-01-09 15:43:09 -08:00
loop = ev - > object ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
2018-01-09 15:43:09 -08:00
args = ecore_getopt_parse_positional ( & options , values , 0 , NULL , args ) ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
if ( args < 0 )
{
fputs ( " ERROR: Could not parse positional arguments. \n " , stderr ) ;
goto end ;
}
if ( ! protocol )
{
fputs ( " ERROR: missing protocol. \n " , stderr ) ;
goto end ;
}
if ( strcmp ( protocol , " tcp " ) = = 0 ) cls = EFL_NET_DIALER_TCP_CLASS ;
else if ( strcmp ( protocol , " udp " ) = = 0 ) cls = EFL_NET_DIALER_UDP_CLASS ;
else if ( strcmp ( protocol , " ssl " ) = = 0 ) cls = EFL_NET_DIALER_SSL_CLASS ;
implement efl_net_{socket,dialer,server}_windows
This is the local socket for windows, analogous to AF_UNIX.
`Efl_Net_Socket_Windows` is the base class doing `ReadFile()` and
`WriteFile()` using overlapped I/O, as well as the close procedure
(`FlushFileBuffers()`, `DisconnectNamedPipe()` and
`CloseHandle()`). These are done on top of an existing HANDLE that is
set by `Efl_Net_Dialer_Windows` (from `CreateFile()`) or
`Efl_Net_Server_Windows` (from `CreateNamedPipe()`).
The overlapped I/O will return immediately, either with operation
completed or `ERROR_IO_PENDING`, which means the kernel will execute
that asynchronously and will later `SetEvent(overlapped.hEvent)` which
is an event we wait on our main loop. That `overlapped` handle must
exist during the call lifetime, thus cannot be bound to `pd`, as we
may call `CancelIo()` but there is no guarantee the memory won't be
touched, in that case we keep the overlapped around, but without an
associated object.
Windows provides no notification "can read without blocking" or
non-blocking calls that returns partial data. The way to go is to use
these overlapped I/O, with an initial `ReadFile()` to an internal
buffer, once that operation finishes, we callback the user to says
there is something to read (`efl_io_reader_can_read_set()`) and wait
until `efl_io_reader_read()` is called to consume the available data,
then `ReadFile()` is called again to read more data to the same
internal buffer.
Likewise, there is no "can write without blocking" or non-blocking
calls that sends only partial data. The way to go is to get user bytes
in `efl_io_writer_write()` and copy them in an internal buffer, then
call `WriteFile()` on that and inform the user nothing else can be
written until that operation completes
(`efl_io_writer_can_write_set()`).
This is cumbersome since we say we "sent" stuff when we actually
didn't, it's still in our internal buffer (`pd->send.bytes`), but
nonetheless the kernel and the other peer may be adding even more
buffers, in this case we need to do a best effort to get it
delivery. A particular case is troublesome: `write() -> close()`, this
may result in `WriteFile()` pending, in this case we wait using
`GetOverlappedResult()`, *this is nasty and may block*, but it's the
only way I see to cope with such common use case.
Other operations, like ongoing `ReadFile()` or `ConnectNamedPipe()`
will be canceled using `CancelIo()`.
Q: Why no I/O Completion Port (IOCP) was used? Why no
CreateThreadpoolIo()? These perform much better!
A: These will call back from secondary threads, but in EFL we must
report back to the user in order to process incoming data or get
more data to send. That is, we serialize everything to the main
thread, making it impossible to use the benefits of IOCP and
similar such as CreateThreadpoolIo(). Since we'd need to wakeup the
main thread anyways, using `OVERLAPPED.hEvent` with
`ecore_main_win32_handler_add()` does the job as we expect.
Thanks to Vincent Torri (vtorri) for his help getting this code done
with an example on how to do the NamedPipe handling on Windows.
2017-03-22 00:29:16 -07:00
# ifdef EFL_NET_DIALER_UNIX_CLASS
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
else if ( strcmp ( protocol , " unix " ) = = 0 ) cls = EFL_NET_DIALER_UNIX_CLASS ;
implement efl_net_{socket,dialer,server}_windows
This is the local socket for windows, analogous to AF_UNIX.
`Efl_Net_Socket_Windows` is the base class doing `ReadFile()` and
`WriteFile()` using overlapped I/O, as well as the close procedure
(`FlushFileBuffers()`, `DisconnectNamedPipe()` and
`CloseHandle()`). These are done on top of an existing HANDLE that is
set by `Efl_Net_Dialer_Windows` (from `CreateFile()`) or
`Efl_Net_Server_Windows` (from `CreateNamedPipe()`).
The overlapped I/O will return immediately, either with operation
completed or `ERROR_IO_PENDING`, which means the kernel will execute
that asynchronously and will later `SetEvent(overlapped.hEvent)` which
is an event we wait on our main loop. That `overlapped` handle must
exist during the call lifetime, thus cannot be bound to `pd`, as we
may call `CancelIo()` but there is no guarantee the memory won't be
touched, in that case we keep the overlapped around, but without an
associated object.
Windows provides no notification "can read without blocking" or
non-blocking calls that returns partial data. The way to go is to use
these overlapped I/O, with an initial `ReadFile()` to an internal
buffer, once that operation finishes, we callback the user to says
there is something to read (`efl_io_reader_can_read_set()`) and wait
until `efl_io_reader_read()` is called to consume the available data,
then `ReadFile()` is called again to read more data to the same
internal buffer.
Likewise, there is no "can write without blocking" or non-blocking
calls that sends only partial data. The way to go is to get user bytes
in `efl_io_writer_write()` and copy them in an internal buffer, then
call `WriteFile()` on that and inform the user nothing else can be
written until that operation completes
(`efl_io_writer_can_write_set()`).
This is cumbersome since we say we "sent" stuff when we actually
didn't, it's still in our internal buffer (`pd->send.bytes`), but
nonetheless the kernel and the other peer may be adding even more
buffers, in this case we need to do a best effort to get it
delivery. A particular case is troublesome: `write() -> close()`, this
may result in `WriteFile()` pending, in this case we wait using
`GetOverlappedResult()`, *this is nasty and may block*, but it's the
only way I see to cope with such common use case.
Other operations, like ongoing `ReadFile()` or `ConnectNamedPipe()`
will be canceled using `CancelIo()`.
Q: Why no I/O Completion Port (IOCP) was used? Why no
CreateThreadpoolIo()? These perform much better!
A: These will call back from secondary threads, but in EFL we must
report back to the user in order to process incoming data or get
more data to send. That is, we serialize everything to the main
thread, making it impossible to use the benefits of IOCP and
similar such as CreateThreadpoolIo(). Since we'd need to wakeup the
main thread anyways, using `OVERLAPPED.hEvent` with
`ecore_main_win32_handler_add()` does the job as we expect.
Thanks to Vincent Torri (vtorri) for his help getting this code done
with an example on how to do the NamedPipe handling on Windows.
2017-03-22 00:29:16 -07:00
# endif
# ifdef EFL_NET_DIALER_WINDOWS_CLASS
else if ( strcmp ( protocol , " windows " ) = = 0 ) cls = EFL_NET_DIALER_WINDOWS_CLASS ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
# endif
else
{
fprintf ( stderr , " ERROR: unsupported protocol: %s \n " , protocol ) ;
goto end ;
}
/* A delimiter is optional, if empty or unset, buffered stream uses
* a copier that will execute writes based on read_chunk_size and
* only event " data " is emitted .
*
* If a line delimiter is set , it will hold writes until the
* delimiter is found , source reached End - of - Stream ( eos ) or the
* copier buffer limit is reached . The " line " event is emitted .
*/
line_delimiter_str = _unescape ( line_delimiter_str ? line_delimiter_str : " \\ r \\ n " ) ;
if ( line_delimiter_str )
line_delm_slice = ( Eina_Slice ) EINA_SLICE_STR ( line_delimiter_str ) ;
dialer = efl_add ( EFL_NET_DIALER_SIMPLE_CLASS , loop ,
efl_name_set ( efl_added , " dialer " ) ,
efl_net_dialer_simple_inner_class_set ( efl_added , cls ) , /* alternatively you could create the inner dialer and set with efl_io_buffered_stream_inner_io_set() */
2016-12-19 09:27:34 -08:00
efl_io_buffered_stream_line_delimiter_set ( efl_added , line_delm_slice ) , /* optional */
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
efl_io_buffered_stream_max_queue_size_input_set ( efl_added , buffer_limit ) , /* optional, defaults to unlimited */
efl_io_buffered_stream_max_queue_size_output_set ( efl_added , buffer_limit ) , /* optional, defaults to unlimited */
efl_io_buffered_stream_read_chunk_size_set ( efl_added , read_chunk_size ) , /* optional, defaults to 4096 */
2016-12-11 07:19:46 -08:00
efl_io_buffered_stream_timeout_inactivity_set ( efl_added , timeout_inactivity ) , /* optional, defaults to 0.0 (disabled) */
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
efl_net_dialer_timeout_dial_set ( efl_added , connect_timeout ) , /* optional, defaults to 0.0 (disabled) */
efl_event_callback_array_add ( efl_added , dialer_cbs ( ) , NULL ) ) ;
err = efl_net_dialer_dial ( dialer , address ) ;
if ( err ! = 0 )
{
fprintf ( stderr , " ERROR: could not dial %s '%s': %s " ,
protocol , address , eina_error_msg_get ( err ) ) ;
goto no_mainloop ;
}
/* unlike low-level I/O that wouldn't write data until it's
* connected , the simple dialer will queue it in memory , sending when
* it ' s ready . Thus just write & forget .
*/
if ( ! to_send )
{
if ( ! do_read )
{
Eina_Slice s = EINA_SLICE_STR_LITERAL ( " Hello World! " ) ;
fprintf ( stderr , " INFO: sending ' " EINA_SLICE_STR_FMT " ' \n " , EINA_SLICE_STR_PRINT ( s ) ) ;
efl_io_writer_write ( dialer , & s , NULL ) ;
if ( line_delm_slice . len )
{
2017-04-19 23:33:59 -07:00
Eina_Slice sw = line_delm_slice ;
efl_io_writer_write ( dialer , & sw , NULL ) ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
}
}
else
fprintf ( stderr , " INFO: nothing to send, just read... \n " ) ;
}
else
{
EINA_LIST_FREE ( to_send , str )
{
/* ignore empty sends, but add line delimiter, so we can do HTTP's last line :-) */
if ( str [ 0 ] ! = ' \0 ' )
{
Eina_Slice s = EINA_SLICE_STR ( str ) ;
fprintf ( stderr , " INFO: sending ' " EINA_SLICE_STR_FMT " ' \n " , EINA_SLICE_STR_PRINT ( s ) ) ;
efl_io_writer_write ( dialer , & s , NULL ) ;
}
free ( str ) ;
if ( line_delm_slice . len )
{
2017-04-19 23:33:59 -07:00
Eina_Slice sw = line_delm_slice ;
efl_io_writer_write ( dialer , & sw , NULL ) ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
}
}
}
efl_io_buffered_stream_eos_mark ( dialer ) ; /* we're done sending */
2018-01-09 15:43:09 -08:00
return ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
no_mainloop :
efl_del ( dialer ) ;
end :
EINA_LIST_FREE ( to_send , str ) free ( str ) ;
2018-01-09 15:43:09 -08:00
efl_loop_quit ( efl_loop_get ( ev - > object ) , eina_value_int_init ( EXIT_FAILURE ) ) ;
efl_net_{socket,dialer,server}_simple: easy to use, buffered network sockets.
The low level I/O primitives are powerful but adds some complexity to
use, for bi-directional streaming communication one ends creating two
Efl.Io.Queue and two Efl.Io.Copier to pipe data to socket when it can
operate.
Then encapsulate the socket using the new Efl.Io.Buffered_Stream, this
will allow the socket, be a dialer or a server client, to be operated
as a single handle that internally carries about the buffering for
you.
As one can see in the examples, compared to their "manual"
alternatives they are very easy to use, ressembling
Ecore_Con_Server/Ecore_Con_Client, but also offers line-based
delimiters and the possibility to let the socket to handle queueing
for you in case you received partial messages (just do not
read/clear/discard the received data).
2016-11-25 11:18:34 -08:00
}
2018-01-09 15:43:09 -08:00
EFL_MAIN_EX ( ) ;