Top Menu

quickP2P API (java/android SDKversion) -programming instructions

AUXILIARYMATERIAL:

- "qp2p_SimplestDemo_FileTransfer" demoeclipse android application project

quickP2P API (java/android SDKversion)  -programming instructions

Startingwithjava/android SDK API(withandroid SDKsamples)

1. quickP2P lib delegate/eventsystem

1.1Events in quickP2PAPI

1.2 Callbacks in quickP2PAPI

1.3Event/Callback handler execution

2. Peer session creation

3. Peer lookup queries

4. Peer state tracing /changes notifications

5. Instantmessages

6. Direct communicationtunnelcreationbetweenpeers

7. Using virtualindexstorage

8. Security, keyexchangeand dataencryption/decryption

9. Required app permissions

10.Completelistofdelegates interfacesusedin ConnectionManagerwiththeirin-prints

1. quickP2P lib delegate/eventsystem

qP2P lib Java API delegate/event system is based on delegates. Since Java supports anonymous objects creation, this becomes quite usable. Just watch out for garbage collector when using anonymous objects. You need to provide object holding right named method to serve as a delegate to event/callback handler. We provided interfaces your object can implement those methods from but important to note is fact that this is not necessary. The method needs to have the right name and right arguments to be invoked by event/callback in delegate object.

*note: One problem we have with this currently is lack of ability of easy code fuscation because of method name textual bindings. Fuscation will change method names and JNI wrapper will be unable to find them. This still can be achieved if you close all qp2p API interaction in separate classes then you can just omit those classes from fuscation.  

Now we will show you some basic examples of using events/callbacks and there is no better way than giving simple examples. Once hooked event handler will be executed every time corresponding event triggers until we un-hook it manually or simply destroy objects. On the contrary, callback is usually passed as some method argument. It will execute only once to handle particular method execution asynchronous response.

1.1Events in quickP2PAPI

- When you hook event once, it will execute response every time correspondent request arrives until you unhook it.

- If you have both event hooked and callback provided to execute on request completion first event is triggered then callback

We will now give you examples of hooking events. Here is example of hooking event to handler that is member of calling class:

publicclass TestActivity extends Activity implements

qp2plib.ConnectionManager.iPeerConnect_delegate {

private qp2plib.ConnectionManager cm = null;

@Override

protectedvoid onCreate(Bundle savedInstanceState) {  

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

//we create new instance of connection manager class

cm = new qp2plib.ConnectionManager(new InetSocketAddress("supernode1.p2p-api.com",80));

cm.addEventHandler(ConnectionManager.ConnectionManagerEvent.onPeerConnected.getCode(), this);

}

//this is implemented method qp2plib.ConnectionManager.iPeerConnect_delegate

publicvoid onConnectionManager_PeerConnect(Peer peer, Guid TransactionUID) throws IOException {

       //... handler body.

}

}

Here is how we would hook the same event to anonymous object method:

publicclass TestActivity extends Activity {

private qp2plib.ConnectionManager cm = null;

@Override

protectedvoid onCreate(Bundle savedInstanceState) {  

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

//we create new instance of connection manager class

cm = new qp2plib.ConnectionManager(new InetSocketAddress("supernode1.p2p-api.com",80));

cm.addEventHandler(ConnectionManager.ConnectionManagerEvent.onPeerConnected.getCode(),

new qp2plib.ConnectionManager.iPeerConnect_delegate(){

       publicvoid onConnectionManager_PeerConnect(Peer peer, Guid TransactionUID) throws IOException  {

              //... handler body.

       }

});

}

}

Reference to delegate object will be kept in inner event/callback object. That will prevent the garbage collector from destroying an anonymous object that serves as a delegate.

1.2 Callbacksin quickP2PAPI

Callbacks are usually given as arguments of functions having unpredictable time for completion to trigger when that result becomes available. There are related only to particular function call/response.

When some event handler is set and we also have callback defend to trigger on the same particular event, both event handler and callback handler will be executed, first event handler then callback handler.

Now here is a use of callbacks demonstration on cm.Connect method. Callback delegate is the last argument of ConnectionManager.connect method. This callback executes when a nat-traversal operation completes. Here is callback handler in an anonymous object:

cm.Connect(remotePeer, ConnectionType.TCP.getCode() , null, new

ConnectionManager.iPeerConnectCallback_delegate(){

        publicvoid onConnectionManager_PeerConnectCallback(Peer peer,

              Guid PeerCheckpointUID, Guid PeerUID, Guid TransactionUID,

              boolean Success, int PeerConnectFailureReason_code) {

              //...

              peer.getTunnelSocket().getOutputStream().writeBytes("Hello through NAT!");

              //...

                          

       }

});

Reference to delegate object will be kept in inner event/callback object. That will prevent the garbage collector from destroying an anonymous object that serves as a delegate.

And here would be an example using calling class as a delegate with the appropriate method defined in it:

cm.Connect(remotePeer, ConnectionType.TCP.getCode() , null, this);

….

//this is implemented method qp2plib.ConnectionManager.iPeerConnect_delegate

publicvoid onConnectionManager_PeerConnect(Peer peer, Guid TransactionUID) {

       //...

       peer.getTunnelSocket().getOutputStream().writeBytes("Hello through NAT!");

       //...

}

So basically we use common programming patterns you probably used by now in Java and Android SDK. The important thing to note about event and callback handlers triggered by quickP2P API is that they will be executed in “some” thread API may create on need. That thread will probably not be UI thread. Watch out when interacting with UI elements you need to transfer UI directives to UI thread. This is a rule that stands for all platforms/languages and not just for Java. Consider following example:

publicvoid onConnectionManager_SessionOpenCallback(ConnectionManager sender,boolean success,int SessionFailReason_code){

   finalint code_val = SessionFailReason_code;

   runOnUiThread(

       new Runnable() {

              privateintcode = code_val;

              publicvoid run() {

               ((TextView) findViewById( R.id.textView1)).setText("Session opened!");

           }

         }

       );

     cm.Query("\"Properties.some_meta\":\"test456\"",null, 0, 0);

    }

We want to set text for textView1 textbox , our handler onConnectionManager_SessionOpenCallback will be exacted from “some” thread so we need to transfer text changing directive to UI thread using runOnUiThread method.

 

1.3 Event/Callback handler execution

 

In quickP2P Java, event/callback handlers are executed in threads API creates by need. This means that if you plan to start some long running operation when even/callback triggers you can just stay in the same thread and do tasks. API will create other new threads if needed for events that happen while that long-running task is being executed. 

Also, there is conservation mechanism, because thread creation in java can be expensive. After the thread is used for handler execution it will not be destroyed for some time in order to possibly execute some new handler if it arrives. 

Fact that event/callback arguments are executed in "some" threads imposes the rule that you can't interact with UI directly from them. You need to transfer execution of UI interaction code to UI thread by using context.runOnUiThread or something similar.

Consider following code from demo project:

Peer remotePeer = peers[0];

//we found our remote peer, now we will create socket tunnel to him

//we found that peer now we will connect to it. Note that track_connect_transaction_uid will match TransactionUID argument in [void onConnectionManager_PeerConnect(...]

Guid track_connect_transaction_uid =

     cm.Connect(remotePeer, ConnectionManager.ConnectionType.TCP.getCode(),null, new ConnectionManager.iPeerConnectCallback_delegate() {//NEXT WE WAIT FOR RESULT IN [void onConnectionManager_PeerConnectCallback(...] in anonymous object below , ON OTHER SIDE  [void onConnectionManager_PeerAccept(...] will trigger instead

publicvoid onConnectionManager_PeerConnectCallback(Peer peer,

                                                    Guid PeerCheckpointUID,

                                                    Guid PeerUID,

                                                    Guid TransactionUID,

                                                    boolean Success,

                                                    int PeerConnectFailureReason_code) {

//...failed handling code omitted...

       if(Success){

//TUNNEL IS CREATED!

//In quickP2P java we can just keep this thread because it is certainly not UI thread and other threads for other

//handlers will be automatically created when needed, so we can continue our long running operation form this thread

       DataInputStream in   = null;

       DataOutputStream out = null;

       try {

              in = new DataInputStream(peer.getTunnelTCPSocket().getInputStream());

              out = new DataOutputStream(peer.getTunnelTCPSocket().getOutputStream());

       } catch (IOException e) {

              try{

                     peer.getTunnelTCPSocket().close();

              }catch (IOException exs) {}

ShowMessageOnUIThread("Error while sending file(s)! Aborted...");

              return;

       }

       ByteBuffer little_endian_buff = ByteBuffer.allocate(8);

       byte[] buff = newbyte[8192];

       for(String FileToSend : MainActivity.this.FilesToSend)

       {

              String[] tmp = FileToSend.trim().split(File.separator);

              String name = tmp[tmp.length - 1];

              BufferedInputStream FS = null;

              try {//we open file for reading

                     FS = new BufferedInputStream(new FileInputStream(new File(FileToSend)));                        

                     char[] cname = name.toCharArray();

                     out.write(newbyte[] {(byte)cname.length});//we send name length

                     out.writeBytes(name);

                     little_endian_buff.order(ByteOrder.LITTLE_ENDIAN);

                     little_endian_buff.putLong(new File(FileToSend).length());

                     little_endian_buff.rewind();

                     out.write(little_endian_buff.array());//we send file length first

                     int read = 0;

                     //we read and send file blocks

                     while ((read = FS.read(buff, 0, buff.length)) > 0)

                               out.write(buff, 0, read);

                     FS.close();

                     //we wait for some response as confirmation

                     read = in.read(buff);

                     final String fileName = FileToSend;

                     if (read > 0)

                            ShowMessageOnUIThread("File " + fileName +" sent!");

              } catch (IOException e) {

                     ShowMessageOnUIThread("Error while sending file(s)! Aborted...");

                     break;

              }

       }

       try {//we close socket

              in.close();

              out.close();

       } catch (IOException e) {}

       try{

              peer.getTunnelTCPSocket().close();

       }catch (IOException e) {}

       }   

}

} );

We put our code for sending files through tunneled TCP socket right in handler body. This is the ok place because we know this will not block API and that other events happening to meanwhile will be served by other threads API will create on need. So we can just place here our possibly long-running file transfer operation. But also note there few calls to ShowMessageOnUIThread (a function that looks like this:

protectedvoid ShowMessageOnUIThread(String message){

       final String msg = message;

       MainActivity.this.runOnUiThread(//Event and callback handlers in quickP2P java are executed in random threads so when we interact with UI we need to send execution to UI thread

              new Runnable() {

                     publicvoid run() {

                           ShowMessage(msg);

                     }

              }

       );

}

protectedvoid ShowMessage(String message){

   Toast.makeText(this, message, Toast.LENGTH_SHORT).show();

}

We want to show a message box to the user, and a message box is UI element so we send execution of message box creation to UI thread.

2. Peer session creation

For quickP2P client, to be able to do any operation like tunnel opening, sending an instant message, searching peers, virtual index operation ... we first need to join an abstract peer-to-peer network like you connect to the internet when your computer powers up. Steps for joining application peer to the super-node network would be this:

1- CreateConnectionManager object to handle operations, set required C.M. properties, handles...

It's always requiredtosetaccesstokens:

cm.setAccessTokens("00000000-XXXX-XXXX-XXXX-XXXXXXXXXXXX","00000000-XXXX-XXXX-XXXX- XXXXXXXXXXXX","");

The first argument is provider UID, second application UID, third is access key. You can see this values in you provider portal.

2- Set some meta-data so other peers can find us and we can find them by filtering values

cm.getLocalPeer().set("some_meta","blablabla");

cm.getLocalPeer().set("email", "someone@somepeople.com");

cm.getLocalPeer().set("gender","male");

you can also alter these properties later while the session is opened, you need to call

cm.BrodcastStateChange()toupdatelocalpeerinformationonnetwork.

InitateConnectionManager.Open(callback) toopensession

-Wait open session completion handler to execute

(note, after session open completion avoid initiating connect - "tunnel open" operation in next 2-3 sec to give time to API to inspect your network environment properly)

Once we have established a session with super-node we can:

- Query for other peers on the network (Peer lookup) with mongo DB-like queries in our application scope

- Register for peers’ status tracking and receive notifications about changes

- Send/Receiveinstantmessages

- Open communication tunnels to other peers and other peers can open tunnels to us

- Use virtual index manager if we need it:

- Create|Delete|EditVirtualNetworks|Users

- Search virtual networks|users with mongo DB like queries

- Join|Un-joinusers tonetworks

....

We will talk about all these operations in following texts

3.Peerlookup queries

 

Peer lookup queries query for peers that are currently online. A query request is initiated using

Guid transaction_uid = ConnectionManager.Query(String QueryText , iPeerQueryCompleted_delegate callback_delegate, int page  , int pageLimit );

or handy version if we need to check single peer:

Guid transaction_uid = ConnectionManager.QuerySingle(Guid PeerUID, iPeerQueryCompleted_delegate callback_delegate);

methods of ConnectionManager object. Callback and event handler method has in-print like this:

publicinterface iPeerQueryCompleted_delegate{

       void onConnectionManager_PeerQueryCompleted(Peer[] peers,int count,boolean Completed, Guid QueryTransactionID,int Page,int PageLimit,int TotalPeers);

}

Agruments:

FoundPeersListofpeersreturnedbyquery

CountNumberofpeersreturnedbyquery

CompletedIfcompletedthentrue,notthatsometimesresultwillcontainpeersbutthis valuewillbe        false.Thiscanhappenforexampleif10000000peersarefoundbysearch criteria, and               operationtimeoutexpiresbeforeallresultsarecollected

QueryTransactionIDTransactionIDofqueryrequest

PagePagereturnedifpaginationexists

PageLimitNumberofpeersperpageifpaginationexists

TotalPeersTotalnumberofpeersmatchedbyquery

Eventhookexample:

...

cm.addEventHandler(ConnectionManager.ConnectionManagerEvent.onPeerQueryResponse.getCode(), this);

...

publicvoid onConnectionManager_PeerQueryCompleted(Peer[] peers, int count,

                     boolean Completed, Guid QueryTransactionID, int Page,

                     int PageLimit, int TotalPeers) {....

Query text format is mongo DB like a query with the exception that you don't put opening and closing brackets.

Let's say we defined some meta properties for our peer like this:

cm.getLocalPeer().set("City","PaloAlto");

cm.getLocalPeer().set("Sex","female");

cm.getLocalPeer().set("Email","peer@peers.com");

cm.getLocalPeer().set("Age","31");

Here are some examples of queries that will include this peer in result:

Findpeerwithemail"peer@peers.com"thatiscurrentlyonline:

cm.Query("\"Properties.Email\":\"peer@peers.com\"");

Findpeerswithemailcontainedinthislist[peer@peers.com,mike@peers.com,a123@bla.com]thatarecurrently online:

cm.Query("\"Properties.Email\":{$in:[\"peer@peers.com\",\"mike@peers.com\",\"a123@bla.com\"]}");

FindfemalepeersfromPaloAltothatarecurrentlyonline:

cm.Query("\"Properties.City\":\"PaloAlto\",\"Properties.Sex\":\"female\"");

FindfemalepeersfromPaloAltowithabove30thatarecurrentlyonline:

cm.Query("\"Properties.City\":\"PaloAlto\",\"Properties.Sex\":\"female\",\"Properties.Age\":{$gt:30}");

You can test your queries on provider portal. To make more advance queries check out mongo dg query syntax.

4. Peer state tracing / changes notifications

Thereisoften aneedfor somepeertohavesomestate onnetwork, likeaway, busy, available .... You probablyseen thatascommonfeatureof chat applications. Ifyouthinkyoucouldachievesame functionalityusingInstantmessagesor somethingelseyouarerightbutthis providesyoumoreelegant wayoftransferringsuchinformationtopeersinstantly. Alsotherearecertainsituationswhen thiscomes handyasirreplaceablefeature. Imagine youhavesomechatlikeapplicationandyoukeepyour peer buddies in somelistor dictionary...your buddykid rips powercablesfromhis computersohis application

don'tmanagetonotify youthatheisoffline. Super-nodewillnoticethatin max90sec,andifyouregistered for thatpeerstatetrackingyouwillgetnotificationthatheisoffline.

Tousethis featureyouneedtohookeventtoacceptnotifications:

cm.addEventHandler(ConnectionManager.ConnectionManagerEvent.onPeerStateChanged.getCode(), this);

...

publicvoid onConnectionManager_PeerStateChanged(Peer peer, int State) {

                if(State==0){

//0-isreservedfordisconnectedstate,1-initialconnected state,allvalues>1areavailablefor freeuse

                                ActiveFriends.erase(peer.getUID());//Peergotdisconnectedweremoveitfromour internallist

                }

}

This is rarecasewhereyoudon'thave optiontousecallbackbecauseyouneverknowwhen notification willcome.

Alsoyouhavetoinformsuper-nodesthatyouwanttobenotified aboutthatpeer changes.Youdothat using following ConnectionManager functions :

public Guid RegisterStateTracking(Guid PeerUID);

public Guid RegisterStateTrackingMultiple(Guid[] PeerUIDList);

Usually you will call this function in handler of peer query response. From that moment you will receive notifications about state changes of peers that you registered tracking for.  You can also cancel tracking of peers using:

public Guid UnregisterStateTracking(Guid PeerUID);

public Guid UnregisterStateTrackingMultiple(Guid[] PeerUIDList);

CommonplacetoplaceRegisterStateTrackingmethodisin abodyof peer query responsehandlerbecausethatis momentwhenyougetfresh informationofwhoisonline. If peer goesoffline youwill get qp2plib.Peer.DisconnectedState (=0)stateargumentvalue. If he comesbackhe needs toinform youthatheis back onlineand youagainneedtoregisterfor itsstatetrackingagain.So statustrackisvaliduntilpeergoesoffline, if he comes backyouneedtoregisterstatetrackingagain.Also youseethereisa needtocombineinstant messageswith thistomakeitfullyusable ,why:

- You could dopeerqueryand you wouldgetfresh informationabout onlinepeers, butafter30secsomepeerthat shouldbe visibleto youby particular applicationimplementation appearsonnetwork. Youdon'tknowthatheisonlineinthat moment andyouwouldnotknowthatuntil youdopeerqueryagain.  Buthealsodoespeer queryandheknows thatyouareonlineso allhehas todois to informyouaboutthat. Hecando thatbysimply sendingyousomeinstantmessage.

cm.SendInstantMessage(remotePeer,"Hey, I'am here!", APP_COMMANDS.IM_ONLINE );

...

Then in receive instant message handler you would know you need to update your peer list.

publicvoid onConnectionManager_ReceiveInstantMessage(Guid FromPeer, Guid FromPeerCheckpoint, Guid MessageUID, int MessageType,      byte[] Data) {

       if(MessageType==APP_COMMANDS.IM_ONLINE.getCode()){

//updateyour list or doquery tocheckallagain

}else....

}

Wewilltalkaboutinstantmessageslater soyoucouldfullyunderstandthiscode.

Instatechanged notificationhandler:

publicvoid onConnectionManager_PeerStateChanged(Peer peer, int State){...

youseetwoargumentsPeerandState. Firstis completefreshpeerobjectofpeerwhosestatechanged and Stateisnewstateof thatpeer.Younoticeit is just intvaluesoyouarefreetouseanyvalueinrange from2toInteger.MAX_VALUEfor your application.

Values0and1arereservedfor

qp2plib.Peer.DisconnectedState    = 0;

qp2plib.Peer.AuthentificatedState= 1;

Systemdepends onthemsoyoucannotusethesevaluesforanythingelse.

Tonotifyother peers aboutyour statechangecodewouldbesomethinglikethis:

- forexampleyouarenowbusy

cm.getLocalPeer().setState(MyApplicationPeerStateEnums.BUSY.getCode());

cm.BrodcastStateChange();

So weuse  cm.BrodcastStateChange()toinformothers ofourstatechanges.This functionalsoupdates our peerobjectinformationonsuper-nodesowealsouseitwhenwechangemetapropertieswhile sessionisactive.

Youdon'tneedtodostatebroadcastwhenyouareclosingsession/disposingC.M. because

ConnectionManager willdo thatautomatically.

5. Instant messages

Instant messages arecarriedusingsuper-nodes,andtheir purposeisto transfershortmessagesbetween peers innetwork.  Often itis notpracticaltoalwaysopen directcommunicationchannel betweentwo peers ifoneneedstoinformotheraboutsomethingbecausetunnelcreationtakes1-15s andalso maybe wewant firstother peertoconfirm thathewillinglyacceptsthat connection.Thesearecommonlysome controlmessagesfromyour applicationor somethinglikechatmessages.

Tobeabletoreceivethemyouneed tohookfollowingevent handler:

cm.addEventHandler(ConnectionManager.ConnectionManagerEvent.onReceiveInstantMessage.getCode(), this);

...

publicvoid onConnectionManager_ReceiveInstantMessage(Guid FromPeer, Guid FromPeerCheckpoint, Guid MessageUID, int MessageType,      byte[] Data) {

        if(MessageType ==APP_COMMANDS.TextMessage.getCode()) {

//dosomething

}

elseif (MessageType== APP_COMMANDS.IM_ONLINE.getCode()){

//dosomething

}

elseif (MessageType== APP_COMMANDS.ALLOW_FILE_TRANSFER.getCode()){

//dosomething

}...

}

Tosend instantmessageyouuseoneof4 overloadsofcm.SendInstantMessagefunction:

public Guid SendInstantMessage(Guid RemotePeerCheckpointUID, Guid RemotePeerUID, byte[] message_buffer, int message_len, int InstantMessageType , iSendInstantMessageComplete_delegate callback_delegate)

      

public Guid SendInstantMessage(Guid RemotePeerCheckpointUID, Guid RemotePeerUID, String text_message, int InstantMessageType, iSendInstantMessageComplete_delegate callback_delegate

      

public Guid SendInstantMessage(Peer Peer, byte[] message_buffer, int message_len, int InstantMessageType, iSendInstantMessageComplete_delegate callback_delegate

      

public Guid SendInstantMessage(Peer Peer, String text_message, int InstantMessageType , iSendInstantMessageComplete_delegate callback_delegate)

Exampleof sendinstantmessage request:

cm.SendInstantMessage(OtherPeer,"Hi man!", APP_COMMANDS.TextMessage.getCode());

Younoticeall haveoptional argument toInstantMessageTypeused todistinguishthem, for exampleyou could havenumberof controlmessagesandtextmessages so this givesyoueasywaytodistinguishthem.

Youalsonoticethereiscallbackargument .That callbackwilltriggerwhenmessageis delivered toyour super-node,itdoesnotmeanitis delivered toother peerwhen callbacktriggers.Messagewilltravel to destinationpeersupernodeandthen itwillbedelivered to it.

6. Direct communication tunnel creation between peers

ThemainAPIfeatureandprobablymainreasonwhy youuseitistheabilitytocreatedirectcommunication channels betweentwocomputersthat areboth behindNAT(router)devicesusingNATtraversal techniques.Themainadvantage ofthis API isthat asaresultyouwill getstandardplatformsockettouse fromthenon.Systemwillself inspectwhatisthebestmethodof tunnelcriterionbetween two peersand createit. Soyoudon'tneedtoknowanything aboutSTUN, NATportmappingprediction,UPnP, NATPMP, PCP...or anythingelsethathappens in thebackgroundand justwaitforyour prepared socket.

(Note - specialsituations when both peersareonsamelocal networkor evenonsame computerare handled byAPIsodatawillbetransferred locallyin thatcasenotgoing overtheInternet).

Inpast textswetalkedabouthow tofindsomepeer.When youknowthat,andyouwantto open direct communicationchannel youinitiateconnectrequest. On theotherside peer needs toacceptthis request sohandshakeprocedurecouldstart.

Youneed thistwoeventhandlers:

//Whenwemakeconnectrequest(tunnelcreation)wewaitforoperationcompletiontotrigger event

cm.addEventHandler(ConnectionManager.ConnectionManagerEvent.onPeerConnected.getCode(), this);

//Whensomeotherpeer initiatetunnelcreationtous,operationcompletionwilltrigger event

cm.addEventHandler(ConnectionManager.ConnectionManagerEvent.onPeerAccepted.getCode(), this);

...

//Aftersuccessfulconnectoperationthishandleristriggered

public void onConnectionManager_PeerConnect(Peer peer, Guid TransactionUID) { {

//peer.getTunnelTCPSocket() or peer.getTunnelUDPSocket() istunneledsocketreadyfordatatransfer

DataOutputStream out = new     DataOutputStream(peer.getTunnelTCPSocket().getOutputStream());

out.writeUTF("Hellothroughtunnel!");

//dosomethingelse

}

.....

//THISWILLTRIGGERWHENOTHERSIDECALL cm.connect(...TARGETINGLOCALPEER

voidonPeerAcceptEventHandler(qp2plib::Peer*remotePeer,qp2plib::Guid*transactionUID){

{

//peer.getTunnelTCPSocket() or peer.getTunnelUDPSocket() istunneledsocketreadyfordatatransfer

DataInputStream in = new DataInputStream(peer.getTunnelTCPSocket().getInputStream());

DataOutputStream out = new DataOutputStream(peer.getTunnelTCPSocket().getOutputStream());

byte[] buff = new byte [512];

int read= in.read(buff,0,512);

out.writeUTF("Hellothroughtunneltoyouto!");

//dosomethingelse

}

One peerinitiatesconnectrequestthatwouldinthiscaseuseeventhandlersthatlooklikethis:

Guid track_connect_transaction_uid =cm.Connect(remotePeer,        ConnectionManager.ConnectionType.TCP.getCode(),null, null);

andoncompletion:

publicvoid onConnectionManager_PeerConnect(Peer peer, Guid TransactionUID)

wouldtrigger onhis side. Onother side:

publicvoid onConnectionManager_PeerAccept(Peer peer, Guid TransactionUID)

would triggeronacceptingconnection.

if youneedtocontrolaccess- whocanconnectandwho cannot,youcanoverride "AcceptConnection

Reslover" byproviding your resolver callback function:

cm.setAcceptPeerConnectionResolver(new ConnectionManager.iAcceptPeerConnection_delegate() {

       publicboolean onConnectionManager_AcceptPeerConnection(

                           ConnectionManager sender, Peer peer, int connectionType_code) {

              if(<???some condition???>)

                     returntrue;

              else

                     returnfalse;

       }

});

Bydefaultall connectionrequest will beacceptedunless yousetcm.setBlockIncoming(true);

Tunnel accept/connectusingcallbackswouldlooklikethis:

SIDETHAT DOESCONNECT:

cm.Connect(remotePeer, ConnectionType.TCP, null,

    new ConnectionManager.iPeerConnectCallback_delegate() {

     publicvoid onConnectionManager_PeerConnectCallback(Peer peer,

       Guid PeerCheckpointUID, Guid PeerUID, Guid TransactionUID,

       boolean Success, int PeerConnectFailureReason_code) {

         if(Success){

              DataOutputStream out = new DataOutputStream(peer.getTunnelTCPSocket().getOutputStream());

             

              out.writeUTF("Hello through tunnel!");

              //...do something else...

         }else{

              Log.e("QuickP2P_API","Failed to connect, reason:" + ConnectionManager.PeerConnectFailureReason.values()[PeerConnectFailureReason_code].toString());

         }

       }

   }

);

SIDETHAT DOESACCEPT:

cm.setPeerAcceptedCallback(

new ConnectionManager.iPeerConnectCallback_delegate() {

publicvoid onConnectionManager_PeerConnectCallback(Peer peer,

              Guid PeerCheckpointUID, Guid PeerUID, Guid TransactionUID,

              boolean Success, int PeerConnectFailureReason_code) {

             

       if(Success){

              DataInputStream in =

                     new DataInputStream(peer.getTunnelTCPSocket().getInputStream());

              DataOutputStream out =

                     new DataOutputStream(peer.getTunnelTCPSocket().getOutputStream());

              byte[] buff = newbyte [512];

              int read = in.read(buff,0,512);

              out.writeUTF("Hello through tunnel to you to!");

       //...do something else...

       }else{

              Log.e("QuickP2P_API","Some peer tried to connect to us and failed, reason:" + ConnectionManager.PeerConnectFailureReason.values()[PeerConnectFailureReason_code].toString());

       }

 }

}

);

Thereisnothingspecialweneedtotalkaboutthis, youhaveareadysocketsoyoudo whatyouwantwith it fromthenon. Justhaveinmindthatyouareresponsible forsocketfromthenonsoyouneed tocloseit when younolongerneedit. Alsoasagoodpracticetofollowisthatyoucanjust savethat socketwhenyou finishthetaskfor newuse if needed.Soifyouneeddonewtaskinvolvingdatatransferwiththatsame

peer youcanjustpooloutthat socketinsteadof invokingtunnel creationagain(1-15sec). Youcandestroy suchsocketwhenyouleaveapplicationorif thatpeergoesoffline.

7. Using virtual index storage

Since quickP2Psystemgoal istoprovideyoucompleteenvironmentso youcouldfocus justonapplication we introduced permanent indexstoragesub-system. Wefigured outthat it wouldbeconvenient toprovide waytostoreandbeabletosearchsomecommonlyused things likeuser registrationdatathat is more closelyawareof peers.Youdon'tneed tousethis sub-systemif youhaveyourownin mind.You wouldjust needsomekey you wouldstoreinpeer properties toassociatepeerswithyourobjectslater.

Youcanimagineindexsystemasremotedatabasewith two tablesUserandNetwork.Of coursetheseare notordinarytables thathavefixedcolumns.Youcanstorewhateverproperty(name,value)youneedand later search them.BesidescommonoperationslikeSELECT/INSERT/UPDATE/DELETEwhatismost importantisthatUserobjectis awarewhichpeersareauthenticated with it.Multiple peers canbe authenticated withsingle userobjectandalsoopen peer canbeauthenticated with multipleuserobjects. Both userandnetworkobjecthaveUIDandcustompropertybag. Constrains arethatUserobjectmust have"Username"propertyandNetworkobjectneedstohave"Name"property.Therecannotbetwo User objects with sameusernamein your applicationscopeandsamestands for namefor networks. Ifyou somehowneed toenable differentsituationforsomereasonyoucangeneratethesepropertiesinsome special waybased onsomeotherproperty.

Userobjecthas columns "Memberof networks"becauseyoucanjoin/un-joinsomeuserfromnetwork and "Currentlybound Peers"whichholds listofcurrently boundpeerUIDs. Userobjectalsohaspassword propertyyoucannotseeinproviderportal.

Objectused for virtualindex management is accessed by cm.VNIndexManager().All index operations complete synchronouslybecausethereare nocriticaltimedependent functions . If youneedasynchronousexecution youneed toimplementit.  Alsoifthereis somethingwrongexceptionisthrown.

This is listofall Virtual index manageroperations:

publicnative VUser SaveUser(VUser user,String user_password);

- Insertsor updates virtualuser. Youpass user objectyouwantto saveandsaveit providingpasswordfor that user.IfVUser UIDpropertyis00000000-0000-0000-0000-000000000000systemwill doINSERTotherwiseit willdoUPDATE.Resultofoperationisfresh Userobjectlikeonindexserverinthatmomentthis also

meant PeerUIDSproperty willhavefreshlistofauthenticatedpeers. SoifUIDwas empty UIDwillgetsome real valuefromserverafteroperationcompletes.

This wouldbecodetocreatenewuser:

VUser newUser = new VUser();

newUser.setUsername("some_username");//THIS IS REQUIRED, IT IS ALIAS FOR newUser ["username"]

newUser.set("name", "Mike");

newUser.set("email", "This email address is being protected from spambots. You need JavaScript enabled to view it.");

newUser = cm.VNIndexManager().SaveUser(newUser, "somepassword");

             

if (newUser.getUID().equals(Guid.Empty)) {

    System.out.println("User is saved , assigned UID is: " + ewUser.getUID().toString());

}

then updateoperationwouldbe:

newUser.set("name", "Mike2");

newUser = cm.VNIndexManager().SaveUser(newUser, "somepassword");

Notethat afterthis operationin caseof creatingnew user getPeerUIDS()will beempty.Peer needto call inAuthenticateUsermethodinorder tohave hisUIDappearingin this list.

public VUser AuthenticateUser(String Username, String Password);

- Checksforexistingusernamewith passwordandifitexistsaddspeerUIDtoVUser getPeerUIDS()list, then returns VUserobject:

qp2plib.VUser u;

...

u = cm.VNIndexManager().AuthenticateUser("some_username", "somepassword");

If youperformAuthenticateUseranduser isalreadyauthenticated thatis notbadoperation. Itwill not havesideeffectsand youcanuseittogetfreshVUser objectfromindex server.

public VUser[] QueryUsers( String QueryCommandText, int skip, int limit , String ascOrderBy, String descOrderBy);

- SearchesforVUsersmatchingquery,addslistof found VUsers toprovidedresult_list

cm.getVNIndexManager().QueryUsers(list_to_fill,"\"Properties.City\":\"PaloAlto\""); If youexpectlargenumberofobjectstobereturned considerusingpagination. Inlasttwo optional parameters youentercomma separatedpropertynames.

publiclong QueryUsersCount(String QueryCommandText);

- SearchesforVUsersmatchingquery,then returnsnumberofobjectsfound

public void DeleteUser(Guid userToDeleteUID);

- DeletesVUserobjecthavingexactUID:

cm.getVNIndexManager.DeleteUser(u.getUID());

publicvoid ChangeUserPassword(Guid userUID, String old_password, String new_password);

- ChangesVUserobjectpassword.Passwordsarenotvisible toanyone. Incasethat user forgets password andwantstoresetit,youneedtousemagicvalue:

(requesterPeerUID.ToString()+ ApplicationUID.ToString()+userUID.ToString()).ToLower()

foroldpasswordtobeabletoresetit.

publicvoid JoinUserToNetwork(Guid userUID , Guid networkUID);

- UpdatesNetworkUIDSlistonindexserverinVUserobjectidentified byuserUIDargument byadding providednetworkUID. Youneedtoupdate your localVUserobjectmanuallyafter thator toreload.

publicvoid UnJoinUserFromNetwork(Guid userUID, Guid networkUID);

- UpdatesNetworkUIDSlistonindexserverinVUserobjectidentified byuserUIDargument byremoving providednetworkUID. Youneedtoupdate your localVUserobjectmanuallyafter thator toreloadit.

public VUser LogOffUser(String Username);

- RemovesPeerUIDfromPeerUIDSlistin VUserobject onindexserver. Returnsfresh copyofVUser

object.

public VNetwork SaveNetwork(VNetwork network);

- Insertsor updates (basedonUIDvalue)VNetwrokobject. IfUIDisemptyinsertwillbeperformed.Result is fresh copyofVNetworkobjectfromindexserver.

qp2plib.VNetwork newNetwork = new qp2plib.VNetwork();

newNetwork.setName("network1");//THIS IS REQUIRED, IT IS ALIAS FOR newNetwork["name"]

newNetwork.set("owner","Mike");

newNetwork = cm.VNIndexManager().SaveNetwork(newNetwork);

if (newNetwork.getUID().equals(qp2plib.Guid.Empty))

{

       System.out.println("Network is saved , assigned UID is: " +                 newNetwork.getUID().toString());

}

then updateoperationwouldbe:

newNetwork.set("owner","Jeff");

newNetwork = cm.VNIndexManager().SaveNetwork(newNetwork);

therecannotbetonetworkswithsamenamein applicationscope.

publicvoid DeleteNetwork(Guid networkToDeleteUID);

- Deletes VNetworkobjecthavingexactUID:

cm.VNIndexManager().DeleteNetwork(n.getUID());

public VNetwork[] QueryNetworks(String QueryCommandText, int skip, int limit , String ascOrderBy , String descOrderBy);

- SearchesforVNetworksmatchingquery,then returns listof foundVNetworkstorequester

cm.VNIndexManager().QueryNetworks(result_list,"\"Properties.Interes\":\"Ecology\""); If youexpectlargenumberofobject tobereturnedconsiderusingpagination. Inlasttwo optional parameters youentercomma separatedpropertynames.

publiclong QueryNetworksCount(String QueryCommandText);

- SearchesforVNetworksmatchingquery,then returns numberofobjectsfound

public VObject SaveObject(VObject obj);

- Insertsor updates (basedonUIDvalue)VObjectobject.If UIDisemptyinsertwill be performed.Resultis fresh copyofVObjectobjectfromindexserver.

qp2plib.VObject newObject = new qp2plib.VObject();

newObject.set("something","[\"1\",\"2\",\"9\"]");

newObject = cm.VNIndexManager().SaveObject(newObject);

if (newObject.getUID().equals(qp2plib.Guid.Empty)){

       System.out.println("Object is saved , assigned UID is: " +           newObject.getUID().toString());

}

then updateoperationwouldbe:

newObject.set("something","[\"1\",\"2\",\"9\",\"15\"]");

newObject = cm.VNIndexManager().SaveObject(newObject);

publicvoid DeleteObject(Guid objectToDeleteUID);

- Deletes VObjectobjecthavingexactUID:

cm.VNIndexManager().DeleteObject(newObject.getUID());

public VObject[] QueryObjects(String QueryCommandText, int skip, int limit , String ascOrderBy , String descOrderBy);

- SearchesforVObject-smatchingquery,then returns listof foundVObject-storequester

cm.VNIndexManager().QueryObjects(result_list("\"Properties.something\":\"15\"");

If youexpectlargenumberofobject tobereturnedconsiderusingpagination. Inlasttwo optional

parameters youentercomma separatedpropertynames.

publicnativelong QueryObjectsCount(String QueryCommandText);

SearchesforVObjectssmatchingquery,then returns numberofobjectsfound

Notewe used json arrayfornewObject.get("something") value.Indexserver willautomaticallydetectjson arraystring shapeor jsonobjectstring shapeand storeitinthatform. Itisimportantthat you don't have blankcharactersbefore"["or"{"inthiscasesbecauseindexserverwill notdoobject/array checkin that case.This enables you fullscaleuseofsub-objectsinsearch queries.

Youmightneed tohaveadministrativeapplicationorsitesoyoucangivesupporttoyour user thatcan performall indexoperation. When creatingithaveinmindthat youmustopen session(becomenetwork peer) togainaccess toindexserver.

8. Security, key exchange and data encryption/decryption

Quick P2PAPI provides youeasyway to obtainsecurekeystobeknownonlytopeers involvedwith tunnel.You usethis keystocrypt/decryptdataduringdatatransfer.

Securekeyexchangeisbased on"DiffieHellman"algorithm with encampment tooriginal concept. There is no"real"fixedkeypart, insteadsomeshortlived dataexistentforfewsecondsduringhandshake operationisused togenerate whatiscalled "Fixed part"inoriginal concept.Thiseliminatespossibilityfor attackerknowingfixedparttointrudedata integrityusinganyencryptionbreakingtheory.

32bytes ofsecuritydataisknownonly totwo peersinvolved withtunnel. Securitydata isgeneratedalways duringhandshakeprocedure. After each successful connect/accept ,generatedbytescanbeobtained

from handler functionPeerargument object :

publicvoid onConnectionManager_PeerConnect(Peer peer, Guid TransactionUID) {

byte[] secret32Bytes = peer.getTunnelSecretData();

....

}

publicvoid onConnectionManager_PeerAccept(Peer peer, Guid TransactionUID) {

byte[] secret32Bytes = peer.getTunnelSecretData();

....

}

quickP2PAPIhas built-inclasses thatfor AESencryption(chipering). AESclass-coreAESencryption

If supportfollowingAESvariants:

Key size:

·         128

·         192

·         256

Chiperingmode

                     ECB (each blockcryptwithkeyseparately, commonly used when partial decryptionisrequired )

                      CBC (default- eachblockisXOR-ed with previous cryptedblockthen cryptedwithkey. First16 chunkis XOR-ed usingCBCInitialVector. Single bytedifferencemakeswhole dataunusable )

Padding:

                None

                PCKS7

AESNetworkStream- usedtopreparedataforcrypttransfer whentunnel isstreambased(TCP). AESNetworkStreamisnotstandardized anditsdesigned foreasyusewithquickP2Ptunnels (youcanuseto else wareifbothsideshaveitas firstgate).AESNetworkStreamenables youtouse CBC modefor real time communicationbecauseprovides youabilitytousefairly sized blockscryptwithCBC chipering. Normallyin CBC modeyou wouldneedtogetall data(likewholefile) ,then decryptitsoyoucoulduseit. AESNetworkStreamcanusemoresecureCBCmode andprovideyoupartial datadecryption.

For UDPdatagramsyoucansimplyusecoreAESclass.

One thingtonotewhenworkingwith thisclassesisthat original andcrypted data donothavesamesize.

Crypteddatais usuallyslightlylargerthan original.

Nextissimpleexample ofcryptinganddecryptingtextwith coreAES class:

//we generate some random key just for test

byte[] key = qp2plib.Guid.NewGuid().getBytes();

qp2plib.AES a = new qp2plib.AES(qp2plib.AES.KeySize.Bits128 ,key, qp2plib.AES.Padding.PCKS7, qp2plib.AES.Mode.AESM_CBC);

//we generate some test text and convert it to bytes const char *original = "Hello I'am text to crypt"; int sLen = strlen(original);

byte[] buff1 = newbyte[512];

byte[] original = "Text to crypt".getBytes("UTF-8");

//we crypt

int cryptlen = a.Crypt(original ,0,original.length,buff1,0);

byte[] buff2 = newbyte[512];

//we decrypt

int dlen = a.Decrypt(buff1, 0, cryptlen, buff2, 0);

//we convert bytes to text buff2[dlen] = '\0';

String decrypted_text = new String(buff2,0,dlen);

This is samethingdonewithAESNetworkStream:

//we generate some random key qp2plib::AESNetworkStream ans( qp2plib::AES::AESKS_Bits128 , key);

byte[] key = qp2plib.Guid.NewGuid().getBytes();

//init AESNetworkStream with random key

qp2plib.AESNetworkStream ans = new qp2plib.AESNetworkStream(qp2plib.AES.KeySize.Bits128 , key);

//we generate some test text

byte[] original = "Hello I'am text to crypt".getBytes("UTF-8");

//we write data to stream to be crypted ans.WriteForCrypt((unsigned char*)original,0,slen); unsigned char buff1[512];

//we read crypted data form stream

int len = ans.ReadCrypted(buff1,0,8192);

//we now write crypted data for decryption ans.WriteForDeCrypt(buff1,0,len);

byte[] buff2 = newbyte[512];

//we read decrypted data

len = ans.ReadDeCrypted(buff2, 0, ans.AvailableDeCrypted());

//we convert bytes back to text buff2[len] = '\0';

String decrypted_text = new String(buff2,0,len);

UsuallyyoualsoneedtosetCBC initialvector (commonly"IV" )ifyouareusingCBC mode.Quick P2PAES class will generateitif notprovided basedonkey value. Inaboveexamplewedidn'tsetitbutit's recommended youdoso. Both classeshavemeans toprovideyoupointer tointernal CBC internal vectorbufferyoucan

setbytesbeforeyoudoanycrypt/decryptoperation.

9. Required app permissions

When using quickP2P java API on desktop operation systems It's recommended thatyouaddfirewallexception inlocalOSforyour application. Communicationwillfind itspathsanywaybutfirewall exceptioncandrastically increaseperformance.While developing youcan do thatmanuallybutyouwouldprobablyneed awaytodothatonusercomputers.Thisisgenerally most important for Windowsoperatingsystems becausefor other systemthis APIversionisusedmostly thisis defined inappbundle itselfor systemaskonceuser toallowthis behavior so thereis noneedtodo anything.

Only permission required by android applications for quickP2P for functioning is:

                android.permission.INTERNET

 

10. Complete list of delegates interfaces used in ConnectionManager with their in-prints

 

*Delegate interface for function deciding of peer acceptance. By default only

BlockIncoming parameter is considered

ConnectionManager - Connection manager

Peer - Peer requesting connection

ConnectionType - Requested connection protocol type

Allow - Output parameter as result of operation: true - alow connection , false - disallow connection

publicinterface iAcceptPeerConnection_delegate{

    boolean onConnectionManager_AcceptPeerConnection(ConnectionManager sender, Peer peer,int connectionType_code);

}

*Status change delegate

sender - Sending service

Status - Current status

publicinterface iStatusChange_delegate{

    void onConnectionManager_StatusChange(ConnectionManager sender, int ConnectionManagerStatus_code);

}

*Session open with success Delegate interface

ConnectionManager - ConnectionManager that opened session

publicinterface iSessionOpenSuccess_delegate{

   void onConnectionManager_SessionOpenSuccess(ConnectionManager sender); 

}

*Session open failure Delegate interface

ConnectionManager - ConnectionManager that tried opening session

Reason - Reason

publicinterface iSessionOpenFailure_delegate{

   void onConnectionManager_SessionOpenFailure(ConnectionManager sender,int SessionFailReason_code);  

}

*Session open callback Delegate interface

ConnectionManager - Callback to use with ConnetionManager.Open function to be execute on operation completion

Success - Outcome of session opening process

Reason - If Success false , gives reason for that outcome

publicinterface iSessionOpenCallback_delegate{

       void onConnectionManager_SessionOpenCallback(ConnectionManager sender,boolean success,int SessionFailReason_code);

}

*Session drop callback Delegate interface

ConnectionManager - Connection manager witch session droped

publicinterface iSessionDrop_delegate{

       void onConnectionManager_SessionDrop(ConnectionManager sender);

}

*Delegate interface for function to trigger when connection to peer (if connect) is successfully created

Peer - Connected peer

TransactionUID - Unique identifier on connect operation

publicinterface iPeerConnect_delegate{

       void onConnectionManager_PeerConnect(Peer peer, Guid TransactionUID);

}

*Delegate interface for function to trigger when connection to peer (if accept) is successfully created

Peer - Accepted peer

TransactionUID - Unique identifier on connect operation

    publicinterface iPeerAccept_delegate{

       void onConnectionManager_PeerAccept(Peer peer, Guid TransactionUID);

    }

  

   

*Delegate interface to function to be called on results arival after peer query request

FoundPeers - List of peers returned by query

Count - Number of peers returned by query

Completed - If completed then true, not that sometimes result will contain peers but this value will be false. This can happen for example if 10000000 peers are found by serch criteria , and operation timeout expires before all results are collected

QueryTransactionID - Transaction ID of query request

Page - Page returned if pagination exists

PageLimit - Number of peers per page if pagination exists

TotalPeers - Total number of peers matched by query

    publicinterface iPeerQueryCompleted_delegate{

       void onConnectionManager_PeerQueryCompleted(Peer[] peers,int count,boolean Completed, Guid QueryTransactionID,int Page,int PageLimit,int TotalPeers);

    }

  

   

*Delegate interface to function to be called to be called when information about peer state change arrives 

Peer - Peer that changed state

State - Value of the peer's state field

publicinterface iPeerStateChanged_delegate{

       void onConnectionManager_PeerStateChanged(Peer peer,int State);

}

*Delegate interface to pass as argument to ConnectionManager.Connect to be executed on connection creation

Peer - Peer object (remote peer) if success = true , otherwise null

PeerCheckpointUID - Unique identifier of remote peer's checkpoint

PeerUID - Unique identifier of remote peer

TransactionUID - Unique operation of connect operation

Success - Outcome of connect operation

FailReason - If success = false , then reason for failing

publicinterface iPeerConnectCallback_delegate{

       void onConnectionManager_PeerConnectCallback(Peer peer, Guid PeerCheckpointUID,Guid PeerUID, Guid TransactionUID, boolean Success,int PeerConnectFailureReason_code);

}

*Delegate interface to function to be called when instant message is Received

FromPeer - UID (Unique identifier) of remote peer that has sent instant message

FromPeerCheckpoint - UID (Unique identifier) of remote peer' checkpoint server that has sent instant message

FromPeer - Unique identifier of remote peer

FromPeerCheckpoint - Unique identifier of remote peer's checkpoint

MessageUID - Unique identifier of message

MessageType - Integer value that can be used to distinguish instant messages by several different types if needed in application

Data - Received bytes 

publicinterface iReceiveInstantMessage_delegate{

       void onConnectionManager_ReceiveInstantMessage(Guid FromPeer,Guid FromPeerCheckpoint,Guid MessageUID,int MessageType,byte[] Data);       

}

*Delegate interface for function to be called when connection attempt fails

PeerCheckpointUID - Checkpoint UID of peer to whom connection creation was attempted

PeerUID - UID of peer to whom connection creation was attempted

TransactionUID - Transaction UID

Reason - Reason of failure

publicinterface iPeerConnectFailure_delegate{

       void onConnectionManager_PeerConnectFailure(Guid PeerCheckpointUID,Guid PeerUID,Guid TransactionUID,int PeerConnectFailureReason_code);

}

*Delegate interface for function to be called when connection acceptation attempt fails

PeerCheckpointUID - Checkpoint UID of peer from whom connection creation was attempted

PeerUID - UID of peer from whom connection creation was attempted

TransactionUID - Transaction UID

Reason - Reason of failure

publicinterface iPeerAcceptFailure_delegate{

       void onConnectionManager_PeerAcceptFailure(Guid PeerCheckpointUID,Guid PeerUID,Guid TransactionUID,int PeerConnectFailureReason_code);

}

*Delegate interface for function to be called when instant message is sent to checkpoint server

MessageGUID - UID of instant message

success - true if sending was successful, otherwise false

publicinterface iSendInstantMessageComplete_delegate{

       void onConnectionManager_SendInstantMessageComplete(Guid MessageGUID,boolean success);

}

*Delegate interface for function to be called when state track action (register/un-register tracking) fails

TransactionUID - UID of transaction

publicinterface iStateTrackActionFailed_delegate{

       void onConnectionManager_StateTrackActionFailed(Guid TransactionUID);

}

*Delegate interface for function to be called when ConnectionManager dispatcher network interface changes

ConnectionManager - ConnectionManager that raised event

IP4Address - New address dispatcher is bound to

publicinterface NetworkInterfaceChange_delegate{

       void onConnectionManager_NetworkInterfaceChange(ConnectionManager sender, InetSocketAddress newAddress);

}