Java and Client/Server
Client-server applications are everywhere. Client-server can be defined as a process which provides services to other processes. The client and server can be run on the same machine or on different machines on opposite sides of the world. A non-programming example of a client-server situation is the telephone system. You are the client (or customer) and the central office is the server. By having a telephone connected to the system (and your bill current!) you are subscribing to the services that the central office provides. Requests are made of the server (central office) to place and receive calls. The server also does accounting on each call made or received and handles emergency (911) requests. In this article I will present a simple CB (citizen's band) radio simulator which was written for a class project. The server is written in C and the client is written in Java. I will assume that the reader understands what sockets are and has a rough idea of how they are used.
The specifications for the project were loosely:
Provide a server which can accept multiple simultaneous connections. The server should have a basic command set that will open and close the connection, change the channel and provide a list of current clients on a specific channel.
Provide an “emergency channel” (9) that will broadcast all traffic to all currently connected clients regardless of which channel they are subscribed to.
Client should “come up” on channel 19. This is the channel where CBers meet. They then agree which channel to move to in order to continue their conversation.
The client should display all traffic on the current channel with the handle of the person who sent the message.
The server can be either a single process concurrent or multiple process server (more on this later).
Besides getting extra credit for doing a graphical user interface, I chose Java in order to simplify programming of the client. Java is used specifically for the following reasons:
Portability. I was developing code at home on my Linux system and running the code on Suns at school.
Functionality. Not just for web page animations, Java is a very useful programming language. Java is object-oriented and very similar to C and C++. A simple user interface is relatively easy to write.
Threads. Java allows multiple threads (like a background process) of execution. A thread can be launched that will listen for incoming messages. When a message comes in, it is automatically sent to output. We start this thread and forget about it.
Can be run remotely from Netscape (or similar browser). While I have not currently implemented this feature, conceivably, web surfers looking at your page could talk to each other using the CB simulator. There are many restrictions to this which we will go into later.
Using C for the client would require more programming to accomplish the same results. First, some sort of GUI builder like Motif or X-Forms would need to be used. I'm not knocking any of these, but not every system has them and they can be difficult to learn and use. C does not have threads, so all I/O would have to be polled. User input as well as incoming messages would have to be polled and processed accordingly. Without a GUI, some type of command codes would have to be developed for the user to control the client and server. This would probably be very cryptic and difficult to use—not to mention difficult to implement.
I developed the server first, from the specification in Table 1. Messages are fixed length and must not vary from the given format. C handles transmissions well through the use of structures and pointers; basically, you just call a write or read routine, passing a pointer to the data structure, and the bytes come or go without much of a problem. This works fine for C; Java is another story.
Sockets work almost the same in Java as their counterparts in C. Since Java is object-oriented, you must create an instance of the socket object. This is done by a simple line of code:
Socket s = new Socket(hostName,portNumber);
where s is the instance of type Socket and hostName,portNumber are the name of the host and port to connect to. But a socket by itself is not very useful without a data input and data output stream. The code segment below sets up a data input and output stream to talk to the socket:
dis= new DataInputStream(s.getInputStream()); dos= new DataOutputStream(new BufferedOutputStream(s.getOutputStream()));
The output stream is created as a buffered output stream. Data will not be written across the socket unless either Java feels that there is enough data to write, or you force a write—using a flush by using something like: dos.flush(); this calls the flush method on the data output stream. On the reading side of the socket, we can simply go into an infinite loop and poll for data from the server, since the listener is running as a separate thread.
Java has most of the same basic data types as C, with a few exceptions. Java has no unsigned integers, but it does have true booleans. To construct the data packet, use a combination of Integers and an array of 120 bytes for the handle and message fields. The data output and input streams have methods for reading and writing integers and bytes. For example, dos.writeInt(1); would write the integer “1” to the data output stream. Conversely, for (int i=0;i<120;i++) dos.writeByte(buffer[i]); (or dos.readByte(buffer[i]) to read) would write the entire buffer to the socket; dos.flush() will make sure that the data is written now and not delayed. It is important to note that we must write or read all of the data (command, channel, handle and message) to or from the server even if all we want to do is change the channel. The server expects this; otherwise it will hang, waiting for all of the bytes to come or go.
One more obstacle remains. How to get the handle and message data into the proper position in the byte array? In the event handler we create string objects for the message and handle, then call the getBytes() method on the string objects. message.getBytes(0,message.length(),buffer,20); will copy message.length() bytes from the string object message starting at position 0 in the string to the byte array buffer starting at position 20. One thing that is missing in my program is error checking. It would be absolutely necessary in a production program to check and recheck to make sure that you don't overflow the buffer by writing more bytes than the buffer can hold.
- Resurrecting the Armadillo
- High-Availability Storage with HA-LVM
- Real-Time Rogue Wireless Access Point Detection with the Raspberry Pi
- Localhost DNS Cache
- March 2015 Issue of Linux Journal: System Administration
- DNSMasq, the Pint-Sized Super Dæmon!
- Days Between Dates: the Counting
- The Usability of GNOME
- You're the Boss with UBOS
- Linux for Astronomers