Network Programming with Perl


 
Network Programming with Perl

By Lincoln  D.  Stein

Slots : 1

Table of Contents
Chapter  19.   UDP Servers

    Content

Detecting Dead Clients

There is, however, a significant problem with the chat server as it is currently written. A client might crash for some reason before sending a LOGOFF event to the server, or a LOGOFF event might be sent but get lost on the network. In this case, the server will think that the user is logged in and continue to send messages to the client. Over long periods of time, the server may fill up with such phantom users. There are a number of solutions to this problem:

  • The server times out inactive users Each time the server receives an event from a user, such as joining or departing a channel, it records the time the event occurred in the corresponding ChatObjects::User object. At periodic intervals, the server checks all users for those who have been silent for a long time and deletes them. This has the disadvantage of logging out "lurkers" who are monitoring chat channels but not participating in them.

  • The server pings clients The server could send a PING event to each client at regular intervals. The clients are expected to respond to the event by returning a PING_ACK . If a client fails to acknowledge some number of consecutive pings, the user is automatically logged out.

  • The clients ping the server Instead of the server pinging clients and expecting an acknowledgment, clients could send the server a STILL_HERE event at regular intervals. Periodically, the server checks that each user is still sending STILL_HERE events and logs out any that have fallen silent.

Adding STILL_HERE Events to the Chat System

The third solution we listed represents a good compromise between simplicity and effectiveness. It requires small changes to the following files:

  • ChatObjects/ChatCodes.pm We add a STILL_HERE event code for the client to use to transmit periodic confirmations that it is still active.

  • ChatObjects/TimedUser.pm We define a new ChatObjects::TimedUser class, which inherits from ChatObjects::User. This class adds the ability to record the time of a STILL_HERE event and to return the number of seconds since the last such event.

  • chat_client.pl The top-level client application must be modified to generate STILL_HERE events at roughly regular intervals.

  • chat_server.pl The top-level server application must handle STILL_HERE events and perform periodic checks for defunct clients.

Modifications to ChatObjects::ChatCodes

The modifications to ChatObjects::ChatCodes are minimal. We simply define a new STILL_HERE constant and add it to the @EXPORTS list:

@EXPORT = qw( ERROR LOGIN_REQ LOGIN_ACK ... STILL_HERE ); ...use constant USER_ITEM => 190; use constant STILL_HERE => 200; 1;

The ChatObjects::TimedUser Subclass

We next define ChatObjects::TimedUser, a simple subclass of ChatObjects::User (Figure 19.8). This class overrides the original new() method to add a stillhere instance variable. A new still_here() method updates the variable with the current time, and inactivity_interval() returns the number of seconds since still_here() was last called.

Figure 19.8. The ChatObjects::TimedUser module

ChatObjects::TimedUser will be used by the modified server instead of ChatObjects::User.

The Modified chat_client.pl Program

Next we modify chat_client.pl in order to issue periodic STILL_HERE events. Figure 19.9 shows the first half of the modified script (the rest is identical to the original given in Figure 19.2). The relevant changes are as follows :

Figure 19.9. chat_client.pl with periodic STILL_HERE events

Line 8: Define an ALIVE_INTERVAL constant We define a constant called ALIVE_INTERVAL , which contains the interval at which we issue STILL_HERE events. This interval must be shorter than the period the server uses to time out inactive clients. We choose 30 seconds for ALIVE_INTERVAL and 120 seconds for the server timeout period, meaning that the client must miss four consecutive STILL_HERE events over a period of 2 minutes before the server will assume that it's defunct.

Line 38: Create a timer for STILL_HERE events The global variable $last_alive contains the time that we last sent a STILL_HERE event. This is used to determine when we should issue the next one.

Line 47: Add a select() timeout We want to send the STILL_HERE event at regular intervals even when neither STDIN nor the server have data to read. To achieve this, we add a timeout to our call to the IO::Select object's can_read() method so that if no data is received within that period of time, we will still have the opportunity to send the event in a timely fashion.

Lines 55 “58: Send STILL_HERE event Each time through the main loop, we check whether it is time to send a new STILL_HERE event. If so, we send the event and record the current time in $last_alive .

The Modified chat_server.pl Program

Figure 19.10 shows the chat_server.pl script modified to support auto logout of defunct clients. The modifications are as follows:

Figure 19.10. chat_server.pl with periodic checks for defunct clients

Line 6: Use ChatObjects::TimedUser We bring in the ChatObjects::TimedUser module to have access to its still_here() and inactivity_interval() methods .

Lines 9 “10: Define auto-logout parameters We define an AUTO_LOGOUT constant of 120 seconds. If a client fails to send a STILL_HERE message within that interval, it will be logged out automatically. We also define an interval of 30 seconds for checking all currently logged-in users of the system. This imposes a smaller burden on the system than would doing the check every time a message comes in.

Line 26: Dispatch on the STILL_HERE event We add an entry to the %DISPATCH dispatch table that invokes the current ChatObject::TimedUser object's still_here() method when the STILL_HERE event is received.

Line 32: Keep track of the check time As in the client, we need to keep track of the next time to check for inactive clients. We do this using a global variable named $next_check , which is set to the current time plus CHECK_INTERVAL .

Lines 43 “45: Call the auto_logoff() method at regular intervals We then add a continue{} block to the bottom of the main loop. The block checks whether it is time to check for defunct users. If so, we call a new subroutine named auto_logoff() and update $next_check .

Lines 49 “56: Check for inactive users and log them off The auto_logoff() method loops through each currently registered user returned by the ChatObjects::TimedUser->users() method (which is inherited from its parent). We call each user object's inactivity_interval() method to retrieve the number of seconds since the client has sent a STILL_HERE event. If the interval exceeds AUTO_LOGOUT , we call the object's logout() method to unregister the user and free up memory.

Unlike the client, we do not time out the call to $server->recv_event() . If the server is totally inactive, then defunct clients are not recognized and pruned until an event is received and the auto_logoff() function gets a chance to run. On an active server, this issue is not noticeable; but if it bothers you, you can wrap the server object's recv_event() in a call to select() .


   
Top

Категории