The last topic showed us how to try out our server program. It also showed us that it's a bit of a pain to make our server program end.
So, why don't we make a quick improvement to our server program? We'll set it up so that if the person's name is 'quit', the program shuts itself down. This should be easy to do. It requires some code to be added in two places in the program:
In the "TalkToClient" subroutine, after the 'RdLine' group of code, we'll add a simple if, like this:
c if line = 'quit' c leavesr c endif
And in the mainline of the program, if the user's name was 'quit', we need to close the socket that listens for connections, and end the program.
Right after the line that says 'callp close(csock)', we add this:
c if line = 'quit' c callp close(lsock) c return c endif
Now, re-compile and run this program again. Test it with telnet. When the server program gets a user whos name is NOT quit, it should work as it did before. When the user's name *is* quit, it should end.
There is a problem with this, however. After entering a user-id of 'quit', the server program ends as it should. But if you immediately run it again, you get this error:
``bind(): Address already in use.''
But... it's not in use... Take a look at NETSTAT! In fact, if you wait two or three minutes before running your program again, it works just fine.
This is one of the most common "gotchas" in socket programming. The same port cannot be opened by two different programs, without taking special actions to make it work. Even though the socket from the first run of our sample program has already been closed, the system still waits on that socket for a period of time, before allowing you to re-use it.
The reason it waits has to do with how the TCP protocol works. TCP is a 'reliable' protocol, it ensures that anything sent by one side of the connection gets received by the other side of the connection. It sends the data in chunks called 'segments'. Each time a segment is received, the receiving side sends back an 'ACK' (acknowledgement) datagram, so that the sending side knows that the data was received. If it never gets an ACK, it re-sends the data.
When a connection is closed, each side sends a 'FIN' (finished) datagram to the other. Once both sides have received a 'FIN', they know that the connection is closed.
So, like everything else in TCP, after a FIN is received, the side that received it sends back an 'ACK'. But how does it know that the ACK was received? You can't acknowledge an 'ACK', that would create an endless chain of ACKs going back and forth.
So how does the side sending back the 'final ACK' know that the ACK has been received?
The answer is... it doesn't. But it needs to wait a period of time, just in case the ACK got lost, so that it can re-send the ACK in the event that it never got received. (It will know that the ACK never got received when the FIN datagram is re-sent) RFC 793, which is the standard for how TCP is to work, tells us that the socket which sends the 'final ACK' is to wait for twice the Maximum Segment Lifetime (MSL) before closing completely.
Getting back to the original question, each time you close a socket, it must wait until the (MSL x 2) has passed before it can completely close the socket. During the time that it's waiting, the AS/400 will show that socket as being in 'TIME-WAIT' state. Since the socket that's in TIME-WAIT state is still using our port! And, by default, two sockets can't have the same port open! And THAT is why you receive the "Address already in use" error.
Fortunately, there is an easy fix. Each socket has many different options that can be set using the setsockopt() API. One of those options is called "SO_REUSEADDR", and it allows a socket to share the same address and port as an existing socket. If we turn this option on, we will be able to re-bind to our port immediately.
Many people have asked me, "isn't it dangerous to allow more than one socket to bind to the same port? What if two different programs tried to use the same port at the same time?"
It's not as dangerous as you might think. Although it's possible for two programs to bind to the same port this way, it's still NOT possible for two programs to LISTEN on the same port. Therefore, if you had two server programs trying to bind to the same port, the second one would still get an error, because the first one was already listening.