source: trunk/CrypPlugins/PeerToPeerBase/PeerToPeerBase.cs @ 1144

Last change on this file since 1144 was 1144, checked in by arnold, 12 years ago

P2PManager/P2PJobAdmin: Finetuning of leaving and re-joining the network.
Miscellaneous null-Checks implemented, so some p2p-sided errors where catched.

File size: 29.4 KB
Line 
1/* Copyright 2009 Team CrypTool (Christian Arnold), Uni Duisburg-Essen
2
3   Licensed under the Apache License, Version 2.0 (the "License");
4   you may not use this file except in compliance with the License.
5   You may obtain a copy of the License at
6
7       http://www.apache.org/licenses/LICENSE-2.0
8
9   Unless required by applicable law or agreed to in writing, software
10   distributed under the License is distributed on an "AS IS" BASIS,
11   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   See the License for the specific language governing permissions and
13   limitations under the License.
14*/
15
16using System;
17using System.Collections.Generic;
18using System.Linq;
19using System.Text;
20using PeersAtPlay.P2PStorage.DHT;
21using PeersAtPlay.P2PStorage.FullMeshDHT;
22using PeersAtPlay.P2POverlay.Bootstrapper;
23using PeersAtPlay.P2POverlay;
24using PeersAtPlay.P2POverlay.Bootstrapper.LocalMachineBootstrapper;
25using PeersAtPlay.P2POverlay.FullMeshOverlay;
26using PeersAtPlay.P2PLink;
27using PeersAtPlay.P2POverlay.Bootstrapper.IrcBootstrapper;
28using System.Threading;
29using Cryptool.PluginBase.Control;
30using System.ComponentModel;
31using PeersAtPlay;
32using PeersAtPlay.Util.Logging;
33using Gears4Net;
34
35/* - Synchronous functions successfully tested (store, retrieve)
36 * - The DHT has an integrated versioning system. When a peer wants
37 *   to store data in an entry, which already holds data, the version
38 *   number will be compared with the peers' version number. If the
39 *   peer hasn't read/write the entry the last time, the storing instruction
40 *   will be rejected. You must first read the actual data and than you can
41 *   store your data in this entry...
42 *
43 * INFO:
44 * - Have considered the DHT-own versioning system in the SynchStore method.
45 *   If this versioning system will be abolished, the SynchStore method must
46 *   be change!
47 * - Everything switched to SnalNG, SimpleSnal isn't used anymore, because
48 *   certification stuff runs now
49 *
50 * TODO:
51 * - Delete UseNatTraversal-Flag and insert CertificateCheck and CertificateSetup
52 * - Testing asynchronous methods incl. EventHandlers
53 */
54namespace Cryptool.Plugins.PeerToPeer
55{
56    /* Advantages of this wrapper class:
57     * - The PeerAtPlay-Libraries are only referenced in this project
58     *   --> so they're easy to update
59     * - PeerAtPlay only works with asynchronous methods, so this class
60     *   "synchronizes" this methods.
61     * - The PeerToPeer-Layers are unimportant for CT2-Developers, so this
62     *   issue is obfuscated by this wrapper class
63     */
64    /// <summary>
65    /// Wrapper class to integrate peer@play environment into CrypTool.
66    /// This class synchronizes asynchronous methods for easier usage in CT2. For future
67    /// </summary>
68    public class P2PBase
69    {
70        #region Delegates and Events for asynchronous p2p functions
71
72        public delegate void SystemJoined();
73        public event SystemJoined OnSystemJoined;
74
75        public delegate void SystemLeft();
76        public event SystemLeft OnSystemLeft;
77
78        public delegate void P2PMessageReceived(PeerId sourceAddr, byte[] data);
79        public event P2PMessageReceived OnP2PMessageReceived;
80
81        /// <summary>
82        /// returns true if key-value-pair is successfully stored in the DHT
83        /// </summary>
84        /// <param name="result"></param>
85        public delegate void DHTStoreCompleted(bool result);
86        public event DHTStoreCompleted OnDhtStore_Completed;
87
88        public delegate void DHTLoadCompleted(byte[] loadedData);
89        public event DHTLoadCompleted OnDhtLoad_Completed;
90
91        /// <summary>
92        /// returns true if key was found and removed successfully from the DHT
93        /// </summary>
94        /// <param name="result"></param>
95        public delegate void DHTRemoveCompleted(bool result);
96        public event DHTRemoveCompleted OnDhtRemove_Completed;
97
98        #endregion
99
100        #region Variables
101
102        private bool allowLoggingToMonitor;
103        /// <summary>
104        /// If true, all kinds of actions will be logged in the PeersAtPlay LogMonitor.
105        /// </summary>
106        public bool AllowLoggingToMonitor
107        {
108            get { return this.allowLoggingToMonitor; }
109            set { this.allowLoggingToMonitor = value; }
110        }
111
112        private const bool ALLOW_LOGGING_TO_MONITOR = true;
113
114        private bool started = false;
115        /// <summary>
116        /// True if system was successfully joined, false if system is COMPLETELY left
117        /// </summary>
118        public bool Started
119        {
120            get { return this.started; }
121            private set { this.started = value; } 
122        }
123
124        private IDHT dht;
125        private IP2PLinkManager linkmanager;
126        private IBootstrapper bootstrapper;
127        private P2POverlay overlay;
128        private AutoResetEvent systemJoined;
129        private AutoResetEvent systemLeft;
130
131        /// <summary>
132        /// Dictionary for synchronizing asynchronous DHT retrieves.
133        /// Cryptool doesn't offers an asynchronous environment, so this workaround is necessary
134        /// </summary>
135        private Dictionary<Guid, ResponseWait> waitDict;
136
137        #endregion
138
139        public P2PBase()
140        {
141            this.waitDict = new Dictionary<Guid, ResponseWait>();
142            this.systemJoined = new AutoResetEvent(false);
143            this.systemLeft = new AutoResetEvent(false);
144        }
145
146        #region Basic P2P Methods (Init, Start, Stop) - synch and asynch
147
148        /// <summary>
149        /// Initializing is the first step to build a new or access an existing p2p network
150        /// </summary>
151        /// <param name="sUserName">Choose an individual name for the user</param>
152        /// <param name="sWorldName">fundamental: two peers are only in the SAME
153        /// P2P system, when they initialized the SAME WORLD!</param>
154        /// <param name="bolUseNatTraversal">When you want to use NAT-Traversal #
155        /// (tunneling the P2P connection through NATs and Firewalls), you have to
156        /// set this flag to true</param>
157        /// <param name="linkManagerType"></param>
158        /// <param name="bsType"></param>
159        /// <param name="overlayType"></param>
160        /// <param name="dhtType"></param>
161        public void Initialize(string sUserName, string sWorldName, P2PLinkManagerType linkManagerType, P2PBootstrapperType bsType, P2POverlayType overlayType, P2PDHTType dhtType)
162        {
163            #region Setting LinkManager, Bootstrapper, Overlay and DHT to the specified types
164            switch (linkManagerType)
165            {
166                case P2PLinkManagerType.Snal:
167                    LogToMonitor("Init LinkMgr: Using NAT Traversal stuff");
168                    // NAT-Traversal stuff needs a different Snal-Version
169                    this.linkmanager = new PeersAtPlay.P2PLink.SnalNG.Snal(new STAScheduler("crypt"));
170
171                    ((PeersAtPlay.P2PLink.SnalNG.Snal)this.linkmanager).Settings.ConnectInternal = true;
172                    ((PeersAtPlay.P2PLink.SnalNG.Snal)this.linkmanager).Settings.LocalReceivingPort = 0;
173                    ((PeersAtPlay.P2PLink.SnalNG.Snal)this.linkmanager).Settings.UseLocalAddressDetection = false;
174                    break;
175                default:
176                    throw (new NotImplementedException());
177            }
178            switch (bsType)
179            {
180                case P2PBootstrapperType.LocalMachineBootstrapper:
181                    //LocalMachineBootstrapper = only local connection (runs only on one machine)
182                    this.bootstrapper = new LocalMachineBootstrapper();
183                    break;
184                case P2PBootstrapperType.IrcBootstrapper:
185                    // setup nat traversal stuff
186                    LogToMonitor("Init Bootstrapper: Using NAT Traversal stuff");
187                    PeersAtPlay.P2POverlay.Bootstrapper.IrcBootstrapper.Settings.DelaySymmetricResponse = true;
188                    PeersAtPlay.P2POverlay.Bootstrapper.IrcBootstrapper.Settings.IncludeSymmetricInResponse = false;
189                    PeersAtPlay.P2POverlay.Bootstrapper.IrcBootstrapper.Settings.SymmetricResponseDelay = 6000;
190
191                    this.bootstrapper = new IrcBootstrapper();
192                    break;
193                default:
194                    throw (new NotImplementedException());
195            }
196            switch (overlayType)
197            {
198                case P2POverlayType.FullMeshOverlay:
199                    // changing overlay example: this.overlay = new ChordOverlay();
200                    this.overlay = new FullMeshOverlay();
201                    break;
202                default:
203                    throw (new NotImplementedException());
204            }
205            switch (dhtType)
206            {
207                case P2PDHTType.FullMeshDHT:
208                    this.dht = new FullMeshDHT();
209                    break;
210                default:
211                    throw (new NotImplementedException());
212            }
213            #endregion
214
215            this.overlay.MessageReceived += new EventHandler<OverlayMessageEventArgs>(overlay_MessageReceived);
216            this.dht.SystemJoined += new EventHandler(OnDHT_SystemJoined);
217            this.dht.SystemLeft += new EventHandler(OnDHT_SystemLeft);
218
219            this.dht.Initialize(sUserName, "", sWorldName, this.overlay, this.bootstrapper, this.linkmanager, null);
220        }
221
222        /// <summary>
223        /// Starts the P2P System. When the given P2P world doesn't exist yet,
224        /// inclusive creating the and bootstrapping to the P2P network.
225        /// In either case joining the P2P world.
226        /// This synchronized method returns true not before the peer has
227        /// successfully joined the network (this may take one or two minutes).
228        /// </summary>
229        /// <returns>True, if the peer has completely joined the p2p network</returns>
230        public bool SynchStart()
231        {
232            //Start != system joined
233            //Only starts the system asynchronous, the possible callback is useless,
234            //because it's invoked before the peer completly joined the P2P system
235            this.dht.BeginStart(null);
236            //Wait for event SystemJoined. When it's invoked, the peer completly joined the P2P system
237            this.systemJoined.WaitOne();
238            return true;
239        }
240
241        /// <summary>
242        /// Disjoins the peer from the system. The P2P system survive while one peer is still in the network.
243        /// </summary>
244        /// <returns>True, if the peer has completely disjoined the p2p network</returns>
245        public bool SynchStop()
246        {
247            if (this.dht != null)
248            {
249                this.dht.BeginStop(null);
250                //don't stop anything else, because BOOM
251
252                //wait till systemLeft Event is invoked
253                this.systemLeft.WaitOne();
254            }
255            return true;
256        }
257
258        /// <summary>
259        /// Asynchronously starting the peer. When the given P2P world doesn't
260        /// exist yet, inclusive creating the and bootstrapping to the P2P network.
261        /// In either case joining the P2P world. To ensure that peer has successfully
262        /// joined the p2p world, catch the event OnSystemJoined.
263        /// </summary>
264        public void AsynchStart()
265        {
266            // no callback usefull, because starting and joining isn't the same
267            // everything else is done by the EventHandler OnDHT_SystemJoined
268            this.dht.BeginStart(null);
269        }
270
271        /// <summary>
272        /// Asynchronously disjoining the actual peer of the p2p system. To ensure
273        /// disjoining, catch the event OnDHT_SystemLeft.
274        /// </summary>
275        public void AsynchStop()
276        {
277            if (this.dht != null)
278            {
279                // no callback usefull.
280                // Everything else is done by the EventHandler OnDHT_SystemLeft
281                this.dht.BeginStop(null);
282            }
283        }
284
285        #endregion
286
287        /// <summary>
288        /// Get PeerName of the actual peer
289        /// </summary>
290        /// <param name="sPeerName">out: additional peer information UserName on LinkManager</param>
291        /// <returns>PeerID as a String</returns>
292        public PeerId GetPeerID(out string sPeerName)
293        {
294            sPeerName = this.linkmanager.UserName;
295            return new PeerId(this.overlay.LocalAddress);
296        }
297
298        /// <summary>
299        /// Construct PeerId object for a specific byte[] id
300        /// </summary>
301        /// <param name="byteId">overlay address as byte array</param>
302        /// <returns>corresponding PeerId for given byte[] id</returns>
303        public PeerId GetPeerID(byte[] byteId)
304        {
305            LogToMonitor("GetPeerID: Converting byte[] to PeerId-Object");
306            return new PeerId(this.overlay.GetAddress(byteId));
307        }
308
309        // overlay.LocalAddress = Overlay-Peer-Address/Names
310        public void SendToPeer(byte[] data, byte[] destinationPeer)
311        {
312            // get stack size of the pap use-data and add own use data (for optimizing Stack size)
313            int realStackSize = this.overlay.GetHeaderSize() + data.Length;
314            ByteStack stackData = new ByteStack(realStackSize);
315
316            stackData.Push(data);
317
318            OverlayAddress destinationAddr = this.overlay.GetAddress(destinationPeer);
319            OverlayMessage overlayMsg = new OverlayMessage(MessageReceiverType.P2PBase,
320                this.overlay.LocalAddress, destinationAddr, stackData);
321            this.overlay.Send(overlayMsg);
322        }
323
324        private void overlay_MessageReceived(object sender, OverlayMessageEventArgs e)
325        {
326            if (OnP2PMessageReceived != null)
327            {
328                PeerId pid = new PeerId(e.Message.Source);
329                /* You have to fire this event asynchronous, because the main
330                 * thread will be stopped in this wrapper class for synchronizing
331                 * the asynchronous stuff (AutoResetEvent) --> so this could run
332                 * into a deadlock, when you fire this event synchronous (normal Invoke)
333                 * ATTENTION: This could change the invocation order!!! In my case
334                              no problem, but maybe in future cases... */
335                OnP2PMessageReceived.BeginInvoke(pid, e.Message.Data.PopBytes(e.Message.Data.CurrentStackSize),null,null);
336                //OnP2PMessageReceived(pid, e.Message.Data.PopUTF8String());
337            }
338        }
339
340        #region Event Handling (System Joined, Left and Message Received)
341
342        private void OnDHT_SystemJoined(object sender, EventArgs e)
343        {
344            if (OnSystemJoined != null)
345                OnSystemJoined();
346            this.systemJoined.Set();
347            Started = true;
348        }
349
350        private void OnDHT_SystemLeft(object sender, EventArgs e)
351        {
352            if (OnSystemLeft != null)
353                OnSystemLeft();
354            // as an experiment
355            this.dht = null;
356            this.systemLeft.Set();
357            Started = false;
358
359            LogToMonitor("OnDHT_SystemLeft has nulled the dht and setted the systemLeft Waithandle");
360        }
361
362        #endregion
363
364        /* Attention: The asynchronous methods are not tested at the moment */
365        #region Asynchronous Methods incl. Callbacks
366
367        /// <summary>
368        /// Asynchronously retrieving a key from the DHT. To get value, catch
369        /// event OnDhtLoad_Completed.
370        /// </summary>
371        /// <param name="sKey">Existing key in DHT</param>
372        public void AsynchRetrieve(string sKey)
373        {
374            Guid g = this.dht.Retrieve(OnAsynchRetrieve_Completed, sKey);
375        }
376        private void OnAsynchRetrieve_Completed(RetrieveResult rr)
377        {
378            if (OnDhtLoad_Completed != null)
379            {
380                OnDhtLoad_Completed(rr.Data);
381            }
382        }
383
384        /// <summary>
385        /// Asynchronously storing a Key-Value-Pair in the DHT. To ensure that
386        /// storing is completed, catch event OnDhtStore_Completed.
387        /// </summary>
388        /// <param name="sKey"></param>
389        /// <param name="sValue"></param>
390        public void AsynchStore(string sKey, string sValue)
391        {
392            //this.dht.Store(OnAsynchStore_Completed, sKey, UTF8Encoding.UTF8.GetBytes(sValue), IGNORE_DHT_VERSIONING_SYSTEM);
393            this.dht.Store(OnAsynchStore_Completed, sKey, UTF8Encoding.UTF8.GetBytes(sValue));
394        }
395
396        private void OnAsynchStore_Completed(StoreResult sr)
397        {
398            if (OnDhtStore_Completed != null)
399            {
400                if (sr.Status == OperationStatus.Success)
401                    OnDhtStore_Completed(true);
402                else
403                    OnDhtStore_Completed(false);
404            }
405               
406        }
407
408        /// <summary>
409        /// Asynchronously removing an existing key out of the DHT. To ensure
410        /// that removing is completed, catch event OnDhtRemove_Completed.
411        /// </summary>
412        /// <param name="sKey"></param>
413        public void AsynchRemove(string sKey)
414        {
415            //this.dht.Remove(OnAsynchRemove_Completed, sKey, IGNORE_DHT_VERSIONING_SYSTEM);
416            this.dht.Remove(OnAsynchRemove_Completed, sKey);
417        }
418        private void OnAsynchRemove_Completed(RemoveResult rr)
419        {
420            if (OnDhtRemove_Completed != null)
421            {
422                if(rr.Status == OperationStatus.Success)
423                    OnDhtRemove_Completed(true);
424                else
425                    OnDhtRemove_Completed(false);
426            }
427        }
428
429        #endregion
430
431        #region Synchronous Methods incl. Callbacks
432
433        #region SynchStore incl.Callback and SecondTrialCallback
434
435        /// <summary>
436        /// Stores a value in the DHT at the given key
437        /// </summary>
438        /// <param name="sKey">Key of DHT Entry</param>
439        /// <param name="byteData">Value of DHT Entry</param>
440        /// <returns>True, when storing is completed!</returns>
441        public bool SynchStore(string sKey, byte[] byteData)
442        {
443            LogToMonitor("Begin: SynchStore. Key: " + sKey + ", Data: " + Encoding.UTF8.GetString(byteData));
444            AutoResetEvent are = new AutoResetEvent(false);
445            // this method returns always a GUID to distinguish between asynchronous actions
446            Guid g = this.dht.Store(OnSynchStoreCompleted, sKey, byteData);
447
448            ResponseWait rw = new ResponseWait() { WaitHandle = are, key=sKey , value = byteData };
449
450            waitDict.Add(g, rw);
451            //blocking till response
452            are.WaitOne();
453
454            LogToMonitor("End: SynchStore. Key: " + sKey + ". Success: " + rw.success.ToString());
455
456            return rw.success;
457        }
458
459        /// <summary>
460        /// Stores a value in the DHT at the given key
461        /// </summary>
462        /// <param name="sKey">Key of DHT Entry</param>
463        /// <param name="sValue">Value of DHT Entry</param>
464        /// <returns>True, when storing is completed!</returns>
465        public bool SynchStore(string sKey, string sData)
466        {
467            return SynchStore(sKey, UTF8Encoding.UTF8.GetBytes(sData));
468        }
469        /// <summary>
470        /// Callback for a the synchronized store method
471        /// </summary>
472        /// <param name="rr"></param>
473        private void OnSynchStoreCompleted(StoreResult sr)
474        {
475            ResponseWait rw;
476            if (this.waitDict.TryGetValue(sr.Guid, out rw))
477            {
478                // if Status == Error, than the version of the value is out of date.
479                // There is a versioning system in the DHT. So you must retrieve the
480                // key and than store the new value
481                if (sr.Status == OperationStatus.Failure)
482                {
483                    byte[] byteTemp = this.SynchRetrieve(rw.key);
484
485                    // Only try a second time. When it's still not possible, abort storing
486                    AutoResetEvent are = new AutoResetEvent(false);
487                    Guid g = this.dht.Store(OnSecondTrialStoring, rw.key, rw.value);
488                    ResponseWait rw2 = new ResponseWait() { WaitHandle = are, key = rw.key, value = rw.value };
489
490                    waitDict.Add(g, rw2);
491                    // blocking till response
492                    are.WaitOne();
493                    rw.success = rw2.success;
494                    rw.Message = rw2.Message;
495                }
496                else
497                {
498                    rw.Message = UTF8Encoding.UTF8.GetBytes(sr.Status.ToString());
499                    if (sr.Status == OperationStatus.KeyNotFound)
500                        rw.success = false;
501                    else
502                        rw.success = true;
503                }
504            }
505            //unblock WaitHandle in the synchronous method
506            rw.WaitHandle.Set();
507            // don't know if this accelerates the system...
508            this.waitDict.Remove(sr.Guid);
509        }
510
511        private void OnSecondTrialStoring(StoreResult sr)
512        {
513            ResponseWait rw;
514            if (this.waitDict.TryGetValue(sr.Guid, out rw))
515            {
516                if (sr.Status == OperationStatus.Failure)
517                {
518                    //Abort storing, because it's already the second trial
519                    rw.Message = UTF8Encoding.UTF8.GetBytes("Storing also not possible on second trial.");
520                    rw.success = false;
521                }
522                else
523                {
524                    //works the second trial, so it was the versioning system
525                    rw.success = true;
526                }
527            }
528            //unblock WaitHandle in the synchronous method
529            rw.WaitHandle.Set();
530            // don't know if this accelerates the system...
531            this.waitDict.Remove(sr.Guid);
532        }
533
534        #endregion
535
536        /// <summary>
537        /// Get the value of the given DHT Key or null, if it doesn't exist.
538        /// For synchronous environments use the Synch* methods.
539        /// </summary>
540        /// <param name="sKey">Key of DHT Entry</param>
541        /// <returns>Value of DHT Entry</returns>
542        public byte[] SynchRetrieve(string sKey)
543        {
544            LogToMonitor("ThreadId (P2PBase SynchRetrieve): " + Thread.CurrentThread.ManagedThreadId.ToString());
545
546            AutoResetEvent are = new AutoResetEvent(false);
547            // this method returns always a GUID to distinguish between asynchronous actions
548
549            LogToMonitor("Begin: SynchRetrieve. Key: " + sKey);
550             
551            Guid g = this.dht.Retrieve(OnSynchRetrieveCompleted, sKey);
552           
553            ResponseWait rw = new ResponseWait() {WaitHandle = are };
554           
555            waitDict.Add(g,rw  );
556            // blocking till response
557            are.WaitOne();
558
559            LogToMonitor("End: SynchRetrieve. Key: " + sKey + ". Success: " + rw.success.ToString());
560
561            //Rückgabe der Daten
562            return rw.Message;
563        }
564
565        /// <summary>
566        /// Callback for a the synchronized retrieval method
567        /// </summary>
568        /// <param name="rr"></param>
569        private void OnSynchRetrieveCompleted(RetrieveResult rr)
570        {
571            LogToMonitor(rr.Guid.ToString());
572           
573            ResponseWait rw;
574
575            LogToMonitor("ThreadId (P2PBase retrieve callback): " + Thread.CurrentThread.ManagedThreadId.ToString());
576
577            if (this.waitDict.TryGetValue(rr.Guid, out rw))
578            {
579                // successful as long as no error occured
580                rw.success = true;
581                if (rr.Status == OperationStatus.Failure)
582                {
583                    rw.Message = null;
584                    rw.success = false;
585                }
586                else if (rr.Status == OperationStatus.KeyNotFound)
587                    rw.Message = null;
588                else
589                    rw.Message = rr.Data;
590
591                //unblock WaitHandle in the synchronous method
592                rw.WaitHandle.Set();
593                // don't know if this accelerates the system...
594                this.waitDict.Remove(rr.Guid);
595            }
596        }
597        /// <summary>
598        /// Removes a key/value pair out of the DHT
599        /// </summary>
600        /// <param name="sKey">Key of the DHT Entry</param>
601        /// <returns>True, when removing is completed!</returns>
602        public bool SynchRemove(string sKey)
603        {
604            LogToMonitor("Begin SynchRemove. Key: " + sKey);
605
606            AutoResetEvent are = new AutoResetEvent(false);
607            // this method returns always a GUID to distinguish between asynchronous actions
608            Guid g = this.dht.Remove(OnSynchRemoveCompleted, sKey);
609            //Guid g = this.dht.Remove(OnSynchRemoveCompleted, sKey, IGNORE_DHT_VERSIONING_SYSTEM);
610
611            ResponseWait rw = new ResponseWait() { WaitHandle = are };
612
613            waitDict.Add(g, rw);
614            // blocking till response
615            are.WaitOne();
616
617            LogToMonitor("Ended SynchRemove. Key: " + sKey + ". Success: " + rw.success.ToString());
618
619            return rw.success;
620        }
621
622        /// <summary>
623        /// Callback for a the synchronized remove method
624        /// </summary>
625        /// <param name="rr"></param>
626        private void OnSynchRemoveCompleted(RemoveResult rr)
627        {
628            ResponseWait rw;
629            if (this.waitDict.TryGetValue(rr.Guid, out rw))
630            {
631                rw.Message = UTF8Encoding.UTF8.GetBytes(rr.Status.ToString());
632
633                if (rr.Status == OperationStatus.Failure || rr.Status == OperationStatus.KeyNotFound)
634                    rw.success = false;
635                else
636                    rw.success = true;
637
638                //unblock WaitHandle in the synchronous method
639                rw.WaitHandle.Set();
640                // don't know if this accelerates the system...
641                this.waitDict.Remove(rr.Guid);
642            }
643        }
644
645        #endregion
646
647        /// <summary>
648        /// To log the internal state in the Monitoring Software of P@play
649        /// </summary>
650        public void LogInternalState()
651        {
652            if (this.dht != null)
653            {
654                this.dht.LogInternalState();
655            }
656        }
657
658        public void LogToMonitor(string sTextToLog)
659        {
660            if(AllowLoggingToMonitor)
661                Log.Debug(sTextToLog);
662        }
663
664    }
665
666    public class PeerId
667    {
668        private readonly string stringId;
669        private readonly byte[] byteId;
670
671        private const uint OFFSET_BASIS = 2166136261;
672        private const uint PRIME = 16777619; // 2^24 + 2^8 + 0x93
673        private readonly int hashCode;
674
675        public PeerId(OverlayAddress oAddress)
676        {
677            if (oAddress != null)
678            {
679                this.stringId = oAddress.ToString();
680                this.byteId = oAddress.ToByteArray();
681
682                // FNV-1 hashing
683                uint fnvHash = OFFSET_BASIS;
684                foreach (byte b in byteId)
685                {
686                    fnvHash = (fnvHash * PRIME) ^ b;
687                }
688                hashCode = (int)fnvHash;
689            }
690        }
691
692        /// <summary>
693        /// Returns true when the byteId content is identical
694        /// </summary>
695        /// <param name="right"></param>
696        /// <returns></returns>
697        public override bool Equals(object right)
698        {
699            /* standard checks for reference types */
700
701            if (right == null)
702                return false;
703
704            if (object.ReferenceEquals(this, right))
705                return true;
706
707            if (this.GetType() != right.GetType())
708                return false;
709
710            // actual content comparison
711            return this == (PeerId)right;
712        }
713
714        public static bool operator ==(PeerId left, PeerId right)
715        {
716            // because it's possible that one parameter is null, catch this exception
717            /* Begin add - Christian Arnold, 2009.12.16, must cast the parameters, otherwise --> recursion */
718            if ((object)left == (object)right)
719                return true;
720
721            if ((object)left == null || (object)right == null)
722                return false;
723            /* End add */
724
725            if (left.hashCode != right.hashCode)
726                return false;
727
728            if (left.byteId.Length != right.byteId.Length)
729                return false;
730
731            for (int i = 0; i < left.byteId.Length; i++)
732            {
733                // uneven pattern content
734                if (left.byteId[i] != right.byteId[i])
735                    return false;
736            }
737
738            return true;
739        }
740
741        public static bool operator !=(PeerId left, PeerId right)
742        {
743            return !(left == right);
744        }
745
746        public override int GetHashCode()
747        {
748            return hashCode;
749        }
750
751        public override string ToString()
752        {
753            return this.stringId;
754        }
755
756        public byte[] ToByteArray()
757        {
758            return this.byteId;
759        }
760    }
761}
Note: See TracBrowser for help on using the repository browser.