I'm still having problems while doing sockets read/write. I have a
server that often crashes with error "Broken Pipe". The server works
always with the same clients and many times it doesn't crash until
something happens that I don't understand.
This is the server code:
-- file tcp_server.adb
with GNAT.Sockets; use GNAT.Sockets;
with Ada.Text_IO; use Ada.Text_IO;
procedure TCP_Server is
Socket : Socket_Type;
Client : Socket_Type;
Sock_Addr : Sock_Addr_Type;
Cli_Addr : Sock_Addr_Type;
L : constant Positive := 5;
Channel : Stream_Access;
S : String( 1 .. 80 );
begin
Initialize;
Create_Socket( Socket );
Set_Socket_Option( Socket, Socket_Level, ( Reuse_Address, True ) );
Sock_Addr.Addr := Inet_Addr( "127.0.0.1" );
Sock_Addr.Port := 9876;
Bind_Socket( Socket, Sock_Addr );
Listen_Socket( Socket, L );
Put_Line( "Server in attesa di connessioni..." );
loop
Accept_Socket( Socket, Client, Cli_Addr );
Channel := Stream( Client );
String'Read( Channel, S );
String'Write ( Channel, ( "Ricevuto " & S ) );
Put_Line( "Client ha inviato codice """ & S & """" );
Free( Channel );
Close_Socket( Client );
if S( 1 .. 4 ) = "kill" then
Put_Line( "Sono stato ucciso" );
exit;
end if;
end loop;
Close_Socket( Socket );
Finalize;
end TCP_Server;
And the following is client code:
-- file tcp_client.adb
with GNAT.Sockets; use GNAT.Sockets;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings; use Ada.Strings;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
with Ada.Command_Line; use Ada.Command_Line;
procedure TCP_Client is
Socket : Socket_Type;
Address : Sock_Addr_Type;
Channel : Stream_Access;
S : String( 1 .. 80 );
begin
if Argument_Count < 1 then
return;
end if;
Initialize;
Create_Socket( Socket );
Address.Addr := Addresses( Get_Host_By_Name( "localhost" ), 1 );
Address.Port := 9876;
Connect_Socket( Socket, Address );
Channel := Stream( Socket );
Move( Argument( 1 ), S );
if S( 1 .. 4 ) = "kill" then
Put_Line( "Sto ordinando a Server di fermarsi!" );
else
Put_Line( "Sto inviando """ & Trim( S, Both ) & """" );
end if;
String'Write( Channel, S );
String'Read( Channel, S );
Put_Line( "Server ha risposto """ & Trim( S, Both ) & """" );
Free( Channel );
Close_Socket( Socket );
Finalize;
end TCP_Client;
I usually test the server this way:
$ ./tcp_client abc & ./tcp_client def & ./tcp_client xyz & ./tcp_client
kill
1) Why does this server sometimes crash with "Broken Pipe"?
2) It seems that using Input/Output instead of Read/Write never causes
"Broken Pipe". Why?
Thanks to all of you who will reply,
fabio de francesco
on my windows machine, i get the windows equivalent ("connection reset
by peer") on every test.
it seems due to the speed of your computer. i observed that closing a
local socket (that is a socket which connect a server and a client on
the same machine) while data are not already received by the other
side always causes loss of data on fast computers.
--
rien
I get the same error as yours on the client side. "Broken Pipe" is only
on server side. We get "connection reset by peer" only after the server
crashes.
>
> it seems due to the speed of your computer. i observed that closing a
> local socket (that is a socket which connect a server and a client on
> the same machine) while data are not already received by the other
> side always causes loss of data on fast computers.
>
I'm not so sure, because I have never seen that "Broken Pipe" error
before now on my computer. Furthermore I don't get any crashes while
running similar client/server Ada programs using T'Input/T'Output or
Receive_Socket/Send_Socket on the same computer.
If it can help in investigating this issue I can post these other
programs too.
As a consequence I must think I am missing something in that specific
code. Please help me to spot what I'm doing wrong.
fabio de francesco
> I usually test the server this way:
>
> $ ./tcp_client abc & ./tcp_client def & ./tcp_client xyz & ./tcp_client
> kill
>
> 1) Why does this server sometimes crash with "Broken Pipe"?
Just now I replaced your 'kill' above with 'lmn' and ran it 25 times
with no errors -- GCC 4.0.0 on Darwin 7.9.0
--S
I compiled the program with GCC 3.4.1 on Linux 2.6.11.5. It continues
to report the same error.
I'm going to bootstrap a brand new GCC 4.0 and then I try again.
fabio de francesco
As I wrote in a previous message I bootstrapped a new GCC 4.0.0 and
then I recompiled those programs again.
The server program continues to crash giving a more elaborated message:
raised GNAT.SOCKETS.SOCKET_ERROR : [32] Broken pipe
The message I got with GCC 3.4.1 was only "Broken Pipe". Now there's
also that "32" number. What is it?
Do any of you have any suggestion on this problem?
Regards,
fabio de francesco
>
> As I wrote in a previous message I bootstrapped a new GCC 4.0.0 and
> then I recompiled those programs again.
>
> The server program continues to crash giving a more elaborated
message:
>
> raised GNAT.SOCKETS.SOCKET_ERROR : [32] Broken pipe
>
> The message I got with GCC 3.4.1 was only "Broken Pipe". Now there's
> also that "32" number. What is it?
>
> Do any of you have any suggestion on this problem?
>
Where and how would you generally investigate on a problem like this?
fdf
> raised GNAT.SOCKETS.SOCKET_ERROR : [32] Broken pipe
One thing you could do is handle Gnat.Sockets.Socket_Error.
--
Jeff Carter
"There's no messiah here. There's a mess all right, but no messiah."
Monty Python's Life of Brian
84
> 1) Why does this server sometimes crash with "Broken Pipe"?
This usually indicates that the client closed its end of the
connection while the server was still sending data. Maybe this
happens because your code does not deal with partial reads/writes.
> 2) It seems that using Input/Output instead of Read/Write never causes
> "Broken Pipe". Why?
The stream implementation provided by GNAT.Sockets deals with partial
reads and writes.
In C I know how to read() in a loop and increment a pointer to a buffer
(a C string) while receiving characters until there are no more of
them. It is sufficient that read() returns how many characters has
already read and some little math involving buffer size.
In Ada I don't know how to do it... Can you please give me any
suggestions?
Thank you in advance.
fabio
I'm sure there's a better way .. but this is what I did
function Read_Request (From : Socket_Type) return String is
Tmp : Stream_Element_Array (1 .. 2048);
Last : Stream_Element_Offset := Tmp'First - 1;
Next : Stream_Element_Offset;
Termination : constant Stream_Element_Array :=
(Character'Pos (CR),
Character'Pos (LF),
Character'Pos (CR),
Character'Pos (LF));
S : Stream_Access := Stream (From);
begin
-- We need to read the whole request from the client. Of course
-- we don't know how long it is. We can't just issue an
-- Ada.Streams.Read for a large buffer, because the client may
-- not have sent that much and if she hasn't we'll block until
-- she gives up and closes the socket. So we read a character
-- at a time until we've got the CR/LF/CR/LF which terminates
-- the line.
loop
Ada.Streams.Read (Stream => S.all,
Item => Tmp (Last + 1 .. Last + 1),
Last => Next);
exit when Next = Last;
Last := Last + 1;
exit when Last >= Termination'Length
and then Tmp (Last - 3 .. Last) = Termination;
exit when Last = Tmp'Last;
end loop;
Free_Stream (S);
declare
Result : String (1 .. Natural (Last));
pragma Import (Ada, Result);
for Result'Address use Tmp'Address;
begin
return Result;
end;
end Read_Request;