Signaling Report-Final

12/14/98
MIA
Dan Spoonhower <spoons@cs.cornell.edu>
Naveen Sastry <nks1@cornell.edu>
Signaling

Goals

We had orignally set out to create a simple API that wraps much of the "core" functionality behind a few easy to use methods. Our rationale for this was that the applications should not have to concern themselves with much of the implememntation details. Thus, initiating a call can be done in as few as 15 lines of code. We wrap the datax and directory code from the applications so that they only have to use our interfaces to access other groups' functionality.

Further, from an interfacing standpoint, we tried to make a minimal set of interfaces. To do this, wanted to create an event observer model allowing anyone who wants information about events to be notified of them. Thus, the management and billing groups do not have to provide us an interface when making or receiving calls. Rather, they listen to the specific messages that signify those actions. Therefore, much of the work shifts from generating interfaces to generating well defined protocols. Since the protocols are no longer internal to signaling code (other people need to know the sequence of messages in a handshake, we set out to create a flexible, well-known messaging protocol. On top of this, we wanted to design handshaking protocols that would hide much of the underlying endpoints. For example, the handshake for phone to internet should be the same as for internet to internet.

We also wanted to try to create a design that was highly modular and built out of components. We wanted to create a server that would start the many different components created by our team and other teams in the group. With this model, we could easily extend the server by starting a new component. Likewise, it allowed us to easily control which portions of the server that we started by passing in simple command line arguments.

Even though our project did not require scalability, we wanted to put in support for the ability to run with a distributed SigServer farm. Thus, we tried to take this point into account during our early design decisions. With a few key decisions early on, we hoped that we could make our system easily scalable for the future.

Work Completed

The signaling components are based around three main tasks: the MessageCenter, the SigServer, and the CallSignaling interface. The first provides a standardized and reliable messaging transport layer, the second provides centralized control and the last is the signaling client interface.

MessageCenter

Early on in our project we discovered a need for a means of communication slightly more abstract than TCP sockets. We needed something that would correctly marshal data types across the network. To meet these needs we implemented the MessageCenter, a communication layer built on top of TCP using Java serialization and the observer model. Using this layer, our team, as well as other teams in our group, could easily implement a protocol by creating Message objects and making a single method call to send these Messages. Messages are received by any client who has been registered as a listener.

MessageCenters also make it easy for one component to watch "over the shoulder" of another component; many components may be registered as listeners, and each will receive Messages destined for the MessageCenter. For example, both the billing and management teams eavesdrop on signaling protocols by listening to the messages pasted between hosts. This frees us from making explicit invocations of billing/management methods when, for example, a call is completed. This is in line with our philosophy of component oriented software: our code executes completely independently of either of these two groups, and also integrates cleanly.

SigServer

The SigServer also follows the component philosophy. The server itself consists only of a few hundred lines of code and merely starts up other services, both signaling and non-signaling. We can easily add other server components to it with minimal effort, allowing us the quickly change the configuration and behavior of the entire system with a few modifications.

The SigServer starts the following componets:

SigServer signaling components are detailed below.

CallSignaling

This is the standard client application interface. It provides everything that applications need by wrapping data exchange and directory interfaces and automatically starting billing and management child components. Both the CallSignaling implementations and the SigServer components use the MessageCenter abtraction to do network communication.

At the time of the demo, we have successfully integrated with both the data exchange and gateway teams for two party calls. Calls can be made both from one computer to another or from a telephone handset to a computer running a client. In both cases, the same protocol is used, a three-way handshake, outlined in the diagram below.

two party handshake
During the initial handshake, data exchange setup parameters are exchanged and negotiated as well as the ports on which data will be transfered. In the case of IP-to-IP calls, the parameters suggested by the initiating party (which are set by management) are always accepted by the receiving end. In the case of the calls involving the gateway, however, the gateway is able to dictate which parameters are used. (If the gateway is initiating the connection then it suggests parameters and denies any response which doesn't consent to these parameters. If the gateway is on the receiving end then it is able to override the suggested parameters, with the parameters it requires.)

During the course of the call, signaling steps out of the way for the most part. If one of the parties wants to senf DTMF, then a ConnectionEventMessage is sent through the control plane to the other client, where it is sent up to the client through a DTMFEvent. If the gateway detects incoming DTMF from the telephone network, then a ConnectionEventMessage is instantiated and sent to the IP client. Going the other way, if the client sends a DTMF through the signaling control plane to the gateway, then the gateway produces the actual tones and sends then through the analog interface to the telephone network.

At the end of a call, or if an error is sent up from the data plane (through a callback), a GoodByeMessage is sent to the other party, instructing them to breakdown the call, terminate the data connection, and send an event to the application.

At this time the multi-party signaling code is not in place. The protocol, diagramed below, has been partially implemented. However, we didn't have the time to integrate data exchange code, nor to test our code completely.

multi party handshake
The multi-party signaling uses a central GroupManager to maintain a definitive list of call participants and information about them. When a user wants to start a multi-party (conference) call, it asks the MPM (MultiPartyManager) to create a new group. The MPM responds with the address of the new GroupManager. The client can then ask the GroupManager to add itself to the group though an InvitationMessage. It then asks other users to join the group though a JoinMessage.

Each time the GroupManager receives an Invitation message it updates its internal table of group members and then sends an AddMessage to each existing member of the group. Similarily, when a GoodByeMessage is received, a DeleteMessage is sent out to each group member.

Problems

We did not get to all of the functionality that we had originally wanted to. Part of this was because of a lack of time to perform some of the more exotic functions, such as full implementation of multi-party calling. We concentrated our efforts on getting two-party code working well. Nevertheless, we did spend quite a bit of time on the multi-party code. We brought the multi-party code to the point where it can establish groups and handshake properly as well and add people to the gruop. It does need some work in finishing of the protocols and in handling some of the error conditions, but much of the groundwork has been laid.

There were also the problems of logistics of merging everyone's code. Since signaling code touches most, if not all of the low level API's between teams, it was quite an undertaking to fold other groups' code into our own. Part of this task was made easier by the observer model that we used, since there wasn't much to do for management to be added to our code. But, it would have been nice to have been able to integrate with the data exchange group a little earlier since they were such a critical group to the success of the project. In addition to specifying interfaces (and even message protocols) we should have been more careful about the specifics of each function. For example, blocking versus non-blocking read() became a major issue when integrating data exchange. If we had considered this earlier, we might have saves some time in the last week.

Useful Lessons

On the techical side, we've learned a lot about protocol design and most importantly here, about handling all possible cases. It seems easy to implement the case where everything goes as planned, but handling the unplanned cases, and espcially, finding all the unplanned cases is much more difficult. We even touch a bit on the group membership problem here. For example, in a multi-party call, maintaining up-to-date information about the group is somewhat simplified by centralizing control, but, for instance, what if we are unable to send to AddMessage to an existing member? Do we deny the new member? Or kick out the old member? We've also learned a lot about engineering software within a large group. We've realized the extent of the interdependence of groups in a large project. Progressing at an optimal rate requires that everyone have their code ready as promised. When 1 or 2 groups delay their code, the progress on the larger code slows. While interfaces can be utilized to hide others' progress, for testing purposes (espicially functional testing) the actual code must be present. This last week has shown some of these problems as we've been forced to wait to incorporate other code into our own. This is understandable in a project of this size, and overcoming these issues helps to learn about the software design process.

We have also refined our process of effectively working on the same codebase (within signaling) to team up on complex problems and split up to increase parallelization when speed is necessary. The effectiveness of CVS was also evident throughout the project, as it allowed a an easy system to manage the code. There were, however, groups who were reluctant to use the system which caused problems for others.

In addition, we have learned much about developing robust protocls that are able to withstand failures throughout. As part of this, passing error messages on any failure help to track the state of the system to provide reliable feedback to the user and programmer. Likewise, since we spent much time just thinking through the development of the actual handshake protocols, we became better at developing protocols that can withstand

Next Time (More Time)

There were some features that we would have liked to implement given more time. Some of these items would have been neat as extentions to the project given sufficient time.

We would have liked to get multiparty code working well. We put in much of the groundwork, so it would have been nice to get the final results working.

We were also extremely close to get be able to call out from an Internet client to a telephone. We have all of the logic in place, and faced a few last minute bugs that prevented us from executing. The last minute time crunch was the most frustrating since we had much of the groundwork but were not able to deliver the final results.

Given more time, we would have liked to have been able to move to a more production-like system that would implement some sort of security at the messaing layer. We currently would suffer some problems if a malicious client were to send random GoodBye messages, for exmaple. The fix could be implemnted by putting some sort of encryption and authentication at the message center layer. This would provide a reliable and secure transport layer. This type of functionality would be nice to put in to move more towards a production system.

Interfaces

Messages used in our protocols:

Management, billing, and directory services can just "listen in" on our protocols, by implementing the MessageListener interface and understanding our messages. As a result, we've left most responsibility for the interaction between these other groups and ourselves in the laps of the other groups. We still need to establish a few small interfaces between management and ourselves, so that management can fine tune system parameters. (We don't see this as a significant stumbling block, however.)

Required from DataX:

/** * A Connection at the data layer. The constructor sets up the connection. * Data to and from the client (application) is passed through signalling * straight to the VoiceConnection object. The VoiceConnectionObserver * interface allows the DataX to notify the signalling component when * errors occur. */ public interface VoiceConnection { public byte[] read(); // read samples public void write(byte [] s); // write samples protected void close(); // shuts down the connection protected void open(); // starts the connection protected ConnectionTable getConnectionTable(); public String toString(); } /** * Holds list of all active connections on the VoiceConnection. */ public interface ConnectionTable extends Enumeration { public static final int MAX_CONNECTIONS = 16; public void add(InetAddress hostAddress, int hostPort); public void delete(InetAddress hostAddress, int hostPort); public boolean isMemeber(InetAddress hostAddress, int hostPort); public Enumeration connections(); public boolean connectionsExist(); int getMyPort(); byte getUniqueID(InetAddress hostAddress, int hostPort); }

Required from Directory:

public abstract class DirClient 
{
	public abstract void newAddress(String user, String phoneNumber, Vector services) throws DirectoryException;
	public abstract void newAddress(String userName) throws DirectoryException;
	public abstract void addPhoneNumber(String userName, String phoneNumber, Vector services) throws DirectoryException;
	public abstract void updateServices(String user, Vector sevices) throws DirectoryException;
	public abstract void registerIP(String userName, String ipAddress, int port, Vector services) throws DirectoryException;
	public abstract void killIP(IPAddress addr) throws DirectoryException;
	public abstract void removeUser(String userName) throws DirectoryException;
	public abstract void removeUser(String userName, String phoneNumber) throws DirectoryException;
	public abstract void close();
	public abstract void askPacket(String a, String p); //take out
}
MiaAddress is an abstract class representing a generic "terminal." MiaAddresses may coorespond to a telephone terminal, an IP telephony application, or possibly to a multiparty GroupManager.

Required from Gateway:

public class Signal
{
	public static native int gw_initialize(boolean useIP);
	public static native int gw_setupConnection(int wire, Connection h);
	public static native int gw_terminateConnection(int status);
	public static native int gw_dial(int wire, char[] dialstr);
}

Provided to applications:

(This code has changed with only slight variations from last time.)
public interface Terminal { /** * Initiate a Connection with another user. * * @param multi enables multi-party conferencing * @param redirectable if true then connections will automatically be redirected if the given user * reponds with REDIRECT, o.w. a RedirectedException will be thrown. */ public Connection invite( String user, String[] services, boolean multi, boolean redirectable) throws SignalingException; /** * Initiate a Connection with another user. * * @param multi enables multi-party conferencing * @param redirectable if true then connections will automatically be redirected if the given user * reponds with REDIRECT, o.w. a RedirectedException will be thrown. */ public Connection invite( MiaAddress address, boolean multi, boolean redirectable) throws SignalingException; /** * Register an observer to receive events for this terminal. */ public void addTerminalObserver( TerminalObserver o); public void removeTerminalObserver( TerminalObserver o); } public interface Connection { /** * Read all data available. (blocking read) */ public byte[] read(); /** * Read only the specified length. (blocking read) * * @return The number of bytes actually read. */ public int read( byte[] b, int length, int offset); /** * Write an entire array of data to the Connection. */ public void write( byte[] b); /** * Write only the specifed data to the Connection. */ public void write( byte[] b, int offset, int length); /** * To invite a 3rd (or 4th) party into an existing conversation. */ public Connection invite( String user, String[] services); /** * Redirect an established Connection to another user. * * This will terminate the current Connection. */ public void redirect( String user); public void close(); /** * Send an event to everyone else on this connection. * * Most likely DTMF. */ public void sendEvent( ConnectionEvent); /** * Register an observer to receive events for this connection. * * See report-1 for a definition of ConnecetionObserver. */ public void addConnectionObserver( ConnectionObserver o); public void removeConnectionObserver( ConnectionObserver o); }

Messaging interfaces:

public interface MessageCenter { /** allows you to be notified of messages that are LEAVING this MessageCenter **/ void addOutgoingListener(MessageListener ml); /** allows you to be notified of messages that are ENTERING this MessageCenter **/ void addIncomingListener(MessageListener ml); /** sends a message to the address as returned by m.getDestination() **/ void sendMessage(Message m) throws MessageException; } public interface Message extends Serializable { MiaAddress getDestination(); } public interface MessageListener { /** Function is called after a MessageListener has registered to receive messages and a message has arrived. @param m Message that the MessageCenter processed. @param ia is the source address of the message. It is null if the message is an outgoing message (and hence it originated here). **/ void handleMessage(Message m, InetAddress iaSource); }

Advice for Staff

The only problems we ran into as far as hardware were sound cards. In order to test our API, we require full-duplex sound cards. Though many of the machines in CSUG have full-duplex cards, they weren't properly configured. ADM was reluctant to change the settings on more than one machine.

Signaling seemed like a big job, perhaps it should have been split between more than just two students. Though with four people it might be difficult to coordinate, three might have helped us accomplish more.

As far as the gateway, it would have been nice for the machine to be located in CSUG (though we realize the security issues associated with this machine) so that it would be in a room with machines on the same side of the firewall. Also, it was difficult to get as much time on the gateway as we would have liked in the last few weeks. (We couldn't complete testing and integration earlier because other teams weren't ready to go.) Breifly, I'm aware of many of the students and faculty of the Systems Lab that were disrupted by the number (and volume) of the CS519 students in the lab.

Finally, it seems like development of the core components: signaling, gateway, and data exchange, should really be near completion well before the end of the semister. Perhaps the course staff should have encouraged this by enforcing an earlier due date. This way apps would have more to work from and not have to worry about implementing their own versions of data/signaling code.

References



Last-modified: 12/16/98 3:19 PM
Modified-by: spoons