UDP Multicasting Clients and a Name ServerFigure 30-4 shows the various objects in the multicasting chat application. Figure 30-4. Multicasting chat systemEach user is represented by a MultiChat object that can send and receive messages from the multicast group. However, this isn't a pure peer-to-peer example since a client must first log in to the NameServer object and receive the address of the group. Upon leaving the group, the client must log out from the server. Each client has a name that is checked by NameServer for uniqueness. If the name is being used by a group member, then NameServer rejects the request to log in. As a side effect of storing user names, NameServer can process who messages. Instead of a client multicasting a who message to all the other group members and receiving multiple replies, a single who message is directed to the NameServer followed by a single response. This approach significantly reduces the number of messages circulating in the system. As discussed in the context of the threaded chat server, the main drawback of using NameServer is that it represents a single point of failure. However, NameServer's disappearance only prevents new clients from joining the group. Communication between existing members can continue. The other consequence of NameServer's demise is that who messages will no longer be answered. NameServer must be able to handle multiple concurrent accesses from clients (to log in, log out, or request who information). The server uses UDP as its communication protocol, permitting it to multiplex easily between the various requests (since each request is a datagram). The messages that a client can send to NameServer are:
A client can send messages to the group for transmission to the other members. The message format is:
The client's name is prepended to the front of the message. The square brackets mean the message may have an optional / extension, which makes the message visible only to that particular client. For example:
is the message "come in for dinner" sent from Andrew to John. Whereas:
broadcasts the message "has anyone seen John's football?" from Andrew to everyone (including himself). The class diagrams for the application are given in Figure 30-5 but are uninformative. Almost no public methods are required since communication is datagram-based. Figure 30-5. Class diagrams for the multicasting chat systemThe Name ServerThe NameServer class is similar to the top-level to the UDP-based server, ScoreUDPServer, in Chapter 29. It creates a DatagramSocket for receiving client packets and then enters a loop waiting for them to arrive. The client's address, port, and the text of the message are extracted from an arriving packet and from the information passed to a processClient( ) method. processClient( ) employs a four-way branch to handle the various kinds of message (hi, bye, who) along with a default catch-all branch:
The client's name is added to an ArrayList called groupMembers if it isn't present in the list. A bye message removes the name. A who message triggers a call to listNames( ), which builds a string of all the groupMembers names. sendMessage( ) creates a datagram that's sent back to the client's address and port:
Improving Client Login and LogoutThe simplicity of processClient( ) in NameServer shows that the login and logout mechanisms could be improved. There's no password associated with the name used in hi, so a client can use any name to log in. A bye message can come from anywhere, so it's possible for any client to log out another client. In any case, the removal of a client from the NameServer doesn't force a client to leave the multicast group. The introduction of a password, together with encryption of the message inside the datagram would solve many of these problems. The remaining concern is that the server cannot force a client to leave the multicast group. The protocol is supported at the IP level, so beyond the reach of the Java MuticastSocket API. Indeed, the server has no control over the group at all, aside from deciding who will be given the multicast group address. This is hardly effective since the address can be shared between clients once one user has it. This lack of control is a crucial difference between the client/server approach and peer-to-peer. Multicast ChattingA MultiChat object is capable of two forms of network communication: it can send ordinary UDP datagrams to the NameServer (and receive replies) and can post datagrams to the multicast group (and receive messages from the group). A MultiChat object is invoked with the client name supplied on the command line:
Its GUI is similar to the one for the threaded chat client, as shown in Figure 30-6. One obvious difference is that messages are now prefixed with the client's name rather than an address and port. Figure 30-6. The multicasting chat clientThe MultiChat constructor creates a DatagramSocket for the NameServer and sends it a hi <name> message requesting the multicast group's address. If the address is sent back, the group will be joined and a hi message sent to it (the message could be anything):
Having the client talk to the name servermakeClientSock( ) creates a DatagramSocket for sending and receiving communication from the NameServer. However, it's given a timeout of five seconds:
A timeout is useful when the client is waiting for a datagram inside receive( ). This occurs after a hi message has been sent and after who queries. If the server has died, the timeout will limit the wait to five seconds before a SocketTimeoutException is raised. The drawback is deciding on a reasonable timeout value. For instance, five seconds would probably be too short if the link was across the internet, through slow modems. This is why most programmers prefer to put blocking calls into separate threads so they can wait as long as they like. An alternative for TCP is to use nonblocking sockets, as illustrated in Chapter 29. Messages are sent to the server from sendServerMessage( ) by creating a datagram and sending it through clientSock. Messages coming from the server are read by readServerMessage( ). Its clientSock.receive( ) call is wrapped in a try-catch block in case of a timeout (or other network problems). A call is made to readServerMessage( ) immediately after a message is sent to the server. There's no attempt to use a separate thread for server processing. For example, a who message is followed by a readServerMessage( ) call:
Having the client talk to the multicast groupThe multicast group address string is extracted from the hi response and used by joinChatGroup( ) to create the multicast socket. A code fragment that does this is:
sendPacket( ) is used to send a message to the group and adds the (userName): prefix prior to delivery:
Text messages are sent to the group when the user hits Enter in the messages text field. actionPerformed( ) calls sendMessage( ):
sendMessage( ) extracts the string from the text field, checks it for validity, and passes it to sendPacket( ). All of this is done inside the GUI thread of the MultiChat application. Hearing from the multicast groupMulticast messages may arrive at any time, so must be handled in a separate thread. Instead of creating a new Thread object, MultiChat uses the application thread by entering an infinite loop inside waitForPackets( ):
This is the same approach as in the ScoreUDPClient in Chapter 29. processPacket( ) extracts the text from the incoming packet and displays it (if it's visible to this client):
A message is visible if it has no / <name> part if / <name> contains the client's name, or if the message is originally from the client. This latter condition can be checked by looking at the start of the message that has the sender's name in brackets. How Invisible Are Invisible Messages?A message won't appear in a client's text display area if it has a / name extension that refers to a different client. Is this sufficient for private communication between two users? Unfortunately, this is not sufficient since the message is still being multicast to every client. This means that a hacker could modify the MultiChat code to display everything that arrives. Privacy requires that the messages be encrypted. The other misleading thing about private communication is that it appears to be point-to-point between users and seemingly more efficient than multicasting. Unfortunately, this is false. True point-to-point communication would require another communication link, perhaps utilizing the DatagramSockets utilized for client/server interaction. Ways to Implement whoMultiChat handles a who message by querying the server for a list of names. Another approach would be to multicast the message to all the clients to gather responses. The main benefit would be communication without relying on the server, which can fail or otherwise stop responding. I didn't use multicasting to process a who message because of the large number of messages it would add to the system. With multicasting, the client's who query would be sent to n clients. Each client would send a reply, multicasting to every client, not just the one interested in the answer. This would mean a total of nxn messages circulating through the group. The grand total for a single who query is n + n2. By using the server, I replaced this large number with two messages: the request sent to NameServer and its reply. Alternatives to multicasting should always be explored since bandwidth is such a crucial commodity. |
Thursday, February 4, 2010
UDP Multicasting Clients and a Name Server
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment