Into Java - Part XXII

From EDM2
Revision as of 13:26, 21 March 2018 by Ak120 (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Into Java / Part
I II III IV V VI VII VIII IX X XI XII
XIII IV XV XVI XVII XVIII XIX XX XXI XXII XXIII

By Simon Grönlund

This time we will start off with a brief look at sockets, what are they and how do we use them? How do common sockets differ from server sockets? And with some knowledge on that topic we will implement our own chat server which you will find running at the [Swedish OS/2 User Group] at port 2015 as you read this - thanks to them for their kindness. You will have to wait until next month for the chat client we will make. How to use the server then? Read on and find out.

We will also use the key word synchronized that is commonly used when multithreading is in use. And how will we make a decently stable server that can handle network errors such that the Internet connection fails for a long period?

Sockets

A socket can be considered an object in between your application and the underlying networking package of the operating system. A socket is considered a software object of the operating system. It is always software and the operating system takes care of opening a socket for you, connecting to an exterior resource, and shuffling data to and from that resource. Hence a socket may be considered an interface to the network. As a developer you need not know how protocols are set up and used, how connections are initiated, used and closed, how reliable data transport is maintained, etc. Using Java you simply create a socket and use the methods available, similar to how you create a file stream to read from or write to.

A socket needs to know which host to connect to, and also which port to use. In Java there are six different constructors to use, but only one differs from the host and port scheme, that is the plain constructor that cannot be connected as such. Normally you do not need to include your own location since it is set by the operating system, but there are two constructors that can take local parameters.

A server socket does not differ that much from a plain socket, it is also a software object but it listens for users connecting to it from the network. Once a user connects, the server socket must itself create another plain socket that will take care of that single connection. Hence a busy server may have several plain sockets to serve. Obviously a server needs to be multithreaded, which Java handles well. Of course a very busy server must scale well, but that is another topic that I will not discuss in this instalment, except to state that Java handles scaling very well, better than most competing platforms.

A server socket is normally set up using a specified port, so we will use 2015, and the host will be the machine that it runs on. You are free to decrease or increase how many simultaneous connections the server may handle. It is wise not to set it too high from the very start and to slowly increase that number over time. The default is 50 connections, the number we will use.

For a little more insight on sockets you might want to read what Sun says about them on their Java tutorial pages.

The Chat Server

Java22a.png
First we sketch how a chat server works.

The main part is the server socket listening for users to connect. A user that connects and is accepted is given a new plain socket on the server side that will communicate with the user side socket. Every time the user and the server communicate, these two plain sockets do the work at each side of the network.

The server socket is mainly listening for new users. During a short, initial time it does some work, when it creates a socket representing the user and then starts over listening for new users.

In a broadcasting chat server we need a data structure to hold the sockets. The very fastest structure is the list, that adds a new item in constant time. It takes on average (number-of-items/2) to remove an item though, since you have to traverse on average half of the items to find the one to remove. The removal is still done in constant time.

A hash table is faster to remove items, it does that in constant time, and it adds items in constant time. A Java Vector can add an item in constant time, adding it at the end. But finding an item to remove takes the same time as with lists, but furthermore it has to move every item "after" the removed item one step toward the front of the vector. To remove one item from a Vector the work sums up to the total number of items.

Anyhow, since I want this chat server to work both for Java 1.1.8 and Java 2 users I settle with the java.util.Vector. Furthermore the Vector class is synchronized in itself and hence very suitable for our purposes. Other times synchronization might be bad since it causes unwanted overhead.

So far we have found that we need a vector that will hold the server side sockets. And we have to make such a socket class. But why not use the ready made java.net.Socket? Since they are plain communication objects and do not know how they are really used, this time in a chat server, other times in clients and so forth. In a server these socket objects must be threaded so that they can listen for user input themselves. Hence a server with 10 simultaneous users have at least 11 threads running, one thread for each user plus itself.

Since this will end up a simple chat server, every message will be broadcast to all other users. The socket in question will send its' message to the other ones by traversing the server's vector and repeatedly sending the message to each, one by one, including echoing to its' own user.

Let us start with the server class and its' constructor.

Java22b.png

The Vector class resides in java.util, hence the import. Only for ease of modelling I have put the ServerSocket in a class of its' own, the ServerDaemon. That is not necessary this time because when the Server class is instantiated and the main thread has launched the thread of the ServerDaemon object it does no more work and the daemon takes the workload.

The constructor can take one parameter - a port number. Remember that Java 1.1.x does not have Integer.parseInt so we would need a workaround. Or you may omit the parameter option completely.

That I use port 2015 is arbitrary. Any free port is okay, but most of the time port numbers just above 2000 are used for external connections. The vector is initiated to 50 only because the server socket default number of connections is 50, and this way we do not need to resize the vector at runtime.

Let us continue with the next little method.

Java22c.png

If there was no trouble starting the server socket this is it. Else the server will exit with an error output. We send this as argument to the ServerDaemon constructor since we will add a few methods useful to the daemon object and to all of the server side sockets here. Let us continue with them.

Java22d.png

With the code comments these methods are pretty obvious. They handle new connections and remove them when they are gone. Sockets are added by the server socket upon connection, but they are removed by the sockets themselves. When a socket closes because of the user, a network error or whatever, the socket class will know and we use that to let it remove itself as a last action before it is disposed.

We continue with one of a chat server's most important functions, the message delivery part.

Java22e.png

Today we add only the ability to answer the common "who?" question, commonly spelled wwhhoo. If we see this we traverse the entire vector and ask each connected socket for its IP information. Such information is not to be broadcast, it is sent only to the asking individual. But if a plain message is received it will be broadcast.

Java22f.png

The message is broadcast to every connected user held in the vector, and hence echoed back to the sender too. We must remember to add a sendMessage(String) method in the server side sockets though.

With all these methods this class is done and we may add a tiny main method. It will simply instantiate a server object, then instantiate the server daemon.

As we have seen this class is more or less only a holder for the server side sockets. To it we have added two message handling methods. Note that we have used synchronized three times, with the loggedOf, handleMessage and broadCast methods. Why not the addMySocket? Since that method is only called from the server daemon object and cannot be called twice at the same time. But all of the others can at any time be called from any of the sockets, even simultaneously. Hence we need to block the entrance to the methods so that only one at a time can be served. $Heading T="The ServerSocket"> The ServerDaemon will only consist of one constructor and one method. Since it is going to be a thread of its' own the method must be "run". Let us start.

Java22g.png

Two common import lines are used. Next we see that this class will implement the Runnable interface. As said before, we could have extended the Thread class, as we will do with the MySocket class. But extending and implementing do not differ much and the workings are basically the same.

The constructor initiates the member variables. If something goes wrong with the ServerSocket creation, an exception is thrown and the server will exit. Finally we start the thread, that will make the JVM call the run method.

This time we will not start from the top of the method, but from its' innermost side. That is because the outer part is added later on for server stability, but the real work is done in the midst of the object.

Java22h.png

A server is mainly an eternal loop that listens for newcomers. When the server socket accepts a new connection it creates a socket that encapsulates the data necessary to communicate with that user. This new socket may be used directly if so, but we wrap it with a class of our own, the MySocket. Since that object need to communicate with the Server object, mainly with its' message handler, we need to give that object as an argument to the new socket wrapper.

Finally we add the new socket to the vector of connected sockets. The server socket is now ready for the next one to connect. The accept method is blocking, that is it is waiting for something to happen but does not use CPU during that waiting time. These things are the essence of a server socket.

But anyone can see that this code can cause trouble. What happens if the network dies? Or if we fail to create a new socket? Or if we fail to instantiate a new MySocket object? All of these things can raise an exception, hence we have to encompass this loop with a try/catch block. Let us make it.

Java22i.png

The first time we enter this block the sock is not null, but as you can see at the bottom line of the image it is set to null if anything goes wrong. We do not care about what kind of trouble it is, we simply set to null after an error output. But what will start this block over again if the innermost loop failed?

We must have an outer loop that will let the server start over. BUT, there is a real but! Is it worth starting over at once? If a network error continues a while, that outer loop will go on and every turn we try to instantiate a new ServerSocket, that maybe can not be done. I present one simple but easily understood solution, aware that there are other, better algorithms around.

Java22j.png

This will be the final run method from start. Let us define a minimum uptime that can be considered the minimum time the server runs "normally". Any time less than five minutes must be named bad behaviour.

Further we count how many turns we make in the outer loop and stop the server if hundred erroneous turns occurs in a row. To measure how long the server has been up we need to know when a turn started. We are not interested in the time presented in GMT or so, but any number representing the time, hence the system time will do fine.

This is okay as long as no error occurs, but more than setting the sock to null, what is to be done?

Java22k.png

There are two cases, either the server has been running less than the stipulated uptime - erroneous behaviour. Otherwise it has run normally for a long time and the error might be momentary. The latter case is the easiest, we simply reset the stopLoop variable and let the while loop run another turn, trying to set up a new server socket.

The erroneous case is somewhat more complex. If the running time was shorter than the five minutes uptime there is most probably not a momentary error. We know for sure that the server broke a few moments ago, either when we first hit the else clause or after another turn in the while loop. Hence it is better take a nap and see if the error vanishes by itself.

How long will we sleep then? After updating the stopLoop variable, why not sleep as many minutes that we have turned around in the while loop with this error. Adding 1 + 2 + 3 + ... + 99 is 4,950 minutes, that is slightly less than 3.5 days to fix this error. Since such an error most probably is a network error the technicians will learn about that in one way or another. The drawback is that we maybe have to wait a rather long time after the broken network went up again. But on the other hand, with a server broken for a while a few minutes more or less is not that bad, is it?

Using a slowly increasing punish time is commonly used on the Internet. For example a "flapping" router, one that goes up and down very often, is punished with blinding it out, but on an exponential time curve. Such a router can be hidden from the Internet for several days only for some minutes of intense flapping, no matter the cause.

Anyway, after a nap we try another turn in the outermost while loop. But if a hundred loops, that is 82.5 hours, is not enough to cure the error we simply quit the server.

The main reason that we take a nap is twofold, two errors in a row indicates a more severe error. Note that a single occasional error does not cause a nap. Severe errors are not cured instantly and it is better to wait a while. Second, a constantly running a loop consumes 100% CPU, and such behaviour is not wanted from any application. Thus, it is better to wait for a while.

Now we are done with the server and its' server socket. Both these classes uses the MySocket class that we still have not seen.

The MySocket class

Since each server side socket is representing the user and a user can send messages to it at any time, we need to have these sockets threaded. That way users can call their sockets simultaneously. Of course, since some crucial methods of the server are synchronized, only one socket at a time can communicate through these methods, and the messages are not messed up. Let us make a threaded socket then.

Java22l.png

The MySocket objects have to know about the server to be able to uses that object's methods. And the socket was given us from the server socket.

Any threaded class, extending Thread or implementing Runnable, has to have a run method. Let us continue with that one.

Java22m.png

The first thing the run method does is to greet the newcomer and give two useful commands to the user. Next comes a try block encompassing the buffered reader that wraps an input stream from the socket.

The next part of the code is rather well known to us, a loop that runs as long as there is more to read. But we must handle the logout command, that is the only way of breaking the loop, the socket is closed after that and we notify the server to remove us from the vector of connected users. Further it is useless to broadcast empty lines.

Any exception is ignored. The main reason is that if anything happened, a simple chat server should not do so much about that. The user can try to start over. This is the entire run method, the reading loop.

Recall though that we have seen two other methods of this class used in the Server class. The command 'wwhhoo' causes the server object to send the IP information of all connected users, then using the getIP method of each MySocket object in the list. In fact it is the thread of the MySocket object that reads that command that is in control of that work, though inside other objects.

When a MySocket object receives a chat message that socket has to send its' message to all other connected users, using the handleMessage method of the server object. But that method simply makes a call to every MySocket object in the list and then uses a sendMessage method. That method is also used upon the initial greeting time as seen in the run method.

Let us make these two final methods then. Java22n.png

The getIp method simply returns a string with the host's address as well as the host's name.

The sendMessage method creates a PrintWriter wrapping the output stream of the socket. Use autoflush so the message will be delivered promptly and not when the system decides to. And we do not bother with errors this time.

This little neat class will now handle a connected user, giving him entrance to the broadcast method of the server, and handing him messages from other users.

Putting it Together

Since Java is this powerful on networking, it is not any harder than this to set up a small chat server of your own. We haven't made much about taking care of different kind of errors that can occur, but it is still pretty stable. Better is that it is secure, no evil code or commands may be used to get to the system. That is because it only delivers text messages to and fro. Except from the 'wwhhoo' and 'logout' commands.

Now it is time to compile and smile. But this time we will go one step further before running it. Consider you want it to run on a remote machine, it is possible to copy three class files to that server, yes. But is that so handy? No, it is better zipping the class files in a JAR file. Such a file is both compressed and takes as many files and directories you want it to. The chat server may be run from that JAR file and need not be extracted.

Here we make that file:

[C:\java\bin\]jar cvf Server.jar *.class

The first part of the line is optional and depends on your environment PATH. I also presume that you do only have the server class files around and no other ones.

Furthermore we better add a manifest file of our own. Any JAR file has a manifest file, but by manipulating that file anyone can start the application hidden inside a JAR file without knowing which class has the main method. We simply add information to the manifest file that points to that class. A manifest file is named MANIFEST.MF and looks similar to this:

Manifest-Version: 1.0
Created-By: 1.3.0 (IBM Corporation)

You may extract it from the JAR file by this command:

jar xvf Server.jar META-INF/MANIFEST.MF

Now open that extracted file in an editor and add one line in between so the manifest file looks similar to this:

Manifest-Version: 1.0
Main-Class: Server
Created-By: 1.3.0 (IBM Corporation)

Note that the middle line now points to the class that has the main method. Save this in the same directory as the JAR file, e.g. with the name MANIFEST.MF. Now we have to update the manifest of the JAR file. Updating a JAR file is simple, and when it is the manifest you simply add a 'm' to the option list:

jar uvfm Server.jar MANIFEST.MF

Updating the JAR file with one or a few class files is also simple, omit the 'm' and name the file(s) to add one by one. Now any Java 2 user can start the server with this simple command:

java -jar Server.jar

Older versions must be given a classpath and use the jre instead of java:

jre -cp Server.jar Server

If you are using the older versions you naturally do not need to update the manifest file, but you never know if you will send the file to someone else don't you? Also note that the JAR file now is only 73 % of the size of the three class files together.

How to use this server?

Next month we will do the GUI chat client but you want to chat today. Open up a OS/2 terminal window then and type

telnet os2ug.se 2015

and voila! there you are. Is there any other user around? Then you can chat 'til you drop. Otherwise you can use two telnet windows at the same time and chat with yourself between these windows.

Testing the chat server at home you can start it and telnet to 'localhost 2015', presuming you have set up your machine's localhost correctly. Of course you can start this server at any convenient place, but to get other chatters around you have to give them the server name or its IP number.

As I have said, in December we will build a GUI chat client. January we will make that client an applet and from then on you can always use the same code both for applets and applications without extra effort.

Beyond that I find this Into Java column is becoming more and more advanced so I plan to make the January instalment the last one. BUT anyone having a good idea of small on-line games or tools is welcome to make a suggestion. As you will see next month the server is very well suited to handle more than chat messages, hence any on-line game can use this server.

I hope to find you around some day, or night, and we might chat for a while.


Into Java, Part 22 Amendment

To my embarrassment I found a rather severe bug a few days after I started the simple chat server at the Swedish OS/2 User Group's server. Not that the bug will cause any damage to anything, but the server will not function properly after a while. I am sorry I overlooked one of the basic mechanisms of the Internet and how sockets work.

A socket closes by request from user, using sock.close(). In our case a user can use the command 'logout' to result in a closed socket and be removed from the list of connected users. If an I/O error occurs it will result in a closed socket, and it does not matter if it was the server thread or any of the MySocket objects that fails, a socket will be closed. What happens if you simply close the OS/2 terminal window? Naturally your telnet session will die, but the OS is most often smart enough to close the socket itself. The same holds if you shut down the server, either by Ctrl+C or you close the terminal window.

When a socket closes it sends the opposite to a hand-shake, we can call it a good-bye-shake, that notifies its' friend socket at the other side of the network that it will close down. The friend server acknowledges that and pretty soon the connection really is closed. Hence both sockets are closed gracefully, and in our case the MySocket object will be removed from the list. So far our simple server works as expected.

But what will happen if anything unexpected occurs? Such that you close the dial-up connection without logging out first? Or if the network is broken and no good-bye-shake can take place?

Any server must react to a disappeared connection. There are several techniques for that, most of them using some kind of time-out schema. I now append such a technique to our server.

The improved server, version 1.1

Recall that we have one thread free to use, the main thread that went inactive (not sleeping though, it is runnable but inactive) after creating the entire server. Why not use that thread to do some useful work? We may implement another method that regularly will check for inactive users. That method can be called by the main thread as its' last action. Let us start.

Java22xa.png

We begin with a local variable that we might set to a selected value - why not ten minutes? - and an eternal loop. Since we want it to regularly clean the list of connected users from inactive sockets we let the thread sleep that time to start with. Having the main thread sleeping is not dangerous unless there are tasks that it should take care of. Here there is no such task.

Every time this thread wakes up it first finds out the actual system time. From the current time we subtract a value representing the time that shall be considered maximum inactive time. We use the sleeping time. An example: actual time represents 11.23.00 and from that we subtract 10 minutes, resulting in 11.13.00. Any socket active after 11.13.00 will be considered active, else it is inactive. We send the 11.13.00 as parameter to the isInactive method.

The for-loop continues traversing the entire list of connected sockets and asks each object if it is inactive. And after that the thread is done. Since the Java Vector class is inherently synchronized this operation is entirely safe.

Note that we only update i if the object is considered active, why so? Since if the object is inactive it will automatically cause its' own logging off (we will see that soon) and thus remove itself from the list. And if any item is removed from a vector its' next neighbour will take the removed item's index. Vectors work that way, but not lists. Hence if an object is removed we need to check that index again.

If you forget this it might not be a disaster, not in our case anyway. That object object passed over will probably be checked the next time and you really must have bad luck to pass over objects over and over again. Though. other times you really have to check every item.

Note the new line in main, the call to this new cleaner method.

The improved MySocket

In the code above we introduced a new member method in the MySocket class, the isInactive method that obviously returns a boolean. And I said that it shall remove itself automatically if it is considered inactive. We continue.

Java22xb.png

This method receives the current system time reduced with some set time as argument and it checks that time against another variable, the lastTime, that I introduce in a moment. If last time this object was active was too far back we try to close this socket. That will break the blocked readLine in the run method. And that will make the reading while-loop to break and call the server's loggedOf method (yes, I have seen the misspelling now, <grin>). Hence an automatic removal from the list takes place. Finally we return a true, this object was inactive too long.

If this object may be considered active we simply do nothing more than return a false, it is not inactive.

Naturally we might change the method's name to isActive and implement it the same but swap the return values. That is not important.

More important though, what time can be considered adequate to tell a socket active or inactive? Is ten minutes, as we chose in the server, an appropriate value? Anyone a silent listener will be thrown out very soon then. We have to find a balance between silent listeners and not having too many sockets on the list of connections. As usual you are to judge.

Now we continue with the variable lastTime. As expected we have to add one member variable to the other two variables, and one line in the constructor:

Java22xc.png

We need to set the new variable to a useful value since it may very well happen that the server wakes up and does its' cleaning before the user actually managed to send anything. If so, the socket will be closed almost at its' setup time. But this variable later on needs to get updated.

Java22xd.png

Any time the user sends a message to the server the variable will get updated, and another set time period will keep the object considered active. But look, by setting it very first in the reading loop a silent listener can keep silent but still notify the server now and then. How? By simply sending an empty line, that is no character at all, only an ENTER now and then. Every line with length zero will simply not be broadcast, but it will still update the variable.

With this I feel pretty comfortable. We have a simple chat (or consider it a message) server. It is pretty stable, not 100 % error free, but still handy for its' purpose.

The only thing I am not satisfied with, though I can not influence it, is that I have found a nasty shortcoming with the Java's Thread.sleep method. This method causes the thread at hand to go to sleep, but in some occasions it will never wake up. This may happen when the system time is changed, e.g. by those cute time setting apps polling Internet time servers.

There must be workarounds to this problem though I haven't had the time to search for them. Anyone that knows can make my day with an e-mail.

CU at telnet os2ug.se 2015


The complete IntoJava archive (updated a few days after each new issue is published.)