Welcome!

Java IoT Authors: Elizabeth White, Liz McMillan, Yeshim Deniz, Zakia Bouachraoui, Pat Romanski

Related Topics: Java IoT

Java IoT: Article

Java and The MahJong Server

Java and The MahJong Server

I had conceived of an Internet server for the Chinese game of MahJong a long time ago, but had never gotten very far trying to implement it in C and Xlib. In January 1996, I learned of Java, and decided to start anew. Java's unique capabilities: multithreading, portability, and AWT just to name a few, proved to be extremely useful for such a project. In three weeks, I got the alpha version running, and soon I had dozens of people playing on my site.

Just like okbridge and the chess servers, my application provides a virtual casino for MahJong fans on the Internet. It actually consists of two parts: a server and a client. The server is a Java application that keeps running twenty-four hours a day. The client is a Java applet that runs under a user's webbrowser. A web page on the server's host machine gives access to the client applet and provides documentation and other information to the user. In this article, I will explain the design and implementation of both the server and the client. There's nothing special about MahJong; the same principles can be applied to many other multi-user Internet applications.

Packets and Client/Server Communication
The nature of a client/server application requires data packets to be sent between the clients and the server. In a typical C implementation (e.g. in Netrek), we define several types of packets, each corresponding to a C structure, and use a byte tag to distinguish different packets apart. To send a packet through a socket connection, we simply coarse the structure into an array of bytes, and reverse the process when we want to read it back in. However, this approach is not possible in a strongly typed language like Java, because we cannot perform pointer casting. Therefore we must first get this technicality out of our way.

My approach is only a slight deviation from what we would normally do in C. Each type of packet is defined to be a class that extends a base abstract class Packet. Its field variables contain information the packet carries. We need two constructors: one takes as its arguments values for the field variables, the other a DataInputStream from which the packet should be read in. We also need a method that converts the packet into a binary form that can be sent out to a DataOutputStream and read back in by the constructor mentioned above.

I chose to use DataInputStream and DataOutputStream because they offer the ability to send binary data which reduces network traffic, and they also provide a pretty complete set of methods that deals with many commonly used data types like integers and strings. It is still tedious to call those methods for all the field variables for all the packet classes, but I see no easy way around it. It is possible, however, to speed up sending the same packet to multiple clients by using a format() method to write the binary data into a byte array first.

One question remains: how do we discern all the packets being sent to us, and how do we act accordingly? The answer is a packet dispatcher along with handlers for all the packets. The dispatcher is a thread that does an infinite loop reading in a single byte from the input stream. This byte would be a tag that marks which packet follows it. Then we can use a switch()statement to call the constructor of the actual packet to read it in, and call an appropriate method that handles that packet. There are also more elegant solutions, but this approach works well for our purposes.

Listing 1 shows some packet examples. We define here a packet that carries a player's identity and a response from the server.

The Client
Now we are ready to turn to writing the client. This is a program that interacts with both the user and the server, but actually it is the easy part of the whole application. Here I will only give an outline.

Unlike most Java applets, the MahJong client needs to live in its own window (Frame in AWT parlence). There are two reasons for this. First, a user is likely to spend a lot of time playing the game with other people, so we want to give him the ability to browse other web sites without interrupting his game. Second, the client needs to display a lot of stuff, including lists of all the players and tables, a communication panel and a big panel that draws all the tiles on his table. Therefore, we are going to need all the screen real estate we can get. Thus we are led to defining two classes: a Greeting class that extends java.applet.Applet and a Client class that extends java.awt.Frame. Since it's the Client class that does all the real work, we should also let it implement Runnable. See Listing 2 for a sketch of Greeting and Client.

The class Greeting simply presents a button and waits for input. When a user wants to connect to the server, he presses the button, and Greeting tries to open a socket connection to the server. We get the host name of the server from an applet parameter embedded in the <applet> tag in our main web page. Upon successful connection, Greeting creates a new instance of Client, and we are in business.

The Client object first spawns a thread from its run() method. The thread is none other than a packet dispatcher that reads in packets from the server and calls the appropriate handler methods in Client. Then we can open our new window (recall that Client extends Frame) with the complete GUI in it. In the sample code, I present the simplest possible login window. A real working client will likely have a lot more GUI elements and other classes.

There is a convention I want to recommend, that all user actions should only cause packets to be sent to the server for processing (possibly with some minimal error-checking on the client side). Only the packet dispatcher thread that reads server packets can update the status variables in the client. For example, although the client keeps a local copy of all the players and which tables they are on, when the user requests to join a table, the client only sends a join-table packet to the server. The server then processes this request and sends back a join-table packet to the client if the request is granted, and only then does the client change its internal data and graphic display to match the action. This convention vastly reduces the care that needs to be taken for client/server synchronization, and also makes future changes in the client/server protocol less likely to mess things up.

The Basic Server
The server is the central piece that ties everything together, so it's expected to be a very complicated program. However, we do have a lot of technical means at our disposal, notably Java's built-in multithreading capability. To begin with, we will first sketch an outline: the classes we will define, and what methods we need to operate on them.

First of all, we need a Server class, which will have a main() method to start the whole thing. It first creates a ServerSocket and listens on a predefined port number. Each time it accepts a connection, it will create a new instance of a Player class.

The Player class stores all the information about a particular player, like his socket connection, character name, total score, table, etc. In particular, we need a DataInputStream and a DataOutputStream, which we can figure out from the socket connection. To receive packets from the player's client, the Player class should extend Thread, have a run() method that is a packet dispatcher, and implement a bunch of packet handler methods. Let's call this thread the player's input thread.

We will also need to send server packets to each player's client, so the Player class should have an output (Packet p) method. Let's think for a moment what this method should do. The naive implementation is to just take the Packet p that we want to send out, and call its output() method with our DataOutputStream. But which threads will be calling Player. output()? And what might happen during the output process? Let's imagine that one player, A, wants to send a text message to a player B (this is actually not allowed on the MahJong server to prevent fraud, but there are many other similar circumstances). So player A's input thread reads in a text message packet and finds out it's intended for player B, and it proceeds to call B's output() method with a server-side text message packet. Thus, in effect, any player's input thread could call any other player's output thread. However, this may cause a problem, because as we know, writing data into a socket can take a long time to finish, or may not finish at all. Thus A's input thread may be blocked indefinitely trying to send packets to B.

My solution is to introduce another class per Player, namely PlayerOutput, that also extends Thread. Let's call it the output thread. Each output thread maintains a queue of preformatted (i.e. converted to byte[] by the format() method) packets to be sent out. The output thread simply waits for a packet to appear on its queue, pops it off the queue, sends it out, and repeats this process. Now, each Player's output() method simply pushes the packet onto the associated PlayerOutput's queue. Writing the Queue class is a simple exercise using linked lists and monitors. We present a version in Listing 3.

The MahJong server provides a bunch of MahJong tables, each table supporting four players and several spectators. We can make a Table class to encapsulate all the table-related data and methods. A typical method of a Table object is to handle some game play made by one of the players on that table. To simplify things, I chose not to implement Table as a Thread. Instead, it's the various Player input threads that read in game-play packets and call the appropriate Table methods to handle them. To make this plan work, the various Table methods should be declared synchronized, and take only a short time to execute. This last requirement is easily satisfied for games like MahJong, bridge, or chess, where the game status can change only when some player makes an action, and the exact effect of a play can be easily figured out. If you are writing a real-time game where the game status changes independently of player actions, you will need to use a dedicated thread that updates the game status periodically. Now we basically have a skeleton of the server. We show it in Listing 4.

Making a Robust Application
We will now explore some more subtle issues. They mostly have to do with synchronizations and dead socket connections. If we don't take care of them, we won't be able to run our application on the Internet for an hour before either the server or the clients crash!

Here's the first problem. In the server, we keep a list of all the players connected to our server. This is simply an array (or Hashtable) of Player objects. Now, when some player joins or leaves our server, we need to update this global list. We also need to occasionally send all the players' names to some player, or perhaps send a particular packet to everyone. Theoretically, these operations may happen at the same time in many threads. There is a similar situation for the list of all open MahJong tables.

The reader may recognize this as the classical "reader-writer" problem: we have a document (the list of all players), many readers (threads that try to send packets to everyone or query everyone's name,) and many writers (threads that handle players joining or leaving the server). An arbitrary number of readers are allowed to read at the same time, but reading and writing are mutually exclusive, and all the writers are mutually exclusive. It's also a good idea to give writers higher priority than readers.

We solve this problem with a class RWMonitor, that solves the "reader-writer" problem in general. The code is in Listing 5. To use it on the player list, for example, we need to create a new RWMonitor object first, call it rw_mon. Whenever we need read (or write) access to the player list, we first call rw_mon.inRead() (or rw_mon.inWrite()) to enter a critical region. Then we can proceed, but remember never to modify the player list in a read-only critical region! After we are done, we simply call rw_mon.outRead() (or rw_mon.outWrite()) to exit the critical region. Listing 4 shows some typical usage of the RWMonitor class.

Granted, our MahJong server spends most of its time waiting for packets to arrive, but race conditions could occur, and it almost certainly will if the server is to run for an extended period of time. It's never a good idea to ignore possible synchronization problems, however unlikely they are to happen.

The other problem I want to mention has to do with dead socket connections. As we know, the Internet is far from being robust. Routers probably go down at a rate faster than car accidents occur. Now it would be all the same if Java's socket library could tell us that some link is dead as soon as it happens, but unfortunately it doesn't all the time. On the alpha version of my server some dead connections simply didn't get reported to my program. No exceptions. The read and write calls simply hang there. And in Java, there is currently no way to specify a time-out on read and write calls.

There is a solution using so-called ping packets. These are packets that are sent from the client (by a dedicated thread) regularly, say at intervals of thirty seconds. When the Player object on the server side receives any packet, it always increases a ping counter. This way, even if the user is idle, his ping counter always increases at least once every thirty seconds. On the server side, we make another Thread, called WatchDog, that is usually sleeping but wakes up once every minute or so, to check the ping counters of all the players on the server. If some player's ping counter is zero, that means the connection between the server and the client must be dead, so our WatchDog proceeds to clear this player's slot. In any case, WatchDog will reset all players' ping counters to zero before it goes to sleep again. We won't go into more details here, as the implementation of such a method depends on the actual client/server protocol. Sample code can be found in my MahJong Server distribution.

This method has the added bonus of being able to gauge a user's idle time. If a player is on a table playing a game, and the ping counter reaches two before we receive any game-play packet from his client, we know that the player is probably answering the phone or caught by his boss, and we can choose to kick him off the table to let someone else on.

The last advice I want to give may sound like a cliche. That is, always follow the practices of object-oriented programming! Do not save on classes. Put methods where they belong. In my beta version I had a persisting dead-lock problem. Eventually I tracked it down to a synchronized method that really belonged to another class, but I had put it in the wrong place in order to save myself some typing!

I hope my article can help you write your own Internet game. The source code listing is brief but self-contained and runs as is, so it can be used as a starting point. Happy Java programming!

The MahJong server: has kept moving for various reasons. When I do find a permanent home for it, I'll put a link in my personal home page. The source code is available under GNU Public License (GPL) at "http://ftp.princeton.edu/pub/MJ_dist.tar.gz".

More Stories By Thomas Z. Feng

Thomas Z. Feng is a Ph.D. student in the Mathematics Department at Princeton University. He is working on Algebraic Geometry, and loves to write programs for fun.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


IoT & Smart Cities Stories
Charles Araujo is an industry analyst, internationally recognized authority on the Digital Enterprise and author of The Quantum Age of IT: Why Everything You Know About IT is About to Change. As Principal Analyst with Intellyx, he writes, speaks and advises organizations on how to navigate through this time of disruption. He is also the founder of The Institute for Digital Transformation and a sought after keynote speaker. He has been a regular contributor to both InformationWeek and CIO Insight...
When talking IoT we often focus on the devices, the sensors, the hardware itself. The new smart appliances, the new smart or self-driving cars (which are amalgamations of many ‘things'). When we are looking at the world of IoT, we should take a step back, look at the big picture. What value are these devices providing. IoT is not about the devices, its about the data consumed and generated. The devices are tools, mechanisms, conduits. This paper discusses the considerations when dealing with the...
IoT is rapidly becoming mainstream as more and more investments are made into the platforms and technology. As this movement continues to expand and gain momentum it creates a massive wall of noise that can be difficult to sift through. Unfortunately, this inevitably makes IoT less approachable for people to get started with and can hamper efforts to integrate this key technology into your own portfolio. There are so many connected products already in place today with many hundreds more on the h...
SYS-CON Events announced today that IoT Global Network has been named “Media Sponsor” of SYS-CON's @ThingsExpo, which will take place on June 6–8, 2017, at the Javits Center in New York City, NY. The IoT Global Network is a platform where you can connect with industry experts and network across the IoT community to build the successful IoT business of the future.
CloudEXPO New York 2018, colocated with DXWorldEXPO New York 2018 will be held November 11-13, 2018, in New York City and will bring together Cloud Computing, FinTech and Blockchain, Digital Transformation, Big Data, Internet of Things, DevOps, AI, Machine Learning and WebRTC to one location.
Andrew Keys is Co-Founder of ConsenSys Enterprise. He comes to ConsenSys Enterprise with capital markets, technology and entrepreneurial experience. Previously, he worked for UBS investment bank in equities analysis. Later, he was responsible for the creation and distribution of life settlement products to hedge funds and investment banks. After, he co-founded a revenue cycle management company where he learned about Bitcoin and eventually Ethereal. Andrew's role at ConsenSys Enterprise is a mul...
DXWorldEXPO | CloudEXPO are the world's most influential, independent events where Cloud Computing was coined and where technology buyers and vendors meet to experience and discuss the big picture of Digital Transformation and all of the strategies, tactics, and tools they need to realize their goals. Sponsors of DXWorldEXPO | CloudEXPO benefit from unmatched branding, profile building and lead generation opportunities.
Disruption, Innovation, Artificial Intelligence and Machine Learning, Leadership and Management hear these words all day every day... lofty goals but how do we make it real? Add to that, that simply put, people don't like change. But what if we could implement and utilize these enterprise tools in a fast and "Non-Disruptive" way, enabling us to glean insights about our business, identify and reduce exposure, risk and liability, and secure business continuity?
The best way to leverage your Cloud Expo presence as a sponsor and exhibitor is to plan your news announcements around our events. The press covering Cloud Expo and @ThingsExpo will have access to these releases and will amplify your news announcements. More than two dozen Cloud companies either set deals at our shows or have announced their mergers and acquisitions at Cloud Expo. Product announcements during our show provide your company with the most reach through our targeted audiences.
DXWorldEXPO LLC announced today that Telecom Reseller has been named "Media Sponsor" of CloudEXPO | DXWorldEXPO 2018 New York, which will take place on November 11-13, 2018 in New York City, NY. Telecom Reseller reports on Unified Communications, UCaaS, BPaaS for enterprise and SMBs. They report extensively on both customer premises based solutions such as IP-PBX as well as cloud based and hosted platforms.