In the
previous post I described sending messages directly between components via
TCP/IP (Transmission Control Protocol/Internet
Protocol) via
Microsoft WinSock residing within the same application, different applications
on the same PC, and different applications on different PCs.
It took me
a while to get the communications between components on different PCs working
but it worked fine after I got the missing links figured out.
However,
there was a problem when WSA 10061 (Connection refused) errors occurred. It was 2 strikes and you're out. So after publishing the previous results I
decided to see what would happen if after a WSA 10061 error, I immediately did another
WSAStartup. This worked great. No longer did I get the WSA 10093 (Successful
WSAStartup not yet performed)
errors that prevented communications from happening. Problem solved.
While at
it I decided to try to terminate an application and then re-launch it to see if
communications between the components of another application that remained
running would start over. This required
a couple of changes to the code but when made the components re-connected and
messages again were sent and received by the components residing in different
applications.
WSA
10093 fix
To fix the
WSA 10093 error after a 10061 error, I replaced the calls to the WSACleanup
function with calls to a new WSARestart procedure located in WinSock.adb.
procedure WSARestart is
Status
-- Result of WSAStartup call
: ExecItf.INT;
use type ExecItf.INT;
begin -- WSARestart
-- Do WSA Cleanup
Status := ExecItf.WSACleanup;
-- Followed by WSA Startup
Status := ExecItf.WSAStartup(
VersionRequired => 16#0202#, -- version 2.2
WSAData => lpWSAData );
if Status /= 0 then
Text_IO.Put("ERROR: WinSock
WSAStartup failed");
Int_IO.Put(Integer(Status));
Text_IO.Put_Line(" ");
return;
end if;
end WSARestart;
For
instance,
if Comm.Link(Index).Receive.Socket.Socket =
ExecItf.INVALID_SOCKET then
ExecItf.Display_Last_WSA_Error;
declare
Text : Itf.V_80_String_Type;
begin
Text.Data(1..9) := "WSA
Error";
Text :=
TextIO.Concat(Text.Data(1..9),Integer(Index));
TextIO.Put_Line(Text);
end;
declare
Text : String(1..28) := "Client
Socket NOT created: x";
begin
Text(28) := to_Digit(Integer(Index));
Text_IO.Put_Line(Text);
end;
else -- valid
of WinSock-Recv-ReceiveCreate of the previous post has
Status := ExecItf.WSACleanup;
become
WSARestart;
as well as removing the declaration of Status. The same for the other calls to the
WSACleanup function.
Reconnect after application closed
A few changes were required in order to have the ability to
have a running application with its components reconnect to another
application's components after it had been terminated and then restarted.
The main one was that the WinSock Xmit forever loop had to
continue to execute its Connect loop rather than exit it and enter a do nothing
loop following the Accept of a client connection. This merely involved removing the "exit Connect;"
statement and then dispensing with the second loop since the code was then never
executed.
That is, the Connect loop already checked to avoid the call
to the C_Accept function if the Transmit connection was connected. Therefore, there was no harm in using the
Connect loop as the forever loop. After
the connection was recognized the loop did nothing so it could continue to be
executed instead of needing the exit.
It thus became ready to recognize the need for the Accept function when
the connection was broken due to the closing of the application that contained
the component of the connection. When
the application was relaunched it was then ready to have the conditions for a
client connection fulfilled once more.
Thus, the WinSock-Xmit callback forever loop just becomes
-- Forever loop as initiated by Threads
procedure Callback
( Id : in Integer
) is
-- This procedure runs in the particular
thread assigned to accept the
-- connection for a component.
Client_Socket
-- Accepted client socket
: ExecItf.SOCKET :=
ExecItf.INVALID_SOCKET;
type Int_Ptr_Type is access ExecItf.INT;
use type Interfaces.C.int;
Index
-- Index for Component for Comm.Link
: Connection_Count_Type
:= Connection_Count_Type(Id);
Client_Address_Size
-- Size of socket address structure
: ExecItf.INT
:=
Comm.Link(Index).Transmit.Socket.Data'size/8;
use type ExecItf.SOCKET;
function to_Int_Ptr is new
Unchecked_Conversion( Source => System.Address,
Target => Int_Ptr_Type );
function to_Integer is new
Unchecked_Conversion -- for debug
( Source =>
ExecItf.PSOCKADDR,
Target =>
Integer );
begin -- Callback
Connect:
Loop
declare
Text : String(1..28);
begin
Text(1..19) := "Xmit Callback
loop ";
Text(20) := to_Digit(Integer(Id));
Text(21) := ' ';
Text(22) := to_Digit(Integer(Index));
if Comm.Link(Index).Transmit.Created
then
Text(23..28) := " True ";
else
Text(23..28) := " False";
end if;
Text_IO.Put_Line(Text);
end;
if Index = 1 then
Text_IO.Put_Line("index of
1");
elsif Index = 2 then
Text_IO.Put_Line("index of
2");
else
Text_IO.Put_Line("index of
3");
end if;
if Comm.Link(Index).Transmit.Created and
then
Comm.Link(Index).Receive.Connected
and then
not
Comm.Link(Index).Transmit.Connected
then
-- Accept a client connection.
Client_Socket :=
ExecItf.C_Accept( S =>
Comm.Link(Index).Transmit.Socket.Socket,
Addr => null,
AddrLen => null
);
declare
Text : Itf.V_80_String_Type;
begin
Text := TextIO.Concat( "Xmit
after C_Accept", Integer(Index) );
TextIO.Put_Line(Text);
end;
if Client_Socket =
ExecItf.INVALID_SOCKET then
Text_IO.Put_Line("ERROR: Server
Client Socket NOT accepted");
ExecItf.Display_Last_WSA_Error;
else -- Accepted
Comm.Link(Index).Transmit.Connected
:= True;
Comm.Link(Index).Transmit.Socket.Client
:= Client_Socket;
end if; -- invalid Client_Socket
end if; -- Comm.Link(Index).Transmit.Created
Text_IO.Put_Line("Xmit Callback
initial loop end");
delay(1.0*Duration(Index)); -- seconds
end loop Connect;
end Callback;
end Xmit;
without the exit from the Connect loop and without the
following do nothing loop.
Also, note that the Client_Socket of C_Accept function is
stored in a new Comm.Link(Index).Transmit.Socket.Client
location of the WinSock structures rather than overwriting the
Transmit.Socket.Socket as before so that it remains available for restart if
needed.
Another minor change was made to WinSock-Transmit.adb to
indicate that the Receive (i.e., Client) connection was no loner valid along
with the Transmit / Server connection when the Send of a message could no
longer be accomplished. Thus, that code
became (using the newly saved Client Socket)
Bytes_Written :=
ExecItf.Send( S =>
Comm.Link(Index).Transmit.Socket.Client,
Buf => to_PCSTR(Message),
Len => ExecItf.INT(Count),
Flags => 0 );
if
Bytes_Written /= ExecItf.INT(Count) then
Text_IO.Put("ERROR: WinSock Message Send failed");
Int_IO.Put(Integer(Bytes_Written));
Text_IO.Put(" ");
Text_IO.Put(String(Comm.Link(Index).Transmit.Name(1..25)));
Int_IO.Put(Integer(Index));
Int_IO.Put(Integer(Comm.Link(Index).Transmit.Socket.Data.SIn_Port));
Text_IO.Put_Line(" ");
ExecItf.Display_Last_WSA_Error;
Comm.Link(Index).Transmit.Connected :=
False;
Comm.Link(Index).Receive.Connected :=
False;
where
--
Indicate that no longer connected
Comm.Link(Index).Transmit.Connected := False;
became
--
Indicate that no longer connected
Comm.Link(Index).Transmit.Connected := False;
Comm.Link(Index).Receive.Connected := False;
I think these minimal changes were all that were
necessary. With the final one
immediately above, Receive.Connected was set back to False. Therefore, the Forever loop of my Recv
callback could re-connect without change since the loop checks for Connected
before waiting for a message via the Microsoft Recv function.
Results
Debugging the two changes I started App1 and App2 at close
to the same time and allowed them to run for 10 to 15 seconds. I then terminated App2 and waited about 10
seconds and then restarted it and allowed both applications to run for another
10 to 15 seconds before terminating both applications.
App1 contains the same two components as in the past –
Component1 and NewComponent that is also identified in the output as
Component4. App2 contains Component2
and ExComponent. Component1 and
Component2 communicate with each other.
Therefore, when App2 was terminated these two components could no longer
communicate while Component1 and NewComponent should continue to send messages
to each other.
The results are
Client
Socket 2 Connected
Comm.Data(2)
Available for Client ß
Receive
Connected 2 17183
valid
socket
Client
Socket 1 Connected
Comm.Data(1)
Available for Client ß
Receive
Connected 1 16671
valid
socket
Client
Socket 3 Connected
Comm.Data(3)
Available for Client ß
Receive
Connected 3 17439
valid
socket
Component1
received a message: Component2 message ß from App2
Xmit
Callback loop 1 1 True
index
of 1
Xmit
after C_Accept 1
Xmit
Callback initial loop end
Component1
wait for event
Component1
after end of wait
Component1
sending to Component2
Transmit
Index 1 DeliverTo 2
Transmit
sent using socket port 16927 ß sending to Component2 of App2
Component1
sending to component4
Transmit
Index 2 DeliverTo 4
Transmit
2 not connected, returning ç above has Available for Client
NewComponent
in forever loop
Transmit
Index 3 DeliverTo 1
Transmit
3 not connected, returning ç above has Available for Client
Xmit
Callback loop 2 2 True
index
of 2
Xmit
Callback loop 1 1 True
index
of 1
Xmit
after C_Accept 2 ß
connection to local component completed
Xmit
Callback initial loop end
Xmit
Callback initial loop end
Component1
received a message: Component2 message ß message from Component2
Xmit
Callback loop 1 1 True
index
of 1
Xmit
Callback initial loop end
Xmit
Callback loop 3 3 True
index
of 3
Xmit
after C_Accept 3 ß
connection to local component completed
Xmit
Callback initial loop end
Component1
wait for event
Component1
after end of wait
Component1
sending to Component2
Transmit
Index 1 DeliverTo 2
Xmit
Callback loop 2 2 True
Xmit
Callback loop 1 1 True
index
of 1
Xmit
Callback initial loop end
index
of 2
Transmit
sent using socket port 16927
NewComponent
in forever loop
Transmit
Index 3 DeliverTo 1
Xmit
Callback initial loop end
Component1
sending to component4
Transmit
Index 2 DeliverTo 4
Transmit
sent using socket port 17183
NewComponent
received a message: Component1 message for 4 ß local msg received
Component1
received a message: Component4 message
3 ß
local msg received
Transmit
sent using socket port 17439
Component1
received a message: Component2 message ß
message with App2 running
Xmit
Callback loop 1 1 True
The above shows the connection between Component1 of App1
and Component2 of App2 at its beginning along with NewComponent of App1
receiving a message from Component1 and Component1 receiving a message from
Component4 (that is, NewComponent).
Then App2 was terminated and is indicated when the Send
failed in WinSock Transmit.
Component1
received a message: Component2 message ß
Xmit
Callback loop 3 3 True
index
of 3
Xmit
Callback initial loop end
Xmit
Callback loop 1 1 True
index
of 1
Xmit
Callback initial loop end
ERROR:
WinSock Receive failed 1
ERROR:
WSALastError 0
Xmit
Callback loop 2 2 True
index
of 2
Xmit
Callback initial loop end
Xmit
Callback loop 1 1 True
index
of 1
Xmit
Callback initial loop end
NewComponent
in forever loop
Transmit
Index 3 DeliverTo 1
Transmit
sent using socket port 17183
Component1
received a message: Component4 message
18
Component1
wait for event
Component1
after end of wait
Component1
sending to Component2
Transmit
Index 1 DeliverTo 2 ç
Try send from Com 1 to Com 2
ERROR:
WinSock Message Send failed -1 ç
WinSock Server Accept 01
1 16927
ERROR:
WSALastError 0 ç App2 has been shutdown
Component1
sending to component4
Transmit
Index 2 DeliverTo 4
NewComponent
received a message: Component1 message for 4 ß (A)
Transmit
sent using socket port 17439
ReceiveCreate
Index 1
Xmit
Callback loop 1 1 True
index
of 1
Xmit
Callback initial loop end
ERROR:
WSALastError 10061 ç
WSA
Connect Error 1
ç Index of (1,2) pair
Client
Socket 1 NOT Connected ç
ERROR:
Client Connect 1 FAILED: ç
WinSock
Receive 01 1
Receive
NOT Connected 1 16671 ç
ReceiveCreate
Index 1
Then WSA errors of 10061 start to happen. But 10093 errors no longer happen since the new WSARestart procedure
does the WSACleanup and then does a new WSAStartup. The messages between the two local components continue as before
as illustrated by NewComponent receiving a message from Component1 at (A).
The 10061 errors continue but don't shutdown Microsoft
WinSock since the WSAStartup is done each time. Then, upon the restart of App2 there is a last 10061 error
(before or maybe after the restart), Component1 attempts to send to component 2
but Transmit has yet to recognize a reconnection (B) and so just returns to
Component1.
Xmit
Callback initial loop end
ERROR:
WSALastError 10061 ç
again
WSA
Connect Error 1
Client
Socket 1 NOT Connected
ERROR:
Client Connect 1 FAILED:
WinSock
Receive 01 1
Receive
NOT Connected 1 16671
Xmit
Callback loop 2 2 True
index
of 2
Xmit
Callback initial loop end
Xmit
Callback loop 1 1 True
index
of 1
Xmit
Callback initial loop end
NewComponent
in forever loop
Transmit
Index 3 DeliverTo 1
Component1
received a message: Component4 message 33
Transmit
sent using socket port 17183
Component1
wait for event
Component1
after end of wait
Component1
sending to Component2
Transmit
Index 1 DeliverTo 2 ß
(B)
Transmit
1 not connected, returning ß
Component1
sending to component4
Transmit
Index 2 DeliverTo 4
NewComponent
received a message: Component1 message for 4
Transmit
sent using socket port 17439
ReceiveCreate
Index 1
Xmit
Callback loop 1 1 True
index
of 1
Xmit
Callback initial loop end
Client
Socket 1 Connected
Comm.Data(1)
Available for Client
Receive
Connected 1 16671
valid
socket
Xmit
Callback loop 1 1 True
index
of 1
Xmit
Callback loop 3 3 True
index
of 3
Xmit
Callback initial loop end
Xmit
Callback loop 2 2 True
index
of 2
Xmit
Callback initial loop end
NewComponent
in forever loop
Transmit
Index 3 DeliverTo 1
Component1
received a message: Component4 message
34
Transmit
sent using socket port 17183
Component1
wait for event
Component1
after end of wait
Component1
sending to Component2
Transmit
Index 1 DeliverTo 2
Transmit
1 not connected, returning
Component1
sending to component4
Transmit
Index 2 DeliverTo 4
NewComponent
received a message: Component1 message for 4
Transmit
sent using socket port 17439
Xmit
Callback loop 2 2 True
index
of 2
Xmit
Callback initial loop end
NewComponent
in forever loop
Transmit
Index 3 DeliverTo 1
Transmit
sent using socket port 17183
Component1
received a message: Component4 message
35
Component1
wait for event
Component1
after end of wait
Component1
sending to Component2
Transmit
Index 1 DeliverTo 2 ß B
(again)
Transmit
1 not connected, returning ß
Component1
sending to component4
Transmit
Index 2 DeliverTo 4
Transmit
sent using socket port 17439
NewComponent
received a message: Component1 message for 4
Xmit
Callback loop 3 3 True
index
of 3
Xmit
Callback initial loop end
Xmit
after C_Accept 1 ç
Connection to App2 completed once again
Xmit
Callback initial loop end
Xmit
Callback loop 2 2 True
index
of 2
Xmit
Callback initial loop end
NewComponent
in forever loop
Transmit
Index 3 DeliverTo 1 ß
message to be sent to Component1
Component1
received a message: Component4 message
36
Transmit
sent using socket port 17183 ß message sent to Component1
Component1
wait for event
Component1
after end of wait
Component1
sending to Component2
Transmit
Index 1 DeliverTo 2 ß
(C)
Transmit
sent using socket port 16927
Component1
sending to component4
Transmit
Index 2 DeliverTo 4
Transmit
sent using socket port 17439
NewComponent
received a message: Component1 message for 4
Xmit
Callback loop 1 1 True
index
of 1
Xmit
Callback initial loop end
Component1
received a message: Component2 message ß msg received from Com 2
Then, the C_Accept invocation of the Xmit Callback succeeds
again (as it did when App2 was initially started). And at (C) Transmit again recognizes that there is a connection
and sends the Component1 message to Component2 of the newly started App2. With the last line shown from the example,
Component1 has again received a message from Component2.
No comments:
Post a Comment