comp.lang.ada
 help / color / mirror / Atom feed
* Sockets - newbie at a brick wall
@ 2002-12-11 18:40 Benjamin Place
  2002-12-12  9:58 ` John McCabe
  2002-12-13 11:31 ` Simon Wright
  0 siblings, 2 replies; 11+ messages in thread
From: Benjamin Place @ 2002-12-11 18:40 UTC (permalink / raw)


I'm looking for some help writing my first sockets (GNAT.Sockets) app
- there must be something basic that I'm missing.

I start my app My_Server (my_server.adb, below) on newton, then start
My_Client (my_client.adb, below) on einstein. Einstein immediately
reports an error, 

    ben@einstein ben 01:12pm $ ./my_client 

    raised GNAT.SOCKETS.SOCKET_ERROR : [56] Socket is 
    already connected

So what am I doing wrong. Which socket is already connected, and what
should I do differently?

I'm using GNATMAKE 5.00w (20010924) (gcc 3.1) on MacOS 10.1.5.

Thanks for any help,
Ben



--  my_server.adb
with GNAT.Sockets;   use GNAT.Sockets;
with Ada.Text_IO;    use Ada.Text_IO;

procedure My_Server is
   Socket     : Socket_Type;
   Address    : Sock_Addr_Type;
   Channel    : Stream_Access;
   Loop_Count : Natural := 0;
begin

   --  Create an endpoint for communication. Raise Socket_Error on error.
   Create_Socket (Socket, Family_Inet, Socket_Datagram);

   --  Initialize sockaddr_in structure with server (local) socket name
   Address.Addr := Addresses (Get_Host_By_Name ("newton.cannella.org"), 1);
   Address.Port := 4134;

   --  Server should bind the address to the socket
   Bind_Socket (Socket, Address);

   Channel := Stream (Socket, Address);
   
   declare
      Message : String := String'Input (Channel);
   begin
      Address := Get_Address (Channel);
      Put_Line (Message & " from " & Image (Address));
   end;
   
   Shutdown_Socket (Socket);		--  Disconnect socket
   Close_Socket (Socket);		--  Close socket

end My_Server;



--  my_client.adb
with GNAT.Sockets;   use GNAT.Sockets;
with Ada.Text_IO;    use Ada.Text_IO;

procedure My_Client is
   Socket   : Socket_Type;
   Server   : Sock_Addr_Type;
   Channel  : Stream_Access;
begin

   --  Create a client-side socket. Raise Socket_Error on error.
   Create_Socket (Socket, Family_Inet, Socket_Datagram);

   --  Initialize sockaddr_in structure with server
   --  (remote) socket name
   Server := 
     (
      Addr => Addresses (Get_Host_By_Name ("newton.cannella.org"), 1),
      Port => 4134,
      Family => Family_Inet
     );

   Connect_Socket (Socket, Server);
   --  Make a connection to another socket which has the address of
   --  Server. Raise Socket_Error on error.

   Channel := Stream (Socket, Server);

   String'Output (Channel, "Hello, world!");

   Close_Socket (Socket);

end My_Client;



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

* Re: Sockets - newbie at a brick wall
  2002-12-11 18:40 Sockets - newbie at a brick wall Benjamin Place
@ 2002-12-12  9:58 ` John McCabe
  2002-12-12 14:19   ` John McCabe
  2002-12-12 16:49   ` Benjamin Place
  2002-12-13 11:31 ` Simon Wright
  1 sibling, 2 replies; 11+ messages in thread
From: John McCabe @ 2002-12-12  9:58 UTC (permalink / raw)


On 11 Dec 2002 10:40:00 -0800, benjamin_place@hotmail.com (Benjamin
Place) wrote:

>So what am I doing wrong. Which socket is already connected, and what
>should I do differently?

To be honest, I haven't used GNAT.Sockets, but two things strike me as
odd:

1) Your use of Socket_Datagram with a Connect() call. Perhaps you
should try Socket_Stream sockets.

2) Should there be a call to Listen() on the server side or is that
included in one of the other calls?

I realise this probably isn't as useful as someone who has worked with
GNAT.Sockets, but hopefully it will be a pointer to something that
would help until someone more knowledgeable provides a solution.



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

* Re: Sockets - newbie at a brick wall
  2002-12-12  9:58 ` John McCabe
@ 2002-12-12 14:19   ` John McCabe
  2002-12-12 16:23     ` Toshitaka Kumano
  2002-12-12 16:49   ` Benjamin Place
  1 sibling, 1 reply; 11+ messages in thread
From: John McCabe @ 2002-12-12 14:19 UTC (permalink / raw)


On Thu, 12 Dec 2002 09:58:25 GMT, john@assen.nospam.demon.co.uk (John
McCabe) wrote:

>On 11 Dec 2002 10:40:00 -0800, benjamin_place@hotmail.com (Benjamin
>Place) wrote:
>
>>So what am I doing wrong. Which socket is already connected, and what
>>should I do differently?
>
>To be honest, I haven't used GNAT.Sockets, but two things strike me as
>odd:
>
>1) Your use of Socket_Datagram with a Connect() call. Perhaps you
>should try Socket_Stream sockets.

Datagrams are 'connectionless' sockets so you wouldn't use the
Connect() call at all.

>2) Should there be a call to Listen() on the server side or is that
>included in one of the other calls?

Listen()/Accept() is only applicable to Stream sockets.

I've now had the chance to look at GNAT.Sockets. First thing is that I
would try starting with a simplified version of the stream sockets
example in g-socket.ads. However, if you want to use datagrams, you
could use the datagram part of the example but ditch the multicast
stuff for the moment.

For what it's worth, I've modified your files based on the example in
g-socket.ads. See below. Hope this helps.

--  my_client.adb
with GNAT.Sockets;   use GNAT.Sockets;

procedure My_Client is
   Socket   : Socket_Type;
   Address  : Sock_Addr_Type;
   Channel  : Stream_Access;
begin
   -- Initialise the socket bindings.
   Initialize (Process_Blocking_IO => False);

   Create_Socket (Socket, Family_Inet, Socket_Datagram);

   Set_Socket_Option
      (Socket,
       Socket_Level,
       (Reuse_Address, True));

   Address.Addr :=
      Addresses (Get_Host_By_Name ("newton.cannella.org"), 1);
   Address.Port := 4134;

   Bind_Socket (Socket, Address);

   Channel := Stream (Socket, Address);

   --  Send message to server.
   String'Output (Channel, "Hello world");

   Close_Socket (Socket);
end My_Client;


--  my_server.adb
with GNAT.Sockets;   use GNAT.Sockets;
with Ada.Text_IO;    use Ada.Text_IO;

procedure My_Server is
   Socket     : Socket_Type;
   Address    : Sock_Addr_Type;
   Channel    : Stream_Access;
begin
   -- Initialise the socket bindings.
   Initialize (Process_Blocking_IO => False);

   Create_Socket (Socket, Family_Inet, Socket_Datagram);

   --  Allow reuse of local addresses.
   Set_Socket_Option
      (Socket,
       Socket_Level,
       (Reuse_Address, True));

   --  If this socket is intended to receive messages, bind it to a
   --  given socket address.
   Address.Addr := Any_Inet_Addr;
   Address.Port := 4134;

   Bind_Socket (Socket, Address);

   Channel := Stream (Socket, Address);

   --  Receive and print message from client.
   declare
      Message : String := String'Input (Channel);
   begin
      --  Get the address of the sender.
      Address := Get_Address (Channel);
      Ada.Text_IO.Put_Line (Message & " from " & Image (Address));
   end;

   Close_Socket (Socket);
end My_Server;




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

* Re: Sockets - newbie at a brick wall
  2002-12-12 14:19   ` John McCabe
@ 2002-12-12 16:23     ` Toshitaka Kumano
  2002-12-12 17:27       ` John McCabe
  2002-12-13 11:25       ` John McCabe
  0 siblings, 2 replies; 11+ messages in thread
From: Toshitaka Kumano @ 2002-12-12 16:23 UTC (permalink / raw)


Sorry for somewhat off topic from GNAT.Sockets,

John McCabe wrote:
> >1) Your use of Socket_Datagram with a Connect() call. Perhaps you
> >should try Socket_Stream sockets.
> 
> Datagrams are 'connectionless' sockets so you wouldn't use the
> Connect() call at all.

This statement seems restrictve.

You can do "connect()" for Datagram socket and get some merits(*).

*) Detect ICMP errors from the remote peer, better performance
   in some protocol stacks and wider avaiability of system calls
   other than recvfrom/sendto.... 

-- 
Toshitaka Kumano
Kamakura, Japan



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

* Re: Sockets - newbie at a brick wall
  2002-12-12  9:58 ` John McCabe
  2002-12-12 14:19   ` John McCabe
@ 2002-12-12 16:49   ` Benjamin Place
  1 sibling, 0 replies; 11+ messages in thread
From: Benjamin Place @ 2002-12-12 16:49 UTC (permalink / raw)


john@assen.nospam.demon.co.uk (John McCabe) wrote in message news:<3df85d10.3167644@news.demon.co.uk>...
> 1) Your use of Socket_Datagram with a Connect() call. Perhaps you
> should try Socket_Stream sockets.

I *knew* I was missing something basic - the fact that "connected
datagrams" is a contradiction didn't even occur to me, though it
should have. Thanks for the critique.

Attached is corrected code for my_client.adb. This version works, and
both client and server exit normally.

--  my_client.adb
with GNAT.Sockets;   use GNAT.Sockets;
with Ada.Text_IO;    use Ada.Text_IO;


procedure My_Client is
   Socket   : Socket_Type;
   Server   : Sock_Addr_Type;
   Channel  : Stream_Access;
begin

   --  Create a client-side socket. Raise Socket_Error on error.
   Create_Socket (Socket, Family_Inet, Socket_Datagram);

   --  Initialize sockaddr_in structure with 
   --  server (remote) socket name
   Server := 
     (
      Addr => Addresses (Get_Host_By_Name ("newton.cannella.org"), 1),
      Port => 4134,
      Family => Family_Inet
     );

   Channel := Stream (Socket, Server);

   String'Output (Channel, "Hello, world!");

   Close_Socket (Socket);

end My_Client;



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

* Re: Sockets - newbie at a brick wall
  2002-12-12 16:23     ` Toshitaka Kumano
@ 2002-12-12 17:27       ` John McCabe
  2002-12-13 11:25       ` John McCabe
  1 sibling, 0 replies; 11+ messages in thread
From: John McCabe @ 2002-12-12 17:27 UTC (permalink / raw)


On Fri, 13 Dec 2002 01:23:06 +0900, Toshitaka Kumano
<kumano@cl.cilas.net> wrote:

>Sorry for somewhat off topic from GNAT.Sockets,

Not necessarily :-)

>John McCabe wrote:
>> >1) Your use of Socket_Datagram with a Connect() call. Perhaps you
>> >should try Socket_Stream sockets.
>> 
>> Datagrams are 'connectionless' sockets so you wouldn't use the
>> Connect() call at all.
>
>This statement seems restrictve.
>
>You can do "connect()" for Datagram socket and get some merits(*).
>
>*) Detect ICMP errors from the remote peer, better performance
>   in some protocol stacks and wider avaiability of system calls
>   other than recvfrom/sendto.... 

If you can find the time and equipment, could you produce a version of
the my_client.adb that does this please? It would be interesting to
see as I've only ever used connect() on stream sockets.

Best Regards
John McCabe

To reply by email replace 'nospam' with 'assen'



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

* Re: Sockets - newbie at a brick wall
  2002-12-12 16:23     ` Toshitaka Kumano
  2002-12-12 17:27       ` John McCabe
@ 2002-12-13 11:25       ` John McCabe
  1 sibling, 0 replies; 11+ messages in thread
From: John McCabe @ 2002-12-13 11:25 UTC (permalink / raw)


On Fri, 13 Dec 2002 01:23:06 +0900, Toshitaka Kumano
<kumano@cl.cilas.net> wrote:

>Sorry for somewhat off topic from GNAT.Sockets,
>
>John McCabe wrote:
>> >1) Your use of Socket_Datagram with a Connect() call. Perhaps you
>> >should try Socket_Stream sockets.
>> 
>> Datagrams are 'connectionless' sockets so you wouldn't use the
>> Connect() call at all.
>
>This statement seems restrictve.
>
>You can do "connect()" for Datagram socket and get some merits(*).

Now that I've had a look at Unix Network Programming by W Richard
Stevens, I see you are entirely correct. There's an interesting table
in there on page 225 of the Second Edition. It says:

It says that if you use sendto() that specifies a destination on a
connected datagram socket, the error EISCONN (i.e. socket is already
connected) will be raised. This is exactly what was happening with
Ben's original example.

Looking at the source of g-socket.adb, you can see that the write
operation for a datagram socket contains the following...

===========
procedure Write
     (Stream : in out Datagram_Socket_Stream_Type;
      Item   : Ada.Streams.Stream_Element_Array)
is
   :
   :
begin
   loop
      Send_Socket
        (Stream.Socket,
         Item (First .. Max),
         Index,
         Stream.To);
   :
   :
   blah, blah.

Here it is using the Send_Socket operation that requires a destination
parameter (in this case Stream.To). That version of Send_Socket()
calls C_Sendto, which is a pragma import of the C library's sendto()
function.

So the behaviour you got (Ben) is what is programmed into
GNAT.Sockets. I wouldn't necessarily call this a bug, but it is
certainly an oversight for GNAT.Sockets not to adjust what call is
made on a datagram socket depending on whether it is connected or not.
There are various ways of fixing this, but it all depends on how you
determine whether a socket is connected or not. In the simplest
scenario you would change part of Send_Socket from:

Res := C_Sendto
  (C.int (Socket),
   Item (Item'First)'Address,
   Item'Length, 0,
   Sin'Unchecked_Access,
   Len);

to:

if Is_Connected (Socket) then
  (C.int (Socket),
   Item (Item'First)'Address,
   Item'Length, 0,
   Null,
   0);
else
  (C.int (Socket),
   Item (Item'First)'Address,
   Item'Length, 0,
   Sin'Unchecked_Access,
   Len);
end if;

If you're a supported user, I would certainly send in a bug report.

Hope this helps.


Best Regards
John McCabe

To reply by email replace 'nospam' with 'assen'



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

* Re: Sockets - newbie at a brick wall
  2002-12-11 18:40 Sockets - newbie at a brick wall Benjamin Place
  2002-12-12  9:58 ` John McCabe
@ 2002-12-13 11:31 ` Simon Wright
  2002-12-13 13:52   ` John McCabe
  2002-12-13 22:38   ` Benjamin Place
  1 sibling, 2 replies; 11+ messages in thread
From: Simon Wright @ 2002-12-13 11:31 UTC (permalink / raw)


You don't show any calls to GNAT.Sockets.Initialize. We found that
leaving out this call made no (apparent) difference on Linux, but that
it was necessary on Windows. No experience with MacOS!

-- 
Simon Wright                         Email: simon.j.wright@amsjv.com
AMS                                        Voice: +44(0)23 9270 1778
Integrated Systems Division                  FAX: +44(0)23 9270 1500



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

* Re: Sockets - newbie at a brick wall
  2002-12-13 11:31 ` Simon Wright
@ 2002-12-13 13:52   ` John McCabe
  2002-12-13 22:38   ` Benjamin Place
  1 sibling, 0 replies; 11+ messages in thread
From: John McCabe @ 2002-12-13 13:52 UTC (permalink / raw)


On 13 Dec 2002 11:31:49 +0000, Simon Wright <simon.j.wright@amsjv.com>
wrote:

>You don't show any calls to GNAT.Sockets.Initialize. We found that
>leaving out this call made no (apparent) difference on Linux, but that
>it was necessary on Windows. No experience with MacOS!

My updates to Ben's original shows these, although I'm not 100% sure
that I've set the Process_Blocking_IO parameter correctly. Actually,
on Windows, Initialize in GNAT.Sockets.Thin doesn't use the
Process_Blocking_IO parameter, all it really does is call WSAStartup. 

Still, it really should be there anyway, as should Finalize really
(I've missed that one out!).


Best Regards
John McCabe

To reply by email replace 'nospam' with 'assen'



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

* Re: Sockets - newbie at a brick wall
  2002-12-13 11:31 ` Simon Wright
  2002-12-13 13:52   ` John McCabe
@ 2002-12-13 22:38   ` Benjamin Place
  2002-12-16 10:16     ` John McCabe
  1 sibling, 1 reply; 11+ messages in thread
From: Benjamin Place @ 2002-12-13 22:38 UTC (permalink / raw)


Simon Wright <simon.j.wright@amsjv.com> wrote in message news:<x7vsmx2m9y2.fsf@galadriel.frlngtn.gecm.com>...
> You don't show any calls to GNAT.Sockets.Initialize. We found that
> leaving out this call made no (apparent) difference on Linux, but that
> it was necessary on Windows. No experience with MacOS!

My code (version 2) works fine on OS X (or so it seems), but you're
right, it fails on Windows.

The following code (version 3) works on Windows. The default parameter
on Windows is

   procedure Initialize (Process_Blocking_IO : Boolean := False);

and this seems to work just fine.

Oddly enough, though, the call Channel := Stream (Socket, Server)
fails on Windows. The way I fixed it is to first call Connect_Socket
(Socket, Server), and then to call Channel := Stream (Socket).

The following code works on Windows, but has not been tested on Linux
or OS X.

Thanks for all the response!


--  my_client.adb version 3
with GNAT.Sockets;   use GNAT.Sockets;
with Ada.Text_IO;    use Ada.Text_IO;


procedure My_Client is
   Socket   : Socket_Type;
   Server   : Sock_Addr_Type;
   Channel  : Stream_Access;
begin

   --  Initialize Sockets library
   Initialize;
   
   --  Create a client-side socket. Raise Socket_Error on error.
   Create_Socket (Socket, Family_Inet, Socket_Datagram);

   --  Initialize sockaddr_in structure with server (remote) socket
name
   Server := (
              Addr => Addresses (Get_Host_By_Name ("passport"), 1),
              Port => 4134,
              Family => Family_Inet
      );
      
   Connect_Socket (Socket, Server);

   Channel := Stream (Socket);

   String'Output (Channel, "Hello, world!");

   Close_Socket (Socket);

end My_Client;


--  my_server.adb version 3
with GNAT.Sockets;   use GNAT.Sockets;
with Ada.Text_IO;    use Ada.Text_IO;

procedure My_Server is
   Socket     : Socket_Type;
   Address    : Sock_Addr_Type;
   Channel    : Stream_Access;
   Loop_Count : Natural := 0;
begin

   --  Initialize Sockets library
   Initialize;
   
   --  Create an endpoint for communication. Raise Socket_Error on
error.
   Create_Socket (Socket, Family_Inet, Socket_Datagram);

   --  Initialize sockaddr_in structure with server (local) socket
name
   Address.Addr := Addresses (Get_Host_By_Name ("passport"), 1);
   Address.Port := 4134;

   --  Server should bind the address to the socket
   Bind_Socket (Socket, Address);

   Channel := Stream (Socket, Address);
   
   loop
      declare
	 Message : String := String'Input (Channel);
      begin
	 exit when Message = "q";
	 Address := Get_Address (Channel);
	 Put_Line (Message & " from " & Image (Address));
      end;
   end loop;
   
   Shutdown_Socket (Socket);		--  Disconnect socket
   Close_Socket (Socket);		--  Close socket

end My_Server;



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

* Re: Sockets - newbie at a brick wall
  2002-12-13 22:38   ` Benjamin Place
@ 2002-12-16 10:16     ` John McCabe
  0 siblings, 0 replies; 11+ messages in thread
From: John McCabe @ 2002-12-16 10:16 UTC (permalink / raw)


On 13 Dec 2002 14:38:09 -0800, benjamin_place@hotmail.com (Benjamin
Place) wrote:

>Simon Wright <simon.j.wright@amsjv.com> wrote in message news:<x7vsmx2m9y2.fsf@galadriel.frlngtn.gecm.com>...
>> You don't show any calls to GNAT.Sockets.Initialize. We found that
>> leaving out this call made no (apparent) difference on Linux, but that
>> it was necessary on Windows. No experience with MacOS!
>
>My code (version 2) works fine on OS X (or so it seems), but you're
>right, it fails on Windows.
>
>The following code (version 3) works on Windows. The default parameter
>on Windows is
>
>   procedure Initialize (Process_Blocking_IO : Boolean := False);
>
>and this seems to work just fine.

You can set it however you like in Windows, it's not used.



Best Regards
John McCabe

To reply by email replace 'nospam' with 'assen'



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

end of thread, other threads:[~2002-12-16 10:16 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-12-11 18:40 Sockets - newbie at a brick wall Benjamin Place
2002-12-12  9:58 ` John McCabe
2002-12-12 14:19   ` John McCabe
2002-12-12 16:23     ` Toshitaka Kumano
2002-12-12 17:27       ` John McCabe
2002-12-13 11:25       ` John McCabe
2002-12-12 16:49   ` Benjamin Place
2002-12-13 11:31 ` Simon Wright
2002-12-13 13:52   ` John McCabe
2002-12-13 22:38   ` Benjamin Place
2002-12-16 10:16     ` John McCabe

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