The method by which one Worker/Supervisor sends messages to another Worker/Supervisor is very simple. A text message is composed, sent to the server via a TCP socket, which then relays the message on to the desired destination. Thus, we have a star network. Every Worker/Supervisor has one TCP connection to the server, for sending/receiving messages relevant to/from the server.
We are opposed to "micro management". Thus, a Supervisor is not required to worry about the details of any one voice circuit. In fact, the only contact a Supervisor will have with any one voice circuit is to create the Worker to handle that voice circuit.
Similarily, when two Workers have to arrange the transfer of audio data between themselves, it is none of the Controller's business. The controller should merely say to one worker, "arrange audio transfer with that worker". Consequently, the Controller is not aware of what port the Worker is using for audio
Prior to any communication, a node must connect to the server and identify itself. Once identified, anyone connected to that server can talk to the node. A node (suppose it is "switch") will first do a TCP Connect to this server. This server will then spawn of a TCPConnection class to listen for all further communications from switch. Node switch then sends a zero terminated string of
to this server. This string identifies the remote node to this server. On receipt of this string, the server can make a unique label for the TCPConnection class, and add it to the internal list. Once the TCPConnection class has been added to the internal list, full bidirectional comms is now possible with node "switch".
The server assumes all remote nodes will correctly choose a valid unique ID. My preference is that the supervisor connects with a label such as "controller", "ssvoip", or "ssam". Workers spawned by that supervisor have a number appended to the supervisor's label. Thus, the voip supervisor connects as "ssvoip". All workers spawned by the voip supervisor will be called "ssvoip1", "ssvoip2", etc. This naming scheme makes debugging somewhat easier, as you can identify, from the label, the origin/destination of a message.
A message sent to the server has the format remoteNode : message
The server changes the message, and sends it to the remote node as sourceNode : message
Thus, this server has changed the message. This is acceptable, as it means that the receiving node is able to determine who sent the message. The receiving node cannot determine the source of the message by socket analysis. This happens as the network stack of the receiving node will always say that the source of each packet is this message server.
To illustrate, suppose A sends the message "status" to B. A sends to the sever
"B:status"0 (string, with a trailing zero byte)
the server receives this packet and then sends to B
"A:status"0 (string, with a trailing zero byte)
Note - the quotes are there to illustrate it is a string, and are not included in the packet.
| Worker | handles commands associated with one particular voip circuit. Thus, for a voice over IP application, there will be X workers for the X active voice calls in the voip application. |
| Supervisor | responsible for establishing (as required) new Workers. It can be used to terminate the sofaswitch application. Messages which are appropriate for a particular Worker are never ever sent via the supervisor |
Even the controller can be split up into a Worker/Supervisor distinction. The Worker in the controller sends commands appropriate to managing one current call. The Supervisor creates Workers to handle new calls, as required.
| hangup | The Worker which receives this terminates the current call |
| callnode | Causes a Worker/Supervisor to start a voip call to voip destination. This command is not relevant for an answering machine type node. |
| status | A query on the Worker/Supervisor's status. If an argument is supplied, then status is required of that entity. For example, you could specify the status of a particular conference room |
| duration | A query for the worker to reply with the duration of the current call, or the supervisor to reply with how long it has been up |
| identify | The node will reply with an english sentence describing how long it has been up |
| debug | casue the receiving node to turn on (or not) debugging features. |
| answer | the worker node will answer the incoming voip call or not. There will be the word "true" or "false" after the word answer, to say if we answer the call. |
| transferudp | Used in the exchange to advise remote nodes of where the local udp receive port is |
| queryaddress | no longer used, as it has been superseded by the transferudp command |
| adviseaddress | no longer used, as it has been superseded by the transferudp command |
| quitnow | can only be processed by the Supervisor, which causes this sofaswitch application to die |
| help | cause the recipient node to reply with the list of understood commands |
| querydestaddress | no longer used, as it has been superseded by the transferudp command |
| senddtmf | advise the recipient worker to send the folling dtmf stream to the remote voip node. Typically, a voip worker would receive this message, and send dtmf to the other end of the call. |
| requestnewid | A Supervisor will receive this message, create a new Worker, and reply with the id of the created Worker. An argument can be supplied. Currently, the only entity which uses this is the conference room supervisor, where the argument specifies the name of the conference room. In this case, the created newid is placed in the specified conference room. The conference room is created, if it is not there. |
| setreceivedmsgfile | An answering machine Worker will receive this message, and write incoming audio to the specified file |
| setannouncementfile | An answering machine Worker will receive this message, and read audio from the specified file, and send this audio to the address specified in the previous transfer udp message. |
"command"0
which contains no colon character, and is terminated by a zero byte. As before, the quotes are there to illustrate it is a string, and are not included in the packet.
| quitnow | Cause the server to end. On ending, every application connected to the server will end also. |
| list | Cause the server to respond with a list of all the currently connected nodes. The command "list" is similar to the notion of presence as used in XMPP, except that here, presence is driven by the client. |
| controller10 | sends | voip41: transferudp ssam3 | (worker to worker) |
| voip41 | sends | ssam3: 192.168.1.109:62054 | (worker to worker) |
| ssam3 | sends | voip41: 192.168.1.54: 45021 done | (worker to worker) |
The controller10 tells voip41 that it has to send audio to the ssam3 node. Once the controller has sent this message, the controller has no further news on if anything has happened. voip41 receives the transferudp message and advises ssam3 of an ip/port address. This second message in the transfer advises ssam3 of where to send audio data to. ssam3, on receipt of message 2, can start sending audio to the address specified in message 2. ssam3 then advises voip41 of the address that voip41 should send audio to. When voip41 receives message 3, voip41 can start sending audio to 192.168.1.54:45021. On receipt of message 3, voip 41 will send no more messages, as message 3 contains the word "done"
The list above describes the messages, as received by the server. From the description above, the server will change the message around to contain the sender's name. Consequently, the messages reeived by the three workers in this example will be
| voip41 | receives | controller10: transferudp ssam3 | (worker to worker) |
| ssam3 | receives | voip41: 192.168.1.109:62054 | (worker to worker) |
| voip41 | receives | ssam3: 192.168.1.54:45021 done | (worker to worker) |
| controller10 | sends | voip:requestnewid | (worker to supervisor) |
| voip | sends | controller10:requestnewid voip20 | (worker to worker) |
Then, controller10 will send messages to voip20 asking it to transferudp, or make a call, or whatever.
| controller10 | sends | voip:requestnewid | (worker to supervisor) |
| voip | sends | controller10:requestnewid voip20 | (worker to worker) |
| controller10 | sends | voip20:callnode h323:h323.voxgratia.org | (worker to worker) |
| voip20 | sends | controller10: callestablished voip20 | (worker to worker) |
The callestablished message is sent when the call is up and running. In this example, controller10 knew who was making the call. However, it need not be so. The controller does not have to request a new id to initiate a voip call to a remote node. Instead, the controller can ask the voip supervisor to create a new call.
In this case the voip supervisor will create a voip worker, and get the voip worker to make the voip call. The voip worker will then advise (from this example) controller10 that the call is established, and the id of the voip worker. Here, the controller worker is required to wait for the call established message to find out who is managing the call.
Thus, the voip supervisor can be used to create a Worker (which then implements the callnode command) In this case, a suitable message flow would be:
| controller10 | sends | voip:callnode h323:h323.voxgratia.org | (worker to supervisor) |
| voip20 | sends | controller10: callestablished voip20 | (worker to worker) |
Once the controller know who the worker nodes are, it can start the transferudp sequence. However if the voip worker and ssam worker complete the transferudp sequence before the call is established (from the voip perspective), and the controller then tells the ssam worker the announcement file, the very first part of the announcement file will not be heard by the remote voip node.
Consequently, the ssam announcement file should be set after the call is established. The ssam received message file can be set at any time. Although, setting the ssam announcement file too late will mean that the first part of the incoming voice call will be missed.
In this example, the box at 192.168.1.100 is being asked to call the node at box 192.168.1.200. The answering machine resides at address 192.168.1.2. The box at 192.168.1.100 has registered with the controller under the basename of ssvoip_send, as it is responsible for initiating calls with remote computers.
Notice that in Example of receiving a voip call and Example of creating outgoing voip call. we have shown "both ends" of the voip call.Incoming, and outgoing. One controller, and one answering machine, was used to manage both ends. Further, we could have placed the incoming and outgoing ends of the call on the same sofaswitch voip application.
In this example, ssvoip_send1 reported to the contrcontroller2 let the call run for 5 seconds, and then sent a hangup message to the two worker nodes managed by controller2.
| send time | sent by | message |
|---|---|---|
| 0:31.493 | controller1 | ssvoip_send:REQUESTNEWID |
| 0:31.494 | controller1 | ssam:REQUESTNEWID |
| 0:31.494 | ssvoip_send | controller1:REQUESTNEWID ssvoip_send1 |
| 0:31.495 | ssam | controller1:REQUESTNEWID ssam1 |
| 0:31.586 | controller1 | ssvoip_send1:callnode h323:192.168.1.200 |
| 0:31.586 | controller1 | ssam1:setannouncementfile sample_message.wav |
| 0:31.626 | controller1 | ssam1:setreceivedmsgfile recorded_ssam1.wav |
| 0:31.990 | ssvoip_send1 | controller1:CallEstablished ssvoip_send1 |
| 0:31.991 | controller1 | ssvoip_send1:transferudp ssam1 |
| 0:31.992 | ssvoip_send1 | ssam1:transferudp ssam1 192.168.1.100:60000 |
| 0:31.999 | ssam1 | ssvoip_send1:transferudp ssam1 192.168.1.2:64000 done |
| 0:37.017 | ssvoip_send1 | controller1:Terminating |
| 0:37.017 | controller1 | ssam1:hangup |
| 0:37.018 | ssam1 | controller1:Terminating |
Notice that in Example of receiving a voip call and Example of creating outgoing voip call. we have shown "both ends" of the voip call.Incoming, and outgoing. One controller, and one answering machine, was used to manage both ends. Further, we could have placed the incoming and outgoing ends of the call on the same sofaswitch voip application.
In this example, controller2 let the call run for 5 seconds, and then sent a hangup message to the two worker nodes managed by controller2.
| send time | sent by | message |
|---|---|---|
| 0:31.871 | ssvoip_rec2 | controller:QueryAcceptIncoming derek [192.168.1.100] |
| 0:31.874 | controller2 | ssam:REQUESTNEWID |
| 0:31.875 | ssam | controller2:REQUESTNEWID ssam2 |
| 0:31.966 | controller2 | ssvoip_rec2:answer true |
| 0:31.966 | controller2 | ssam2:setannouncementfile sample_message.wav |
| 0:31.984 | ssvoip_rec2 | controller2:CallEstablished ssvoip_rec2 |
| 0:31.984 | controller2 | ssam2:setreceivedmsgfile recorded_ssam2.wav |
| 0:31.984 | controller2 | ssvoip_rec2:transferudp ssam2 |
| 0:32.049 | ssvoip_rec2 | controller2:CallEstablished ssvoip_rec2 |
| 0:32.049 | ssvoip_rec2 | ssam2:transferudp ssam2 192.168.1.200:60001 |
| 0:32.050 | ssam2 | ssvoip_rec2:transferudp ssam2 192.168.1.2:64001 done |
| 0:36.966 | controller2 | ssvoip_rec2:hangup |
| 0:36.966 | controller2 | ssam2:hangup |
| 0:36.967 | ssvoip_rec2 | controller2:Terminating |
| 0:36.967 | ssam2 | controller2:Terminating |
1.4.6