aStreams delayed termination

mc73

Well-Known Member
Licensed User
Longtime User
After some time of playing around inside my app with aStreams and sockets, I found out something that I missed at first look and was causing occasional problems:
While a socket can be closed, and the corresponding streams can be closed, it takes some time for the streams to really close! Thus, if for example, you're closing both, and then reusing the closed socket for a new connection, the streams (which were supposed to be closed) throw out a delayed terminated event, producing error.
I've changed my code, because I'm using many simultaneous open sockets, in order to prevent a hanged socket due to a delayed streams close, so now I have many sockets but NOT with the same index for streams (for e.g. I can have socket1(0) with astreams(1) or socket(1) with astreams(0) and so on).

Yet, I am wondering, is this an unavoidable property of streams (os)?
 

mc73

Well-Known Member
Licensed User
Longtime User
Oh, and here's the code for a new connection:
B4X:
Sub Server_NewConnection (Successful As Boolean, NewSocket As Socket)
    If Successful Then
    
        Dim freeSocketId As Int, freeStreamsId As Int 
        freeSocketId=-1:freeStreamsId=-1
        Dim k As Int 
        k=-1
        Do While freeSocketId=-1 AND k<99
        k=k+1
        If socketsStatus(k)=0 Then
        socketsStatus(k)=1:freeSocketId=k
        socket1(freeSocketId)=NewSocket
        fSocket=freeSocketId
        End If
        Loop
        If freeSocketId=-1 Then
        freeSocketId=0
        socket1(freeSocketId)=NewSocket
        fSocket=freeSocketId
        End If
        Log(DateTime.Time(DateTime.Now) & " - " & "new connection - socket:" & freeSocketId)
        Dim k2 As Int 
        k2=-1
        Do While freeStreamsId=-1 AND k2<99
        k2=k2+1
        If streamsStatus(k2)=0 Then
        streamsStatus(k2)=1:freeStreamsId=k2
        fStreams=freeStreamsId
        End If
        Loop
        
        If freeStreamsId=-1 Then
        freeStreamsId=0
        End If
        aStreams(freeStreamsId).InitializePrefix(socket1(freeSocketId).InputStream, False, socket1(freeSocketId).OutputStream, "AStreams" & freeStreamsId)
        Log("init astreams" & freeStreamsId)
    Else
        ToastMessageShow(LastException.Message, True)
    End If
    server.Listen
    
End Sub
 
Upvote 0

mc73

Well-Known Member
Licensed User
Longtime User
Where is the code that closes the socket?

B4X:
Sub freeThisSocket(whichSocket As Int,whichStream as Int)
    'log("freeThisSocket("&whichSocket&") called")
    
    'If aStreams(whichSocket).IsInitialized Then aStreams(whichSocket).Close 
    aStreams(whichStream).Close 
    'If socket1(whichSocket).IsInitialized Then socket1(whichSocket).Close
    socket1(whichSocket).Close 
    socketsStatus(whichSocket)=0
    Log("socket " & whichSocket & " closed")
End Sub
Here it is. When server gets in aStreams_NewData a 'close' command, we call this sub after we finished editing data.
 
Last edited:
Upvote 0

mc73

Well-Known Member
Licensed User
Longtime User
In order to explain better, before the code I posted, I was closing sockets and streams with the very same id. When I was doing this, occasionally, especially upon quick actions, I was getting at the logs, things like 'socket1(0) closed'. Then I had a 'new connection socket1(0)'. The problem was that even if I was astreams(whichSocket).close, I was getting the aStreams0_terminated, AFTER a new connection was established, thus destroying the communication.
 
Upvote 0

mc73

Well-Known Member
Licensed User
Longtime User
My guess is that the other side sends "close" and then closes the connection. The connection is actually closed before you get to handle this message. You can "free" the socket / astream after 100ms (or try with CallSubDelayed).
Not exactly. The client is sending a 'closeSocket' message to the server. Then we're freeing the socket at the server's side. At the clients side, after we get the final data, we truly close the socket. This happens in a service module which is recreated everytime we set a new connection.
It's been a long time since this project, I can't remember why exactly I wasn't straight closing the socket from the client side. I think it had to do with some inaccuracies in astreams_terminated at the server's side. Anyway, as you say, there is a 100ms delay. This is exactly where the problem is, because in some occasions, I immediately re-establish a connection! Actually I am happy with differentiating between sockets' and streams' indexes, still I am hoping there is a more simple way which I am missing to see right now.
 
Upvote 0

mc73

Well-Known Member
Licensed User
Longtime User
And here's a modification inside the newConnection sub, after playing with 5 open sockets from different devices:
B4X:
correspondingStreamToSocket(freeSocketId)=freeStreamsId
This is mapping stream index to socket index.

Then freeing the socket gets
B4X:
Sub freeThisSocket(whichSocket As Int)
   
  aStreams(correspondingStreamToSocket(whichSocket)).Close 
  socket1(whichSocket).Close 
  socketsStatus(whichSocket)=0
  Log("socket " & whichSocket & " closed")
End Sub
Finally, in aStreamsTerminated (this sub is used by multiple streams)

B4X:
Sub aStreamsTerminated(currentStream As Int)
    
    Log("astreams" & currentStream & "terminated")
    streamsStatus(currentSocket)=0
    End Sub
In order to give a better idea of why all this complication (while still I believe that somehow I'm missing the obvious), here's a log, demonstrating the 'delayed' (in quotations, since Erel explained that a 100ms is normal) astreams termination event.
B4X:
LogCat connected to: c08903280035ae6e
--------- beginning of /dev/log/main


--------- beginning of /dev/log/system
** Service (serverlistener) Create **


** Service (serverlistener) Start **


13:31:44 - new connection - socket:0


init astreams0
aStreams0_newData
13:31:44 - test02    READDBCHANGES    1    1    1


aStreams0_newData
13:31:44 - test02    end
send2client=dbChanges    1    1    1


send2client=end


aStreams0_newData
13:31:44 - test02    CLOSESOCKET
aStreams0_newData
13:31:44 - test02    end


socket 0 closed


astreams0terminated


13:31:50 - new connection - socket:0


init astreams0
aStreams0_newData
13:31:50 - test02    READSHIFTS    1
aStreams0_newData


13:31:50 - test02    end
send2client=shiftExists    4


send2client=end
aStreams0_newData


13:31:50 - test02    CLOSESOCKET
aStreams0_newData
13:31:50 - test02    end
socket 0 closed


13:31:50 - new connection - socket:0


init astreams1


astreams0terminated
aStreams1_newData


13:31:51 - test02    READTABLES    1000
aStreams1_newData


13:31:51 - test02    end
server tables=error


send2client=error


send2client=end
aStreams1_newData


13:31:51 - test02    CLOSESOCKET
aStreams1_newData
13:31:51 - test02    end
socket 1 closed
astreams1terminated


13:32:06 - new connection - socket:1
init astreams0


aStreams0_newData


13:32:06 - test02    READTABLES    1001
aStreams0_newData
13:32:06 - test02    end
server tables=10004    17    17    0    0    


send2client=10004    17    17    0    0    
send2client=end


aStreams0_newData


13:32:06 - test02    CLOSESOCKET
aStreams0_newData
13:32:06 - test02    end
socket 0 closed
You can see here, that we have occasions at which, the app reacts faster than the stream, thus obliging me to open a new stream.
 
Upvote 0

mc73

Well-Known Member
Licensed User
Longtime User
What is exactly the problem? Do you need to ignore an AsyncStream_Terminated event?

You should not reuse any socket or asyncstream object. You should instead create a new object (by calling Dim again) and use new objects.

Suppose you have 10 clients. You establish connection with the first. Let's now dim socket and streams for this one. Their index is 1, for now. In a while we have a new connection, name it 2 (both socket and stream).
Here's the catch: At some point client1 after finishing the process, is free again. So, I am setting a variable telling that socket1 (please remember that I'm using arrays, cause I can have up to 100 clients in this e.r.p). is free again for reuse. The same goes for streams(1). Ok. The thing is at that very same moment, another client may connect. Now, this client can get the socket client1 was using. This is NO problem at all. The problem is if it tries to use aStreams1. If it does, we're getting the old aStreams1 delayed terminated event, thus the new connection breaks!

Now, your suggestion for re-dimming sockets and streams, is one I will try very soon. Please note that this was one of my first projects, and I didn't see back then the necessity of re-dimming this type of objects, since I was re-initializing them. Even now, I really don't understand quite well, why I should re-dim an already closed object instead of simply re-initializing it. I remember having a look at b4a-bridge. In the new Connection sub, there was a re-dimming if streams, though client socket was not redimmed. Probably cause there was no other client involved.
Anyway, as already said, now I'm quite happy with how I handle streams (in fact my previous post was just to show the log that got me into changing stuff), but surely I will try simply redimming sockets and streams, thank you.
 
Upvote 0

mc73

Well-Known Member
Licensed User
Longtime User
You should instead create a new object (by calling Dim again) and use new objects.
Cannot redim the objects cause I have an array.
B4X:
 dim aStreams(myIndex) as aSyncStreams
bring an outOfBounds exception, which is quite normal. Anyway, since project works now as intended with previous arrangements, and since there was the conclusion of the 100ms delay in streamsClosing, I truly think there is no further need at the moment. In real life, since I've already re-installed the new version to the main server at the factory, gives again problems, I guess I'll have to request your advices on this subject again. Thank you, again.
 
Upvote 0

mc73

Well-Known Member
Licensed User
Longtime User
Just for the record, you can create a new object with:
B4X:
Dim a As AsyncStream
aStreams(x) = a
Sure and thanks again, I will give this a try too.
Then,
B4X:
socket1(freeSocketId)=NewSocket
m, which is already used inside the newConnection sub, is in fact dimming a fresh socket, no?
 
Upvote 0
Top