Skype Logo Take a deep breath™.
Buy Skype Credit · Help ·
  • Download
  • Use Skype
  • Business
  • Shop
  • Account
Avo Nappo

App2App Streams, The Mystery and The Workaround

By My status Avo Nappo on August 2, 2007 in Examples, Tutorials.

Skype Public API has two built-in methods for 3rd party applications to communicate with each-other: streams and datagrams. As The API reference states, datagrams are a non-guaranteed packet delivery method, vaguely similar to UDP while streams are a continuous, guaranteed delivery and packet order method, similar to TCP/IP connections. Except that there has been a long-standing problem with API streams apparently not really delivering the packets in the same order.

This phenomenon was particularly easy to reproduce with short streams of small packets. Pumping a string of 10 packets of 100 bytes each, pretty much always resulted in packets coming out from the stream in reverse order.

After long search, we finally found the root cause of this problem and while it appears to be too difficult to fix it in a hurry, we can at least offer firstly an explanation of what is going on and secondly a (hopefully temporary) workaround.

Firstly, the good news is - the API itself is working fine. The problem occurs when the receiver part of an application uses Skype4Com library. A Skype4Com receiver works by handling OnApplicationReceived events. On this event, the handler uses IStream.Read method to retrieve the data from the stream and then process it.

The "occasionally random packet order" phenomenon is caused by two facts. Firstly, IStream.Read is for practical purposes not instantaneous. Secondly, the event handler is re-entrant. When a string of packets arrive, the next packet may trigger OnApplicationReceived event while the previous packet is still in process of being read and processed. This causes the event handler to essentially go into recursion. And as with all recursions, the processing out of it will occur in reverse order.

For purpose of visualization, lets take following example.

A stream of five packets is sent out from transmitter, packets are numbered 1, 2, 3, 4 and 5. Now, lets examine what may well happen on receiver end.

1. First packet arrives. OnApplicationReceived event is fired.
2. Application handles the event, reads data and processes it. Everything is still fine.
3. Second packet arrives. Again, OnApplicationReceived event is fired.
4. Application handles the event.. starts reading the data from stream, but alas..
5. Third packet arrives before the reading/proccessing of the second packet is complete.
6. Your application launches its event handler once more, going into recursion.
7. 4th packet arrives, recursion is now 3 instances deep.
8. Processing of packet no. 4 gets finished and is processed.
9. Processing of packet no. 3 gets finished and is processed.
10. Processing of packet no. 2 gets finished and is processed. The event handler is now out of recursion.
11. Packet no. 5 is received, event handler gets fired for the last time.

The apparent order of packets from receiver looks like this: 1, 4, 3, 2, 5.

Now the obvious solution to solve this problem would be to implement a packet-by-packet protocol in your application. Send one packet at a time from transmitter, report back with an ACK message from receiver, then send next packet. But hey, where's the fun in that and wouldn't it make streams as such pretty much useless? After all, their only excuse and advantage over datagrams is supposed to be the guaranteed order, guaranteed delivery being mostly myth in any case.

Another (and somewhat more challenging) method is to make your !OnApplicationReceived event handler re-entry compliant. To do this you would have to detect when the handler goes into recursion and then dynamically buffer the received data until the handler digs itself out of recursion. Then process the data out of your buffer in correct order and clear the buffer. Until next recursion.

As writing re-entry compliant handlers is somewhat less trivial than your normal linear programming, here's two examples (Delphi and VBScript).


//---------------------------------------------------------------
// This example uses TStrings list as dynamic buffer. 
// Note that you need to initialize both Buffer and RecursionLevel variables.

Var Buffer: TStrings;
RecursionLevel: Integer;

Procedure TForm1.SkypeApplicationReceiving (
Sender: TObject; const
pApp: IApplication; const
pStreams: IApplicationStreamCollection);

Var S : String;
I, Z : Integer;

Begin
if pStreams.Count > 0 Then
Begin
// Exiting handler if the stram contains no data,
if pStreams[1].DataLength = 0 Then Exit;

Inc(RecursionLevel);

// Appending empty string slot at the end of the buffer
// and storing it's position in Z
Buffer.Append('');
Z := Buffer.Count - 1;

// Reading data out of stream. The following is the most likely place
// your handler will stall and cause recursive re-entries
S := pStreams.Item[1].Read;
Inc(ReadCounter);
Buffer[Z] := S;

// This is where we process the data. First we check if we are in recursion (RecursionLevel > 1)
// If we are in recursion, we do nothing - just accumulate received data in dynamic buffer.
// When we are out of recursion tho, the data gets processed out of Buffer in FIFO order.
// After each processed string, corresponding Buffer item is deleted.
if RecursionLevel = 1 Then
Begin
Z := Buffer.Count - 1;
For I := 0 to Z do
Begin
// This is where the data processing should go.
// In this example, we just log 1st 5 characters and recursion depth.
Log('SenderID: ' + Copy(Buffer[0], 1, 5) + ' Recursion depth: ' + IntToStr(Z-I));
Buffer.Delete(0);
End;
End;
Dec(RecursionLevel);
End;
End;

NB! Note that if the handler above goes really deep into recursion then you will eventually end up with stack overflow. If you start getting stack overflows with the example above, try running it without Delphi IDE.

Here is the VBScript example:


    Dim arrBuffer
    Dim nLevel : nLevel = 0
    
    Public Sub Skype_ApplicationReceiving(ByRef aApp, ByVal aStreams)
      
      If aStreams.Count Then 
        nLevel = nLevel + 1
        If nLevel = 1 Then 
          arrBuffer = Array()
          ReDim arrBuffer(1)
        Else
          ReDim PRESERVE arrBuffer(nLevel)
        End If
        
        arrBuffer(nLevel-1) = aStreams.Item(1).Read
        nLevel = nLevel - 1
        
        If nLevel=0 Then 
          For i=0 to UBound(arrBuffer)-1
            WScript.Echo arrBuffer(i)
          Next 
          arrBuffer = Null
        End If
      End If
      
    End Sub

View blog reactions

Comments

Hello,
Is there a full src code in vb or c++ for this example?
Where can i find an example for the data manipulation of call streams?
Thank you

erez_l | Monday, Sep 17

> Is there a full src code in vb or c++ for this example?

No, but the VBScript version should not be too difficult to port if needed.

But anyhow, the post above was meant to be a temporary workaround. This problem is fixed in Skype4Com lib version 1.0.28 - incoming stream packets no longer cause events to be fired at receiver before previous events are handled.

Link to current beta version of 1.0.28:
https://developer.skype.com/Docs/Skype4COM

> Where can i find an example for the data manipulation of call streams?

Manipulation of a raw PCM stream is somewhat non-trivial, and we don't have any examples of that up on DevZone at this point - as it is strictly speaking, outside the scope of Public API. There is however an example on how to get access to the PCM stream via API:

https://developer.skype.com/Docs/Skype4COM/Example/VoiceStreams_pas

And no I don't have that in VB or C++ :)

anappo | Monday, Sep 17

Comment on this post

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Back to index

Subscribe to this blog
What? Tell me more…

using RSS Subscribe
via Bloglines Subscribe in Bloglines
using Newsgator Subscribe in NewsGator Online
with MyYahoo
with Google Add to Google
with My AOL Add to My AOL
with Anothr.com Subscribe by Anothr
with netvibes Add to Netvibes
with email Get email updates
Skype Developer Newsletter

Sign up now for all the latest news, tips and tricks on using Skype Public API.

Developer Zone

  • Home
  • Docs
  • Tutorials
  • Download
  • Support
  • Certification
  • Blog
  • Community
  • Help
  • Find...
Skype Blogs
  • Share Skype Blog
  • About Skype
  • Heartbeat
  • Developer Zone
  • Business
  • Jobs
  • Skype Prime
  • Skype Gear
  • Security
  • Garage
  • Mac
  • Linux
  • Eesti keeles
  • Töökuulutuste leht
  • 日本語
  • Česky
  • Deutsch
  • Français
  • Italiano
  • Brasil
  • United Kingdom
  • Svenska
  • Polski
  • United States

Recent posts

  • New Skype Certified product in June -- Trend Micro WTP for Skype
  • Developer Zone version 3 is out there
  • Developer Zone down for maintenance
  • Skype for Business Showcase in Stockholm, Sweden
  • Skype sponsors Python Developer Conference (sold out)
  • Support updates and fixes in EM 2.0
  • New Skype Certified products in April
  • Update from Eion Robb and Brandon Holland
  • Long time partners... SDP, what's going on?
  • Time to Join Skype at the eBay DevCon

Archives

  • June 2008
  • May 2008
  • April 2008
  • March 2008
  • February 2008
  • January 2008
  • December 2007
  • November 2007
  • October 2007
  • September 2007
  • August 2007
  • July 2007
  • June 2007
  • May 2007
  • April 2007
  • March 2007
  • February 2007
  • January 2007
  • December 2006
  • November 2006
  • October 2006
  • September 2006
  • August 2006
  • July 2006
  • June 2006
  • May 2006
  • April 2006
  • March 2006
  • February 2006
  • January 2006
  • December 2005
  • November 2005
  • October 2005
  • September 2005
  • August 2005
  • July 2005
About us · Partners · Jobs · Prices · Security
Privacy policy · Legal · © 2008 Skype Limited