Java and Client/Server
The server is a simple single-process concurrent server. Simply put, the server polls each connection, and processes requests in order. An alternative would be to fork a new process for each incoming connection. In this situation the single process server is far simpler (and, after all, the computer can only really do one thing at a time). The basic order of things is:
Create the master socket on the well-known port.
Bind the socket so that incoming requests are directed to the proper place.
Listen for connections.
Accept incoming connections.
The “well-known port” is a port which is known to all clients. All clients can't connect to the same port, so the server “hands off” connection requests to a different port. This is done by the TCP/IP layer, and we don't need to concern ourselves about how. This process is analogous to that of making a phone call to a large corporation's toll free number. Suppose that you wish to call 1-800-257-1234. You are asking the server for a connection on that port (phone) number. This company probably has hundreds of lines, but you would not want to try each of them until you finally got through, so the corporation has set up a rotary on their lines to put connections through to the next available phone line.
TCP/IP sockets work the same way. When a connection is accepted on a socket, a new file descriptor is created. The file descriptor is used as an index to an array of structures. Every client has exactly one unique file descriptor and a slot in the client array. Each array position holds a structure which contains the handle and current channel number. When the server receives a message packet, it looks through the entire array and retransmits the message to all clients who are subscribed to the channel number that the message came from.
Currently supported server commands are:
CB_ON sets the client's channel to 19 and sends a welcome message. It also stores the handle in the client array.
CB_OFF closes the socket and clears the client info from the client array.
SET_CHAN changes the channel of the client in the client array.
WHO_CHAN sends a message containing the handles of all connected clients subscribed to the current channel.
SEND-MESSAGE sends the message contained in the message field of the data packet to each client subscribed to the same channel as the originator. Emergency traffic on channel 9 is also sent to all connected clients regardless of what channel they are subscribed to. As stated earlier, the server must have all bytes in the data packet sent or received at once. It is not possible (with this implementation) to send part of a packet.
My original goal was to make a client that looked like a CB radio panel. This turned out to be too difficult to do with Java; while Java is a good portable programming language, creating a complex user interface is very difficult. I adapted my client from an example in Java in a Nutshell by David Flanagan, O'Reilly & Associates (an excellent book—great for reference). The CB client user interface is very simple. A Connect menu is at the top. From here, the user can quit or ask the server who is on the current channel. The middle window is the message area. Here all messages from other users and the server are listed. The client will print the handle and message from the data packet. The server is responsible for the data in those packets as it will put “System: WHO” in the handle for a WHO request. The bottom field is for entering a new channel. When Java detects activity in the menu bar or channel field, it will call the event handler routine. From here, it determines where the event came from and performs the appropriate processing. The user interface is not much—more a “proof of concept” than anything else—but it does provide much more functionality in fewer lines of code than would be required by an equivalent program written in C.
The big vs. little endian debate has been the topic of many flame wars on the Internet. But what is it? Big and little endian refers to the order of bytes. When moving data around, some systems start with the most significant byte (MSB) and some start with the least significant byte (LSB). Imagine an array of 4 bytes. How do you store or send this array? Would you start at the LSB (little endian) or would you start at the MSB (big endian)? Some hardware does it one way, and some does it the other. Why do we care? If you are writing a client and server in C to run on the same type of hardware, the endian problem doesn't pop up. But if you are using a different language, like Java, to talk to a server written in C, there could be a big problem. Endian problems crop up only when multiple byte data types like integers are sent across the network. Java automatically converts its data to and from network byte order when it sends data through a socket. C, on the other hand, does only as it is told. There are two C system calls, ntohl() and htonl(), which convert data to and from network byte order. Read the man pages for these calls and use them in your C-based servers and clients to avoid endian problems.
- High-Availability Storage with HA-LVM
- DNSMasq, the Pint-Sized Super Dæmon!
- Localhost DNS Cache
- Real-Time Rogue Wireless Access Point Detection with the Raspberry Pi
- Days Between Dates: the Counting
- You're the Boss with UBOS
- The Usability of GNOME
- Linux for Astronomers
- Multitenant Sites
- PostgreSQL, the NoSQL Database