Xin Chào
In the 70s/80/90s, IBM was a lonely computer behemoth in both areas: hardware AND software. Small creatures like DEC, ICL, Fujitsu, Siemens, Nixdorf, WANG etc. had to bow to IBM in order to survive in IBM’s shadow. The word IBM-compatible was the magic word for the survival of these poor creatures. Tiny creatures as critters, lived like outcasts. The critters were the workstation companies like SUN, Silicon Graphics etc. SUN, founded in 1982, was one of the outcasts. SUN was not the sun, but stood for S(tanford) U(niversity) N(etwork). Or to be more precise: SUN Microsystems Inc. Like the small creatures, SUN also had to bow to the behemoth IBM…
Then came the wizard Bill Gates with his ingenious idea to convince IBM to hand over the development of “Critters Stuff” to Microsoft so that IBM could focus on “big” things. And IBM, led by a former Navy fighter pilot, agreed. MSDOS (later as Windows) was the magic potion. For the large corporate market, MSDOS was christened the “workstation” operating system standard, approved and blessed by the behemoth IBM. A fatal mistake that made Microsoft a giant and shrank IBM to a dwarf in the software empire. And that was also the reason why SUN boss Scott McNealy hated Bill Gates and Microsoft. Today, IBM is only a shadow of its former self.
The embedded word N(etwork) obliged SUN to be a network player. And that was no surprise, because workstations had to be connected to exchange data with each other. For the commercial network at that time, it was the primitive twisted pair copper cable provided by the telephone companies, which were mostly state-owned companies. And the speed was absolutely ridiculous compared to today: 32 kbps (kilobits per second or kbaud) was standard. 64 kbps was a luxury as a “leased line”. Imagine how long it took to transfer 1 MB at 32 kbps: more than 4 minutes! Unbearable long.
Sockets were invented in Berkeley University around 1983 as APIs for TCP/UDP-IP and SUN came from Stanford, a “neighboring” university. It was natural that SUN integrated them into its network. A trend away from the Telephone Protocol to TCP/UDP-IP.
And as mentioned, IBM also set standards in the area of networking: SNA (Systems Network Architecture) with its PUs (Physical Units) and LUs (Logical Units) were the nightmare of tiny critters and small creatures. Too clumsy, too complicated and too proprietary made the life of tiny critters and small creatures unbearable. And the need for messaging grew and grew enormously. IBM again set a new messaging standard: MQSeries. And again: too cumbersome, too complicated and too proprietary… SUN had no choice but to “invent itself” with Java Message System (JMS) and JMS-Queues (JMS-Q) which were much better and much easier to work with than with SNA-MQSeries. JMS distinguishes between two messaging systems:
- Point-to-Point (P2P).
- Publish-Subscribe (PubSub)
If two participants communicate exclusively with each other, this type of communication is called point-to-point or P2P for short. An example: E-mail exchange between two people.
When someone publishes something for an exclusive audience that subscribes to that person’s work, this type of communication is called Publish-Subscribe, or PubSub for short. Example: YouTuber with his videos for his subscribers.
The illustrations greatly simplify the complexity of messaging systems and queues. I was involved in the development of BEA Weblogic in 1996 (acquired by ORACLE in 2008) and I know this.
In short, for a messaging system like JMS, you need a messaging server that manages the messages and the message queues (store, retrieve, process, stateless, stateful, persistent, etc.) on behalf of the message producers and message consumers. And this approach is very complex, so complex that ORACLE had to acquire BEA even though it is a DB company. Facebook and all other social networks are more or less based on this messaging system/queue technology.
So I don’t want to go into detail about JMS and JMS-Q in connection with DB, but show you how to create your own simple messaging system with messaging queues. Let’s start with some principles:
- Messages are data produced and consumed by users.
- Users are those who are active in the messaging process.
- The message server is the place where messages are collected and distributed.
- Instead of a DB, a simple medium (here: JTextArea, ArrayList and HashMap) is used to display messages and store the active participants.
- A simple text file (ForumUserList.txt) contains all registered/blocked participants.
Of course, you probably know what Client/Server (C/S) is and have probably already implemented it. The most common C/S application is the chat application. Here participants chat with each other, using a server as an intermediary. And the chat flow for two people is clear:
- You pick a partner from a provided list and invite him or her to chat.If that person agrees, you have a chat partner and you can start exchanging “messages” between you two. This chat process is called P2P in messaging system term.
- Or you broadcast a message to all participants in which you talk about anything and all participants who are active automatically receive your message. This type of broadcast is a PubSub process.
In such a case, you have two options: Socket or DatagramSocket. Socket is more reliable than DatagramSocket. It is a question of TCP/IP or UDP/IP. One is managed by API, the other by you (UDP: User Datagram Protocol).
Only two options? Normally: yes. But why don’t you, as an IT developer, “invent” something that has the advantages of both Socket and DatagramSocket? I did it and want to show you how easy it can be to implement a UDPServerSocket that works like a SerrverSocket and a UDPSocket that is similar to the Socket API.
UDPServerSocket.java
import java.net.*;
// joe t. schwarz
public class UDPServerSocket extends DatagramSocket {
/**
Constructor
@param port int Port number
@Exception Exception thrown by JAVA
*/
public UDPServerSocket(int port) throws Exception {
super(port);
}
/**
start accept to incomming message
@return UDPSocket
@Exception Exception thrown by JAVA or closed
*/
public UDPSocket accept( ) throws Exception {
if (closed) throw new Exception("UDPServerSocket is closed.");
DatagramPacket pkt = new DatagramPacket(new byte[1024], 1024);
this.receive(pkt); // wait for Client's message
return new UDPSocket(pkt);
}
/**
disconnect
*/
public void disconnect() {
super.disconnect();
closed = true;
}
/**
close
*/
public void close() {
super.close();
closed = true;
}
//
private volatile boolean closed = false;
}
and UDPSocket.java
import java.net.*;
import java.util.concurrent.TimeUnit;
// joe t. schwarz
public class UDPSocket extends DatagramSocket {
/**
Constructor - instantiated by Client app -
@param port int Port number
@param hostName String or IP
@Exception Exception thrown by JAVA
*/
public UDPSocket(int port, String hostName) throws Exception {
super();
calibrate(new DatagramPacket(new byte[256], 256, InetAddress.getByName(hostName), port));
}
/**
Constructor - instantiated by UDPSocketServer accept -
@param pkt DatagramPacket
@Exception Exception thrown by JAVA
*/
public UDPSocket(DatagramPacket pkt) throws Exception {
super();
port = pkt.getPort();
ia = pkt.getAddress();
send(pkt); // send this pkt to client
TimeUnit.MICROSECONDS.sleep(10); // and wait for draining...
}
/**
send
@param msg String. If length >, 65000 bytes will be truncated to 65000 bytes
@Exception Exception thrown by JAVA
*/
public void send(String msg) throws Exception {
if (closed) throw new Exception("UDPSocket is closed.");
send(new DatagramPacket(msg.getBytes(), msg.length(), ia, port));
TimeUnit.MICROSECONDS.sleep(10); // wait for draining
}
/**
send
@param msg byte array. If length >, 65000 bytes will be truncated to 65000 bytes
@Exception Exception thrown by JAVA
*/
public void send(byte[] msg) throws Exception {
if (closed) throw new Exception("UDPSocket is closed.");
send(new DatagramPacket(msg, msg.length, ia, port));
TimeUnit.MICROSECONDS.sleep(10);
}
/**
send
@param msg byte array.
@param idx int, starting index
@param len int, length. If length >, 65000 bytes will be truncated to 65000 bytes
@Exception Exception thrown by JAVA
*/
public void send(byte[] msg, int idx, int len) throws Exception {
if (closed) throw new Exception("UDPSocket is closed.");
if (len > 65000) len = 65000;
byte[] bb = new byte[len];
System.arraycopy(msg, idx, bb, 0, len);
send(new DatagramPacket(bb, len, ia, port));
TimeUnit.MICROSECONDS.sleep(10); // wait for draining
}
/**
read
@return byte array
@Exception Exception thrown by JAVA
*/
public byte[] read() throws Exception {
DatagramPacket pkt = new DatagramPacket(new byte[65000], 65000);
receive(pkt);
byte buf[] = new byte[pkt.getLength()];
System.arraycopy(pkt.getData(), 0, buf, 0, buf.length);
return buf;
}
/**
read
@param buf byte array
@return int Number of read bytes
@Exception Exception thrown by JAVA
*/
public int read(byte[] buf) throws Exception {
DatagramPacket pkt = new DatagramPacket(new byte[65000], 65000);
receive(pkt);
int n = pkt.getLength();
if (n > buf.length) n = buf.length;
System.arraycopy(pkt.getData(), 0, buf, 0, n);
return n;
}
/**
read
@param buf byte array
@param off int starting offset
@param len int length of bytes
@return int Number of read bytes
@Exception Exception thrown by JAVA
*/
public int read(byte[] buf, int off, int len) throws Exception {
if (off < 0 || off > buf.length) throw new Exception("Invalid off.");
DatagramPacket pkt = new DatagramPacket(new byte[65000], 65000);
receive(pkt);
int n = pkt.getLength();
if (n > len) n = len;
System.arraycopy(pkt.getData(), 0, buf, off, n);
return n;
}
/**
readPacket
@return DatagramPacket
@Exception Exception thrown by JAVA
*/
public DatagramPacket readPacket() throws Exception {
DatagramPacket pkt = new DatagramPacket(new byte[65000], 65000);
receive(pkt);
int n = pkt.getLength();
if (n >= 65000) return pkt;
DatagramPacket p = new DatagramPacket(new byte[n], n);
p.setData(pkt.getData());
return p;
}
/**
disconnect
*/
public void disconnect() {
super.disconnect();
closed = true;
}
/**
close
*/
public void close() {
super.close();
closed = true;
}
/**
getInetAddress
@return InetAddress of this UDPSocket
*/
public InetAddress getInetAddress() {
return ia;
}
/**
getPort
@return int port of this UDPSocket
*/
public int getPort() {
return port;
}
private void calibrate(DatagramPacket pkt) throws Exception {
send(pkt); // ping the host to get real InetAddress and port
TimeUnit.MICROSECONDS.sleep(10); // wait for draining
// then wait for reply...
receive(pkt);
ia = pkt.getAddress();
port = pkt.getPort();
}
//
private int port;
private InetAddress ia;
private volatile boolean closed = false;
}
and how they work:
- As server:
private ExecutorService pool = Executors.newFixedThreadPool(2048);
…
pool.execute(() -> {
try {
uServer = new UDPServerSocket(port);
while (!closed) pool.execute(new aChatter(uServer.accept()));
} catch (Exception e) {
if (!closed) e.printStackTrace();
System.exit(0);
}
});
…
private class aChatter implements Runnable {
public aChatter(UDPSocket me) {
this.me = me;
}
public UDPSocket me;
…
public void run() {
…
}
}
- as Client
soc = new UDPSocket(iaddress, port);
...
(new Listener( )).start();
...
private class Listener extends Thread {
public Listener( ) { }
public void run( ) {
try {
while (true) {
byte[] rep = soc.read();
...
}
...
} catch (Exception ex) {
ex.printStackTrace();
}
}
...
More: click GITHUB. There you find 3 different versions of Chat apps:
- iForum.java, iChat.java with ServerSocket and Socket
- UDPForum.java and UDPChatter.java with DatagramSocket
- uForum.java, uChat.java, UDPServerSocket.java and UDPSocket.java
Authenticate.java is used to encrypt and decrypt user ID and password using a simple algorithm. The apps are self-explanatory.
Note: The difference between the SOFT and HARD option of iForum/uForum and UDPForum is:
- SOFT: If a participant is banned, he or she can “re-register” with a different user ID.
- HARD: If a participant is banned, his or her ID and computer are “banned”. He or she can only “re-register” with a different computer.
Have fun !