comp.lang.ada
 help / color / mirror / Atom feed
* Question about Streams and UDP sockets using GNAT.Sockets
@ 2006-07-18  9:41 lekktu
  2006-07-18  9:55 ` Dmitry A. Kazakov
  2006-07-19 18:59 ` Simon Wright
  0 siblings, 2 replies; 16+ messages in thread
From: lekktu @ 2006-07-18  9:41 UTC (permalink / raw)


Hi.

I'm using GNAT GPL 2006 (20060522-34) on Windows XP.

I'm trying to broadcast an UDP packet, with the following test code:

----------------------------------------------------------------------
with GNAT.Sockets; use GNAT.Sockets;
procedure Test is
   Local_Port  : constant Port_Type := 20769;
   Remote_Port : constant Port_Type := 20770;
   Address    : Sock_Addr_Type;
   Socket     : Socket_Type;
   Channel    : Stream_Access;
   Local_Host : String := "127.0.0.1";
begin
   Initialize;
   Create_Socket (Socket, Family_Inet, Socket_Datagram);
   Set_Socket_Option (Socket, Socket_Level, (Broadcast, True));
   Address.Addr := Inet_Addr (Local_Host);
   Address.Port := Local_Port;
   Bind_Socket (Socket, Address);
   Address.Addr := Broadcast_Inet_Addr;
   Address.Port := Remote_Port;
   Channel := Stream (Socket, Address);
   String'Write (Channel, "TEST");         -- sends "T", "E", "S", "T".
   Free (Channel);
   Close_Socket (Socket);
   Finalize;
end Test;
----------------------------------------------------------------------

The trouble I'm having is not about sockets, but the streams vs.
sockets interaction. The above code does not send one UDP packet, but
four, one for each byte of the test message. What I'm doing wrong?

Thanks,

             Juanma




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-18  9:41 Question about Streams and UDP sockets using GNAT.Sockets lekktu
@ 2006-07-18  9:55 ` Dmitry A. Kazakov
  2006-07-18 10:25   ` Alex R. Mosteo
  2006-07-18 19:07   ` Jeffrey R. Carter
  2006-07-19 18:59 ` Simon Wright
  1 sibling, 2 replies; 16+ messages in thread
From: Dmitry A. Kazakov @ 2006-07-18  9:55 UTC (permalink / raw)


On 18 Jul 2006 02:41:06 -0700, lekktu@gmail.com wrote:

> I'm using GNAT GPL 2006 (20060522-34) on Windows XP.
> 
> I'm trying to broadcast an UDP packet, with the following test code:
> 
> ----------------------------------------------------------------------
> with GNAT.Sockets; use GNAT.Sockets;
> procedure Test is
>    Local_Port  : constant Port_Type := 20769;
>    Remote_Port : constant Port_Type := 20770;
>    Address    : Sock_Addr_Type;
>    Socket     : Socket_Type;
>    Channel    : Stream_Access;
>    Local_Host : String := "127.0.0.1";
> begin
>    Initialize;
>    Create_Socket (Socket, Family_Inet, Socket_Datagram);
>    Set_Socket_Option (Socket, Socket_Level, (Broadcast, True));
>    Address.Addr := Inet_Addr (Local_Host);
>    Address.Port := Local_Port;
>    Bind_Socket (Socket, Address);
>    Address.Addr := Broadcast_Inet_Addr;
>    Address.Port := Remote_Port;
>    Channel := Stream (Socket, Address);
>    String'Write (Channel, "TEST");         -- sends "T", "E", "S", "T".
>    Free (Channel);
>    Close_Socket (Socket);
>    Finalize;
> end Test;
> ----------------------------------------------------------------------
> 
> The trouble I'm having is not about sockets, but the streams vs.
> sockets interaction. The above code does not send one UDP packet, but
> four, one for each byte of the test message. What I'm doing wrong?

Don't use String'Write. GNAT implementation treats strings as array so
characters don't coalesce. It is unexpected, but legal. You might wish to
define your own type of the packet and implement a suitable write (and
read) for it.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-18  9:55 ` Dmitry A. Kazakov
@ 2006-07-18 10:25   ` Alex R. Mosteo
  2006-07-18 11:02     ` lekktu
  2006-07-18 20:58     ` Randy Brukardt
  2006-07-18 19:07   ` Jeffrey R. Carter
  1 sibling, 2 replies; 16+ messages in thread
From: Alex R. Mosteo @ 2006-07-18 10:25 UTC (permalink / raw)


Dmitry A. Kazakov wrote:

> On 18 Jul 2006 02:41:06 -0700, lekktu@gmail.com wrote:
> 
>> I'm using GNAT GPL 2006 (20060522-34) on Windows XP.
>> 
>> I'm trying to broadcast an UDP packet, with the following test code:
>> 
>> ----------------------------------------------------------------------
>> with GNAT.Sockets; use GNAT.Sockets;
>> procedure Test is
>>    Local_Port  : constant Port_Type := 20769;
>>    Remote_Port : constant Port_Type := 20770;
>>    Address    : Sock_Addr_Type;
>>    Socket     : Socket_Type;
>>    Channel    : Stream_Access;
>>    Local_Host : String := "127.0.0.1";
>> begin
>>    Initialize;
>>    Create_Socket (Socket, Family_Inet, Socket_Datagram);
>>    Set_Socket_Option (Socket, Socket_Level, (Broadcast, True));
>>    Address.Addr := Inet_Addr (Local_Host);
>>    Address.Port := Local_Port;
>>    Bind_Socket (Socket, Address);
>>    Address.Addr := Broadcast_Inet_Addr;
>>    Address.Port := Remote_Port;
>>    Channel := Stream (Socket, Address);
>>    String'Write (Channel, "TEST");         -- sends "T", "E", "S", "T".
>>    Free (Channel);
>>    Close_Socket (Socket);
>>    Finalize;
>> end Test;
>> ----------------------------------------------------------------------
>> 
>> The trouble I'm having is not about sockets, but the streams vs.
>> sockets interaction. The above code does not send one UDP packet, but
>> four, one for each byte of the test message. What I'm doing wrong?
> 
> Don't use String'Write. GNAT implementation treats strings as array so
> characters don't coalesce. It is unexpected, but legal. You might wish to
> define your own type of the packet and implement a suitable write (and
> read) for it.

I've workarounded this in past versions of GNAT using a direct call to the
Write procedure in Ada.Streams, though you must use a Stream_Element_Array
instead of a String. Doing so your array is transferred in a single call
and packet. (Contrarily to using the 'Write attribute of
Stream_Element_Array, that will also make a call for every element, which
is very CPU expensive for large arrays).



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-18 10:25   ` Alex R. Mosteo
@ 2006-07-18 11:02     ` lekktu
  2006-07-18 14:25       ` Alex R. Mosteo
  2006-07-18 20:52       ` Samuel Tardieu
  2006-07-18 20:58     ` Randy Brukardt
  1 sibling, 2 replies; 16+ messages in thread
From: lekktu @ 2006-07-18 11:02 UTC (permalink / raw)


Thanks, Dmitry and Alex.

> I've workarounded this in past versions of GNAT using a direct call to the
> Write procedure in Ada.Streams, though you must use a Stream_Element_Array
> instead of a String.

I've been able to make it work with:

----------------------------------------------------------------------

   use Ada.Streams;

   type Message is new String;

   procedure Message_Write
      (Stream : not null access Root_Stream_Type'Class;
       Item   : in              Message);

   for Message'Write use Message_Write;

   procedure Message_Write
      (Stream : not null access Root_Stream_Type'Class;
       Item   : in              Message)
   is
      subtype Message_Array is Stream_Element_Array (1 .. Item'Length);
      function To_SEA is new
         Ada.Unchecked_Conversion (Message, Message_Array);
   begin
      Write (Stream.all, To_SEA (Item));
   end Message_Write;

----------------------------------------------------------------------

Is there any simpler (or more correct) way, or that's about it?

Thanks,
              Juanma




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-18 11:02     ` lekktu
@ 2006-07-18 14:25       ` Alex R. Mosteo
  2006-07-18 14:58         ` lekktu
  2006-07-18 20:52       ` Samuel Tardieu
  1 sibling, 1 reply; 16+ messages in thread
From: Alex R. Mosteo @ 2006-07-18 14:25 UTC (permalink / raw)


lekktu@gmail.com wrote:

(snip)
> 
> Is there any simpler (or more correct) way, or that's about it?

That's the simplest solution I had in mind.



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-18 14:25       ` Alex R. Mosteo
@ 2006-07-18 14:58         ` lekktu
  0 siblings, 0 replies; 16+ messages in thread
From: lekktu @ 2006-07-18 14:58 UTC (permalink / raw)


Alex R. Mosteo wrote:

> That's the simplest solution I had in mind.

OK, thanks.




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-18  9:55 ` Dmitry A. Kazakov
  2006-07-18 10:25   ` Alex R. Mosteo
@ 2006-07-18 19:07   ` Jeffrey R. Carter
  2006-07-18 20:13     ` Dmitry A. Kazakov
  1 sibling, 1 reply; 16+ messages in thread
From: Jeffrey R. Carter @ 2006-07-18 19:07 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> 
> Don't use String'Write. GNAT implementation treats strings as array so
> characters don't coalesce. It is unexpected, but legal. You might wish to
> define your own type of the packet and implement a suitable write (and
> read) for it.

Since the language defines String as an array type, it would be strange 
to expect anything else.

-- 
Jeff Carter
"I'm a lumberjack and I'm OK."
Monty Python's Flying Circus
54



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-18 19:07   ` Jeffrey R. Carter
@ 2006-07-18 20:13     ` Dmitry A. Kazakov
  2006-07-19  0:32       ` Jeffrey R. Carter
  0 siblings, 1 reply; 16+ messages in thread
From: Dmitry A. Kazakov @ 2006-07-18 20:13 UTC (permalink / raw)


On Tue, 18 Jul 2006 19:07:24 GMT, Jeffrey R. Carter wrote:

> Dmitry A. Kazakov wrote:
>> 
>> Don't use String'Write. GNAT implementation treats strings as array so
>> characters don't coalesce. It is unexpected, but legal. You might wish to
>> define your own type of the packet and implement a suitable write (and
>> read) for it.
> 
> Since the language defines String as an array type, it would be strange 
> to expect anything else.

Yet, the language does not prescribe that each element of that array should
be written using an individual call to stream's Write. Compare it with an
implementation of assignment. The language does not require the compiler to
copy each string character individually. It is free to use memcpy.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-18 11:02     ` lekktu
  2006-07-18 14:25       ` Alex R. Mosteo
@ 2006-07-18 20:52       ` Samuel Tardieu
  1 sibling, 0 replies; 16+ messages in thread
From: Samuel Tardieu @ 2006-07-18 20:52 UTC (permalink / raw)


>>>>> "Juanma" == lekktu@gmail com <lekktu@gmail.com> writes:

Juanma> Is there any simpler (or more correct) way, or that's about
Juanma> it?

You can also make your own stream type with overloaded Write and Read
procedures and a Send function that will send the data onto the
socket. It will work with anything you want to write into your UDP
packet, not only strings. The advantage of this is that if you use
GLADE encoding library instead of GNAT one, you get a portable UDP
packet format even accross different architecture.

Here is an excerpt of some code I wrote to do this very thing. Then
Sock'Write (Stream.Content (1 .. Stream.Last)) was used.

with Ada.Streams; use Ada.Streams;

private package Shix.Streams is

   --  Gather stream oriented data into one element to be able to
   --  transmit atomic packets.

   Max_Transfer_Size : constant := 1000;

   type Atomic_Stream_Type is new Root_Stream_Type with record
      Content : Stream_Element_Array (1 .. Max_Transfer_Size);
      Next    : Stream_Element_Offset := 1;
      Last    : Stream_Element_Offset := 0;
   end record;

   procedure Read
     (Stream : in out Atomic_Stream_Type;
      Item   : out Stream_Element_Array;
      Last   : out Stream_Element_Offset);

   procedure Write
     (Stream : in out Atomic_Stream_Type;
      Item   : in Stream_Element_Array);

end Shix.Streams;

package body Shix.Streams is

   ----------
   -- Read --
   ----------

   procedure Read
     (Stream : in out Atomic_Stream_Type;
      Item   : out Stream_Element_Array;
      Last   : out Stream_Element_Offset)
   is
      Count : constant Stream_Element_Count :=
        Stream_Element_Count'Min (Item'Length, Stream.Last - Stream.Next + 1);
   begin
      Last := Item'First + Count - 1;
      Item (Item'First .. Last) :=
        Stream.Content (Stream.Next .. Stream.Next + Count - 1);
      Stream.Next := Stream.Next + Count;
   end Read;

   -----------
   -- Write --
   -----------

   procedure Write
     (Stream : in out Atomic_Stream_Type;
      Item   : in Stream_Element_Array)
   is
   begin
      pragma Assert (Stream.Last + Item'Length <= Stream.Content'Last);
      Stream.Content (Stream.Last + 1 .. Stream.Last + Item'Length) := Item;
      Stream.Last := Stream.Last + Item'Length;
   end Write;

end Shix.Streams;



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-18 10:25   ` Alex R. Mosteo
  2006-07-18 11:02     ` lekktu
@ 2006-07-18 20:58     ` Randy Brukardt
  2006-07-19 10:36       ` Alex R. Mosteo
  2006-07-19 18:50       ` Simon Wright
  1 sibling, 2 replies; 16+ messages in thread
From: Randy Brukardt @ 2006-07-18 20:58 UTC (permalink / raw)


"Alex R. Mosteo" <devnull@mailinator.com> wrote in message
news:4i3r3nF215adU1@individual.net...
...
> I've workarounded this in past versions of GNAT using a direct call to the
> Write procedure in Ada.Streams, though you must use a Stream_Element_Array
> instead of a String. Doing so your array is transferred in a single call
> and packet. (Contrarily to using the 'Write attribute of
> Stream_Element_Array, that will also make a call for every element, which
> is very CPU expensive for large arrays).

If the type you are sending is more complex than a simple array, you
probably should try to use 'Write (Unchecked_Conversion won't necessarily
work on complex types). The best way to do it is to create a buffer stream
type that simply puts the bytes in a Stream_Element_Array. Then use the
direct call to the Write procedure of the socket stream.

We include such a type in Claw for this very reason. (Putting things into
the Clipboard or Registry must be done with a single write, so we have to
get all of the bytes together first.)

                           Randy.

                             Randy





^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-18 20:13     ` Dmitry A. Kazakov
@ 2006-07-19  0:32       ` Jeffrey R. Carter
  2006-07-19  8:12         ` Dmitry A. Kazakov
  0 siblings, 1 reply; 16+ messages in thread
From: Jeffrey R. Carter @ 2006-07-19  0:32 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> 
> Yet, the language does not prescribe that each element of that array should
> be written using an individual call to stream's Write. Compare it with an
> implementation of assignment. The language does not require the compiler to
> copy each string character individually. It is free to use memcpy.

ARM 13.13.2: "For composite types, the Write or Read attribute for each 
component is called"

So the language does prescribe this for arrays.

-- 
Jeff Carter
"I'm a lumberjack and I'm OK."
Monty Python's Flying Circus
54



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-19  0:32       ` Jeffrey R. Carter
@ 2006-07-19  8:12         ` Dmitry A. Kazakov
  0 siblings, 0 replies; 16+ messages in thread
From: Dmitry A. Kazakov @ 2006-07-19  8:12 UTC (permalink / raw)


On Wed, 19 Jul 2006 00:32:16 GMT, Jeffrey R. Carter wrote:

> Dmitry A. Kazakov wrote:
>> 
>> Yet, the language does not prescribe that each element of that array should
>> be written using an individual call to stream's Write. Compare it with an
>> implementation of assignment. The language does not require the compiler to
>> copy each string character individually. It is free to use memcpy.
> 
> ARM 13.13.2: "For composite types, the Write or Read attribute for each 
> component is called"
> 
> So the language does prescribe this for arrays.

I believe it is relaxed now:

"{AI95-00195-01} Explicitly provided a permission that the number of calls
to the underlying stream Read and Write
operations may differ from the number determined by the canonical
operations. If Ada 95 code somehow depended on
the number of calls to Read or Write, it could fail with an Ada 2005
implementation. Such code is likely to be very
rare; moreover, such code is really wrong, as the permission applies to Ada
95 as well (as it was a Binding
Interpretation)."

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-18 20:58     ` Randy Brukardt
@ 2006-07-19 10:36       ` Alex R. Mosteo
  2006-07-19 18:50       ` Simon Wright
  1 sibling, 0 replies; 16+ messages in thread
From: Alex R. Mosteo @ 2006-07-19 10:36 UTC (permalink / raw)


Randy Brukardt wrote:

> "Alex R. Mosteo" <devnull@mailinator.com> wrote in message
> news:4i3r3nF215adU1@individual.net...
> ...
>> I've workarounded this in past versions of GNAT using a direct call to
>> the Write procedure in Ada.Streams, though you must use a
>> Stream_Element_Array instead of a String. Doing so your array is
>> transferred in a single call and packet. (Contrarily to using the 'Write
>> attribute of Stream_Element_Array, that will also make a call for every
>> element, which is very CPU expensive for large arrays).
> 
> If the type you are sending is more complex than a simple array, you
> probably should try to use 'Write (Unchecked_Conversion won't necessarily
> work on complex types). The best way to do it is to create a buffer stream
> type that simply puts the bytes in a Stream_Element_Array. Then use the
> direct call to the Write procedure of the socket stream.

I also have implemented a stream type to do this. Take a look to the child
packages under Agpl.Streams:

https://svn.mosteo.com/public/agpl/

and particularly to

https://svn.mosteo.com/public/agpl/agpl-streams-circular.ads
https://svn.mosteo.com/public/agpl/agpl-streams-circular_unbounded.ads

These may involve some extra copying around, but you can adapt them to avoid
it.



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-18 20:58     ` Randy Brukardt
  2006-07-19 10:36       ` Alex R. Mosteo
@ 2006-07-19 18:50       ` Simon Wright
  1 sibling, 0 replies; 16+ messages in thread
From: Simon Wright @ 2006-07-19 18:50 UTC (permalink / raw)


"Randy Brukardt" <randy@rrsoftware.com> writes:

> If the type you are sending is more complex than a simple array, you
> probably should try to use 'Write (Unchecked_Conversion won't
> necessarily work on complex types). The best way to do it is to
> create a buffer stream type that simply puts the bytes in a
> Stream_Element_Array. Then use the direct call to the Write
> procedure of the socket stream.
>
> We include such a type in Claw for this very reason.

There is also one in the Booch Components
(http://booch95.sourceforge.net/) at BC.Support.Memory_Streams.




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-18  9:41 Question about Streams and UDP sockets using GNAT.Sockets lekktu
  2006-07-18  9:55 ` Dmitry A. Kazakov
@ 2006-07-19 18:59 ` Simon Wright
  2006-07-19 20:54   ` lekktu
  1 sibling, 1 reply; 16+ messages in thread
From: Simon Wright @ 2006-07-19 18:59 UTC (permalink / raw)


"lekktu@gmail.com" <lekktu@gmail.com> writes:

> I'm trying to broadcast an UDP packet, with the following test code:

> The trouble I'm having is not about sockets, but the streams vs.
> sockets interaction. The above code does not send one UDP packet,
> but four, one for each byte of the test message. What I'm doing
> wrong?

If you tried to read the data you would probably not have noticed
anything wrong; because GNAT (GNAT.Sockets, anyway) is rather naive
about UDP sockets. It would have happily done 4 reads, one for each
byte of the test message.

The only way I can see to get UDP working is

(1) stream the record to be output to a memory stream,then write the
    bytes of the stream to the socket stream in one go;

(2) read a datagram into a maximally-sized Stream Element Array (1600
    bytes or so?), populate a memory stream with the bytes actually
    read, read the record to be input from that.

Personally I think that letting us get a Stream for a GNAT.Sockets
datagram socket is almost bound to result in error and shouldn't be
allowed, I couldn't persuade AdaCore of that!



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Question about Streams and UDP sockets using GNAT.Sockets
  2006-07-19 18:59 ` Simon Wright
@ 2006-07-19 20:54   ` lekktu
  0 siblings, 0 replies; 16+ messages in thread
From: lekktu @ 2006-07-19 20:54 UTC (permalink / raw)


Simon Wright wrote:

> The only way I can see to get UDP working is
>
> (1) stream the record to be output to a memory stream,then write the
>     bytes of the stream to the socket stream in one go;

This has been suggested, but it is overkill for my needs, as the UDP
sockets are going to exchange only very simple String (or maybe
Wide_String) data.

> Personally I think that letting us get a Stream for a GNAT.Sockets
> datagram socket is almost bound to result in error and shouldn't be
> allowed, I couldn't persuade AdaCore of that!

Yes, I agree. I've already gotten rid of the Stream stuff and I'm using
Send_Socket and Receive_Socket directly. I'll use the streams for the
TCP/IP connections, though.

Thanks,
            Juanma




^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2006-07-19 20:54 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-07-18  9:41 Question about Streams and UDP sockets using GNAT.Sockets lekktu
2006-07-18  9:55 ` Dmitry A. Kazakov
2006-07-18 10:25   ` Alex R. Mosteo
2006-07-18 11:02     ` lekktu
2006-07-18 14:25       ` Alex R. Mosteo
2006-07-18 14:58         ` lekktu
2006-07-18 20:52       ` Samuel Tardieu
2006-07-18 20:58     ` Randy Brukardt
2006-07-19 10:36       ` Alex R. Mosteo
2006-07-19 18:50       ` Simon Wright
2006-07-18 19:07   ` Jeffrey R. Carter
2006-07-18 20:13     ` Dmitry A. Kazakov
2006-07-19  0:32       ` Jeffrey R. Carter
2006-07-19  8:12         ` Dmitry A. Kazakov
2006-07-19 18:59 ` Simon Wright
2006-07-19 20:54   ` lekktu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox