Write your own client/server apps

manyp_16x9

Throughout this Learn to Code series, we’ve been slowly cranking up the complexity of the Java code we’ve created, looking at concepts from assigning values to variables, right through to how to create multi-threaded apps.

But in everything we’ve done so far, we’ve only been running code on just the one computer.

Here, we’re taking our first simple steps into Java networking and learning how to send and receive data between two computers.

The end goal will be to create a multi-user chat system, coding both the server and client sides and running it on your local network.

TCP/IP basics

Network data is commonly transmitted using the Internet Protocol Suite.

Network data is commonly transmitted using the Internet Protocol Suite.

Networking in general can scare the daylights out of even seasoned professionals, but just understanding the basics can really help you in your coding.

All networking is about sending and receiving data between locations in such a way that the data arrives as quickly as possible (routing) and reliably handles transmission failure (error recovery).

Put simply, data transmission typically involves four network layers:

Application layer

This is the layer where HyperText Transfer Protocol (HTTP), File Transfer Protocol (FTP) and other protocols are often used. It’s essentially your app’s data level.

Transport layer

This layer encapsulates your data with a Transmission Control Protocol (TCP) transport header, adding connection continuity, error recovery and flow control parameters.

Network layer

This layer further encapsulates the data and TCP header with an Internet Protocol (IP) header providing addressing or ‘routing’ parameters.

Network Access or Link layer

At this layer, the data is encapsulated yet again, this time with communications protocols, depending on the relative location.

For local routing, a MAC (Medium Access Control) header is added. If sending the data across the Internet, the MAC header is replaced by Wide Area Network (WAN) header.

The fully encapsulated data packet then hits the cables in what’s called the ‘physical layer’, converted into electrical voltages as its whizzes around your network or around the world.

Together, these layers form the Internet Protocol Suite, and if you combine the transport and network layers, you get the TCP/IP protocol pairing that makes the Internet work.

At the destination end, the router pulls off the WAN header and replaces it with the appropriate MAC header for that local area network.

The data block or ‘packet’ is then progressively stripped of each header encapsulation as it goes back up the layers at the destination end, until the last thing remaining by the time it reaches the application layer is just the original data.

Now, there are big thick books written about TCP/IP, so I’m on a hiding-to-nothing trying to cover it in a couple of paragraphs. But just be aware it’s a series of network communication layers that controls how data is reliably transmitted.

Java networking

The client socket is a one-line network dialer to the server on localhost.

The client socket is a one-line network dialer to the server on localhost.

Thankfully, that’s about as much theory as you need to start playing around with Java network coding.

In simple terms, transferring data over a network relies on two computers negotiating roles between them — one acting as a ‘server’, the other as ‘client’.

Now, we throw these terms around like lollies, but it’s important to understand them and what they actually represent.

A client is a computer that makes a request for data from a server and the server’s role is to be ever ready to supply the data requested.

The process begins with the server awaiting a data request from a client on the network. The client then requests a connection to the server and the server says yes or no.

If ‘no’, the client should ideally wait for a time period and try again. If it’s ‘yes’, the two connect, the client makes its data request and, if the server has the data, sends it back to the client.

A client can only make one server connection at a time, whereas a server can typically handle many clients at once. However, the key is that the connection between a client and server in Java occurs through ‘sockets’.

Java has two socket classes — ‘ServerSocket’ for creating a server and ‘Socket’ for creating a client.

Server listens

Every time we’ve created a JButton in an app so far, we’ve attached a Listener object to it that continually monitors for when that button is pressed. Servers are similar, but in a more complex way.

A server is normally seen as a heavy-duty computer with buckets of storage, but at a software level, it’s just an app continually listening out for connection requests from clients.

That means the server application needs to be running first, otherwise the clients will just be whistling in the wind.

Yet, just as there’s not much point waiting for a phone call if no-one knows your phone number, the same goes for running a server. So when creating a server app, we have to create a phone number, which consists of the server computer’s network connection (IP address) and a TCP ‘port’ number.

Just briefly, the IP address is the common 32-bit dotted address you’re no doubt familiar with, for example 192.168.0.1, while the TCP port address is a 16-bit number between 1025 and 65535.

A TCP data packet is encoded with source and destination port numbers, but in both cases, the bottom 1024 port numbers are usually reserved.

So putting this altogether in Java, we can create a simple server with just the single line:

ServerSocket server = new ServerSocket(3801);

The IP address is set by the computer’s network connection, but the port number you can choose from the above range, and here, we’ve chosen ‘3801’.

But to now set that server to begin listening for a client connection request, we need to activate it and we do that with:

Socket socket = server.accept();

We create a Socket class object called ‘socket’, but what’s unusual about this code line is that, once Java sees it, it doesn’t proceed beyond this point until it receives that connection request, so it’s a bit like a ‘wait’ or ‘delay’ statement.

Input/Output Streaming

With the server initialised and waiting, what happens when it receive a connection request?

Thankfully, Java, your OS and your networking gear handle all the really tough stuff, but we still need to create two data connections — one coming into the server, the other going out back to the client, both through the connecting socket.

The way Java does this is very similar to handling file I/O — by data streams.

‘DataInputStream’ is the stream class used to accept data from the client and ‘DataOutputStream’ does the job going back the other way. We create the two stream objects with:

DataInputStream dataIn = new DataInputStream(socket.getInputStream());
DataOutputStream dataOut = new DataOutputStream(socket.getOutputStream());

From here, we read the input from the client through ‘dataIn’ and write data back out to the client through ‘dataOut’.

Clients talk

Okay, the server is up and running, now we turn our attention to the client. Thankfully, they’re quite similar.

We start by creating a Socket object with the parameters that are essentially the phone number of the server we want to dial up.

We do that with:

Socket client = new Socket(“localhost”, 3801);

The parameter “localhost” is cheat-code for when you’re running the client and the server on the same box.

If the server is on another computer, you replace ‘localhost’ with the IP address, such as:

Socket client = new Socket(“192.168.0.4”, 3801);

Again, we use the DataInputStream and DataOutputStream classes to create objects that talk to the server.

Making your first server

The simple user interface is created in NetBeans 8.o.2 IDE.

The simple user interface is created in NetBeans 8.o.2 IDE.

So let’s try all this out and see how it works in practice.

Grab the ‘apc_clientserver.zip’ source code from the our website, unzip it, open up NetBeans, import the inner ‘TablesServer.zip’ project file (File, Import Project, From Zip) and run it.

Next, import the ‘TablesClient.zip’ project file and run that.

This simple client/server project allows the client app to send a times tables maths question to the server app, something like ‘8 x 7’.

The server accepts input from the client, calculates the problem result and sends it back to the client.

It’s scintillating stuff, for sure, but it shows the basic techniques required for getting two apps talking to each other — and that’s still pretty cool!

TablesServer project

The server UI just consists of a scrollpane housing a jTextArea

The server UI just consists of a scrollpane housing a jTextArea.

Let’s start with the server side code, since we need this running first.

The user interface consists of a single jTextArea inside a jScrollPane — that enables the vertical scroll bars when text flows beyond the visible JFrame window area. And that’s it.

The actual server code itself is as simple as you can get and sits inside the ‘try’ statement block.

It begins with initialising the ‘serverStatus’ text area with time, date and server IP address, followed by setting up the ServerSocket with its port (3801) and starting the socket using statements we looked at previously.

The whole Times Table Server code in a few lines.

The whole Times Table Server code in a few lines.

The code waits until it receives a connection request from a client, at which point, we initialise the input and output stream objects, so the server can read and write data to the connecting client.

With the sockets and input and output streams now set up, we run the server inside an infinite while() loop.

It starts with initialising the string called ‘problem’ that waits for input on the input stream, reading it as a UTF string of characters.

We then split that string (which should be something like ‘7 x 6’) into separate integers using the String.split() function, splitting on the ‘x’ character and storing the arguments in two integer variables, arg1 and arg2.

Next, we send the multiplication result of those two arguments back out to the client over the data output stream and make a log note in the serverStatus textarea.

Following that, the while(true) loop kicks over, the code sits at the dataIn.readUTF() statement again, waiting for more input from the client.

TablesClient project

Pressing Calculate sends your times table problem to the server to solve.

Pressing Calculate sends your times table problem to the server to solve.

Since the server’s job is to respond to client requests, we design the client to make those requests.

The user interface is only slightly more complex, with a jTextField for entering in the times table problem, a jButton to initiate sending of the problem to the server and a jTextArea to display both the times table problem and answer from the server.

Again, the initialisation code bits are inside the try{} statement block, starting with a connection request to the ‘localhost’ IP address as port ‘3801’, where our server is expected to be ready and waiting.

We then create a pair of data input and output stream objects as before — and that’s it.

Once it receives a problem, the Server solves it and returns the answer.

Once it receives a problem, the Server solves it and returns the answer.

However, the important part of the code occurs inside the ‘jBtnCalculateActionPerformed’ private method.

This is basically just an ActionListener for the Calculate button, and as soon as it’s pressed, this method fires up.

First, we send the trimmed text that is our times table problem inside the ‘jTextProblem’ jTextField to the server via the dataOut data output stream object.

After that, we clear or ‘flush’ the stream object and wait for the return data from the server through the dataIn input stream object.

Once that data (the answer to the multiplication problem) arrives, we append it to the jTextFromServer jTextArea. And we’re done.

The problem(s)

Shut down the client and the server throws its toys out of the cot.

Shut down the client and the server throws its toys out of the cot.

Now as simple as this client/server example is, it does have a couple of major flaws — the first is that the server can only accept one client connection and, as soon as that connection is broken, that’s it — game over.

The only way to get the system back up and running is to reboot the server and client apps.

But while the connection remains open, you can send as many times table problems to the server as you like — close that connection and the server is done.

What’s the solution? Threads! We’ve been banging on for a few months now about how important threads are to proper coding, and if you plan to code a server capable of supporting multiple client connections, you’re going to need to implement threads.

The second flaw, entirely of our own making, is there’s absolutely no error recovery in either app whatsoever — that means, if the client shuts down for some reason, the server spits the dummy and sulks in the corner.

But that’s a comparatively easy fix. What’s more important for now is making sure you see how client and server apps work, how you set up the communications channels between them and how they actually communicate.

And hopefully, you’ve seen from this simple example that it’s not really rocket surgery.

Next time…

Now that we’ve got the basics under our belts, we’re ready to ratchet things up another notch and, next time, build our multi-threaded local chat system that supports multiple clients broadcasting messages globally and gracefully handles lost connections.

The whole thing works a little like your own Twitter system. Join us then.