Socket reconnection requires an application restart

Previous topic - Next topic

FutureCow

As per my earlier post (http://www.glbasic.com/forum/index.php?topic=4152.0), I'm classing this as a bug.

I have a client and server app which get connected by a TCP socket. If one side has a failure of some description, both sides close their sockets. If I then try to recreate the socket without restarting my application, it refuses to do so - the Server's SOCK_TCPACCEPT returns "10035 A non-blocking socket operation could not be completed immediately."
There's also a bug in that the following SOCK_TCPCONNECT on the Client manages to connect to the broken socket, but then constantly returns a -2 return code (a non-blocking socket operation could not be completed immediately).

Here's a test program which is as simple as I could make it while checking each command for an error return code. It's a console application so make sure you compile it as such.
Run two copies side by side, one as the server, one as the client. Here's the code, results follow
Code (glbasic) Select
GLOBAL Net_ServerIP$
GLOBAL EstablishedConnection
GLOBAL Net_PortNumber
GLOBAL Done

Net_PortNumber=1234
Net_ServerIP$="127.0.0.1"

SOCK_INIT()

GLOBAL XLOC,YLOC
// MAIN
//-------------------------------------------------------------------------
Done=0
YLOC=0
XLOC=0
LOCAL Result$

STDOUT "Press s to be server\n"
STDOUT "Press c to be client\n"

WHILE Done=0
Result$=INKEY$()
IF Result$ = "s" // pressed 1
STDOUT "Server\n"
DEBUG "Server-debug\n"
GOSUB server
Done=1
ELSEIF Result$= "c" // pressed 2
STDOUT "Client\n"
DEBUG "Client-debug\n"
GOSUB Client
Done=1
ENDIF
WEND
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
SUB server:
LOCAL Done=0,RV=0
LOCAL ClientIPAddress%
LOCAL Sock%


Sock=SOCK_TCPLISTEN(Net_PortNumber) // Listen for connections on our socket
IF Sock=-1
STDOUT "Server : SOCK_TCPLISTEN error. TCPLISTEN returned "+Sock+"\n"
STDOUT "Error was : " + NETGETLASTERROR$() + "\n"
SLEEP 4000
END
ENDIF

STDOUT "Server : Waiting for connection with TCPACCEPT...\n"
// STDOUT "'1' to exit.\n"
WHILE Done=0
EstablishedConnection=SOCK_TCPACCEPT(Sock,ClientIPAddress) // Check for established client connection
IF EstablishedConnection <> -1
Done=1 // If a connection was successful
ENDIF

// IF INKEY$() = "1" // quit
// STDOUT "Exiting...\n"
// SLEEP 1000
// END
// ENDIF
WEND

// We now have a connection
STDOUT "\nServer : Sending message '1'\n"
RV = SOCK_TCPSEND(EstablishedConnection,"1")
STDOUT "Server : TCPSEND Return value = " + RV + "\n"
IF RV=-1
STDOUT "Error occurred using TCPSEND to send data.\n"
STDOUT "Error was : " + NETGETLASTERROR$() + "\n"
SLEEP 4000
END
ENDIF

STDOUT "When message has been received by client, press '2' to break connection\n"
Done=0
WHILE Done=0
IF INKEY$() = "2"
RV=SOCK_CLOSE(EstablishedConnection)
IF RV <> TRUE
STDOUT "Server : Error breaking connection : RV = " + RV + "\n"
STDOUT "Error was : " + NETGETLASTERROR$() + "\n"
SLEEP 4000
END
ELSE
STDOUT "Server : Connection broken : RV = " + RV + "\n"
STDOUT "Sleeping for 2 seconds.\n\n\n\n"
Done=1
ENDIF
ENDIF
WEND
SLEEP 2000

STDOUT "Server Waiting for connection again with TCPACCEPT...\n"
// STDOUT "'3' to exit.\n"

Sock=SOCK_TCPLISTEN(Net_PortNumber) // Listen for connections on our socket
Done=0
WHILE Done=0
EstablishedConnection=SOCK_TCPACCEPT(Sock,ClientIPAddress) // Check for established client connection
IF EstablishedConnection > 0 THEN Done=1 // If a connection was successful
IF EstablishedConnection = -1
STDOUT "Server : SOCK_TCPACCEPT error\n"
STDOUT "Error was : " + NETGETLASTERROR$() + "\n"
SLEEP 4000
END
ENDIF
// IF INKEY$() = "3" // esc
// SLEEP 4000
// END
// ENDIF
WEND

// We now have a second connection
STDOUT "Server : Sending message '2'\n"
RV=SOCK_TCPSEND(EstablishedConnection,"2")
STDOUT "Server : TCPSEND Return value = " + RV + "\n"
IF RV=-1
STDOUT "Error occurred - error was : " + NETGETLASTERROR$() + "\n"
SLEEP 4000
END
ENDIF

STDOUT "When message received by client, press '4' to break connection\n"
Done=0
WHILE Done=0
IF INKEY$()="4"
RV=SOCK_CLOSE(EstablishedConnection)
IF RV <> TRUE
STDOUT "Server : Error breaking connection : RV = " + RV + "\n"
STDOUT "Error was : " + NETGETLASTERROR$() + "\n"
SLEEP 4000
END
ELSE
STDOUT "Server : Connection broken : RV = " + RV + "\n"
STDOUT "Sleeping for 2 seconds.\n"
Done=1
ENDIF
ENDIF
WEND

DEBUG ("Server Done\n")
SLEEP 4000
ENDSUB

//-------------------------------------------------------------------------
SUB Client:
LOCAL Done=0
LOCAL RV
LOCAL ReceivedMessage$
LOCAL YLoc=50

STDOUT "Client : Establishing connection with "+Net_ServerIP$+" on port "+Net_PortNumber+"...\n"
EstablishedConnection=SOCK_TCPCONNECT(Net_ServerIP$,Net_PortNumber) // Try to connect to server
IF EstablishedConnection = -1 // Failure
STDOUT "Client : Network error : client failed to create connection"
STDOUT "Client : Error was : " + NETGETLASTERROR$() + "\n"
SLEEP 4000
END
ELSE
STDOUT "Connection Established. EstablishedConnection variable = " + EstablishedConnection + "\n"
ENDIF

STDOUT "Waiting for first message\n"
SLEEP 1000 // allow messages to cache
LOCAL Done=0
WHILE Done=0
RV=SOCK_RECV(EstablishedConnection, ReceivedMessage$, 1023) // Get first message
IF RV=0 // Error
STDOUT "Client : Remote end has disconnected : RV = " + RV + "\n"
STDOUT "Client : Error was : " + NETGETLASTERROR$() + "\n"
STDOUT "Closing socket...\n\n"
SOCK_CLOSE(EstablishedConnection)
STDOUT "Sleeping for 4 seconds....\n"
SLEEP 4000
Done=1
ELSEIF RV = -1 // Error
STDOUT "Client : Network error has occurred : RV = " + RV + "\n"
STDOUT "Client : Error was : " + NETGETLASTERROR$() + "\n"
STDOUT "Closing socket...\n\n"
SOCK_CLOSE(EstablishedConnection)
STDOUT "Sleeping for 4 seconds....\n"
SLEEP 4000
Done=1
ELSEIF RV=-2 // Nothing waiting in the queue
STDOUT "Client : Socket not ready (returned -2). Sleeping for 1 second.\n"
SLEEP 1000
// ELSEIF KEY(1)=1 // ESC
// SLEEP 4000
// END
ELSE
STDOUT "Client : Message length received : "+ RV +"   Message recieved = " + ReceivedMessage$ + "\n"
ENDIF
WEND


// END OF FIRST MESSAGE ----------------------------------------------------------

STDOUT "Client : Reestablishing connection with "+Net_ServerIP$+" on port "+Net_PortNumber+"...\n"
EstablishedConnection=SOCK_TCPCONNECT(Net_ServerIP$,Net_PortNumber) // Try to connect to server
IF EstablishedConnection = -1 // Failure
STDOUT "Client : Network error : client failed to create connection"
STDOUT "Client : Error was : " + NETGETLASTERROR$() + "\n"
SLEEP 4000
END
ELSE
STDOUT "Connection Established. EstablishedConnection variable = " + EstablishedConnection + "\n"
ENDIF

STDOUT "Waiting for second message\n"
Done=0
WHILE Done=0
RV=SOCK_RECV(EstablishedConnection, ReceivedMessage$, 1023) // Get second message
IF RV=0 // Error
STDOUT "Client : Remote end has disconnected : RV = " + RV + "\n"
STDOUT "Client : Error was : " + NETGETLASTERROR$() + "\n"
SLEEP 4000
END
ELSEIF RV = -1 // Error
STDOUT "Client : Network error has occurred : RV = " + RV + "\n"
STDOUT "Client : Error was : " + NETGETLASTERROR$() + "\n"
SLEEP 4000
END
ELSEIF RV=-2 // Nothing waiting in the queue
STDOUT "Client : Socket not ready (returned -2). Sleeping for 1 second.\n"
SLEEP 1000
ELSE
STDOUT "Client : Message length received : "+ RV +"   Message recieved = " + ReceivedMessage$ + "\n"
ENDIF
WEND

STDOUT "When message received from server , press 1 to break connection\n"
Done=0
WHILE Done=0
IF KEY(2)=1
RV=SOCK_CLOSE(EstablishedConnection)
IF RV <> TRUE
STDOUT "Client : Error breaking connection : RV = " + RV + "\n"
STDOUT "Client : Error was : " + NETGETLASTERROR$() + "\n"
SLEEP 4000
END
ELSE
STDOUT "Client : Connection broken : RV = " + RV + "\n"
STDOUT "Client : Sleeping for 2 seconds.\n"
Done=1
ENDIF
ENDIF
WEND
SLEEP 4000

ENDSUB




Results
(I've copied the output in the order you'll see it from the two application windows)

I start the application and choose "s" to run the Server. It runs a SOCK_TCPLISTEN then loops with a SOCK_TCPACCEPT :
QuotePress s to be server
Press c to be client
Server
Server : Waiting for connection with TCPACCEPT...

I run the second copy choosing "c" to run the Client. It runs a SOCK_TCPCONNECT, establishes the connection, then loops on a SOCK_RECV waiting for data.
Quote
Press s to be server
Press c to be client
Client
Client : Establishing connection with 127.0.0.1 on port 1234...
Connection Established. EstablishedConnection variable = 0
Waiting for first message

The Server sends a 1 character string - "1" with SOCK_TCPSEND:
QuoteServer : Sending message '1'
Server : TCPSEND Return value = 1
When message has been received by client, press '2' to break connection

The Client receives the 1 character string "1":
QuoteClient : Message length received : 1   Message recieved = 1

Server : I press the "2" key which runs a SOCK_CLOSE and breaks the connection.
QuoteServer : Connection broken : RV = 1
Sleeping for 2 seconds.

Client : The client's looping SOCK_RECV gets a return value of 0 and determines that the link has closed. It runs a SOCK_CLOSE (so both sides now have shut down the link) and sleeps for 4 seconds.
QuoteClient : Remote end has disconnected : RV = 0
Client : Error was : 0 The operation completed successfully.

Closing socket...

Sleeping for 4 seconds....

Server : This is where the problem is. The server then runs the TCPLISTEN (doesn't return -1 which is the only error code listed in the manual). It then tries to recreate the connection with a SOCK_TCPACCEPT. This returns -1, the error is a non-blocking socket error.
Quote
Recreating connection with TCPLISTEN
Server Waiting for connection again with TCPACCEPT...
Server : SOCK_TCPACCEPT error
Error was : 10035 A non-blocking socket operation could not be completed immediately.

Client : And the other half of the problem The client then runs a SOCK_TCPCONNECT (which doesn't fail even though the server isn't listening).  The SOCK_RECV's that run afterwards all return a -2 return value (socket not ready)
QuoteClient : Reestablishing connection with 127.0.0.1 on port 1234...
Connection Established. EstablishedConnection variable = 0
Waiting for second message
Client : Socket not ready (returned -2). Sleeping for 1 second.
Client : Socket not ready (returned -2). Sleeping for 1 second.

The client then dies as the server program dies (breaking the faulty network link).
Quote
Client : Remote end has disconnected : RV = 0
Client : Error was : 10054 An existing connection was forcibly closed by the remote host.

Kitty Hello

Let me get what you do:
Code (glbasic) Select

   Sock=SOCK_TCPLISTEN(Net_PortNumber)                                    // Listen for connections on our socket
      EstablishedConnection=SOCK_TCPACCEPT(Sock,ClientIPAddress)               // Check for established client connection
[... loop ... ]
   RV = SOCK_TCPSEND(EstablishedConnection,"1")
   RV=SOCK_CLOSE(EstablishedConnection)
   // press "2"
   Sock=SOCK_TCPLISTEN(Net_PortNumber)     


Right?
See - you never closed the socket that listens to port Net_PortNumber.

A quick test seems to do it right now. Can you confirm that?



FutureCow

Hi Gernot,
What command did I miss to close the socket? I'm basing my variables to use to close the connection on where the manual pages use the word (variable) "sock"

For the server :
According to the manual page for SOCK_TCPACCEPT ,
Quotesock% = SOCK_TCPACCEPT(sock_listen%, BYREF ip%)
[/qtote]
From my code (line 57), I'm using the variable "EstablishedConnection" in the place of the "sock" variable -
Code (glbasic) Select
EstablishedConnection=SOCK_TCPACCEPT(Sock,ClientIPAddress)

From the SOCK_CLOSE manual page, you pass it your "sock" variable -
Quoterv% = SOCK_CLOSE(sock%)
so I then close it at line 84 using my sock variable 'EstablishedConnection' -
QuoteRV=SOCK_CLOSE(EstablishedConnection)
----------------------------

For the client :
From the SOCK_TCPCONNECT manual page
Quotesock% = SOCK_TCPCONNECT
Again, I'm using EstablishedConnection as my "sock" variable. Line 159 I create my connection :
Code (glbasic) Select
EstablishedConnection=SOCK_TCPCONNECT(Net_ServerIP$,Net_PortNumber)

Then I close it at line 178
Code (glbasic) Select
SOCK_CLOSE(EstablishedConnection

It looks right to me based on the manual pages.
Unless you're saying that you close the connection based on the "sock_listen" variable from SOCK_TCPLISTEN rather than the "sock" variable (in which case the manual is wrong).

OR do I have to do a sock_close for both the SOCK_TCPLISTEN and SOCK_TCPACCEPT variables?

FutureCow

Okay, I've done some research and I can see what's appears to be happening now. Can you please confirm if my new understanding is correct.

SOCK_TCPLISTEN creates a socket that will listen for incoming connections.
When a client tries to SOCK_TCPCONNECT to your socket, it places a connection request in your listening queue. By running a SOCK_TCPCONNECT you create a brand new socket which handles communications between your server and this particular client.
The original socket (opened by SOCK_TCPLISTEN) is still listening for connection requests.

So break a connection with a particular client you close their SOCK_TCPCONNECT socket. To stop your server listening for any new connection requests you close down the OTHER socket which was opened by SOCK_TCPLISTEN.

Assuming I have this right can I help rewrite the manual pages as it's not very clear.

Kitty Hello


FutureCow

Nope, not clear  :'(
I'll send you some suggestions probably Monday.