source: trunk/CrypPlugins/PeerToPeerBaseProxy/P2PPeer.cs @ 1611

Last change on this file since 1611 was 1611, checked in by Paul Lelgemann, 12 years ago

o CrypWin: P2P world button always visible
o Changed behaviour of command line option "-p2p" and "-peer2peer": triggers autoconnect to peer-to-peer network during startup of CrypTool
o P2PEditor: "Start" button disabled and moved to settings pane for consistency
o CrypP2P: local workspace path is set to "%TEMP%/CrypTool", if not specified before
+ PeerToPeerProxy has a new setting to enable autoconnecting the peer-to-peer network, if it is not connected during workspace start

File size: 18.3 KB
Line 
1/*
2   Copyright 2010 Paul Lelgemann, University of Duisburg-Essen
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17using System;
18using System.ComponentModel;
19using System.Text;
20using System.Threading;
21using System.Windows.Controls;
22using Cryptool.P2P;
23using Cryptool.P2P.Internal;
24using Cryptool.P2P.Worker;
25using Cryptool.PluginBase;
26using Cryptool.PluginBase.Control;
27using Cryptool.PluginBase.IO;
28using Cryptool.PluginBase.Miscellaneous;
29using Cryptool.Plugins.PeerToPeer.Internal;
30
31namespace Cryptool.Plugins.PeerToPeerProxy
32{
33    [Author("Paul Lelgemann", "lelgemann@cryptool.org", "Uni Duisburg-Essen", "http://www.uni-due.de")]
34    [PluginInfo(false, "P2P_Peer_Proxy",
35        "Creates a new Peer. Uses the CrypTool2 built-in P2P network and can be used as a replacement for P2P_Peer.", ""
36        , "PeerToPeerBaseProxy/icons/peer_inactive.png", "PeerToPeerBaseProxy/icons/peer_connecting.png",
37        "PeerToPeerBaseProxy/icons/peer_online.png", "PeerToPeerBaseProxy/icons/peer_error.png")]
38    public class P2PPeer : IIOMisc
39    {
40        #region IIOMisc Members
41
42        public event GuiLogNotificationEventHandler OnGuiLogNotificationOccured;
43
44        public event PluginProgressChangedEventHandler OnPluginProgressChanged;
45
46        public event PropertyChangedEventHandler PropertyChanged;
47
48        #endregion
49
50        // to forward event from overlay/dht MessageReceived-Event from P2PBase
51        public event P2PBase.P2PMessageReceived OnPeerMessageReceived;
52
53        public void OnPropertyChanged(string name)
54        {
55            EventsHelper.PropertyChanged(PropertyChanged, this, new PropertyChangedEventArgs(name));
56        }
57
58        private void GuiLogMessage(string p, NotificationLevel notificationLevel)
59        {
60            // for evaluation issues only
61            EventsHelper.GuiLogMessage(OnGuiLogNotificationOccured, this,
62                                       new GuiLogEventArgs(p + "(" + DebugToFile.GetTimeStamp() + ")", this,
63                                                           notificationLevel));
64        }
65
66        #region In and Output
67
68        [PropertyInfo(Direction.ControlSlave, "Master Peer", "One peer to rule them all", "", true, false,
69            DisplayLevel.Beginner, QuickWatchFormat.Text, null)]
70        public IP2PControl P2PControlSlave
71        {
72            get { return _p2PSlave ?? (_p2PSlave = new P2PPeerMaster(this)); }
73        }
74
75        #endregion
76
77        #region Start and Stop Peer
78
79        /// <summary>
80        /// Status flag for starting and stopping peer only once.
81        /// </summary>
82        public bool PeerStarted()
83        {
84            return P2PManager.Instance.IsP2PConnected() && !P2PManager.Instance.IsP2PConnecting;
85        }
86
87        public void StartPeer()
88        {
89            _settings.PeerStatusChanged(P2PPeerSettings.PeerStatus.Connecting);
90            P2PManager.Instance.P2PBase.OnP2PMessageReceived += P2PBaseOnP2PMessageReceived;
91
92            if (P2PManager.Instance.IsP2PConnected())
93            {
94                GuiLogMessage("P2P connected.", NotificationLevel.Info);
95                _settings.PeerStatusChanged(P2PPeerSettings.PeerStatus.Online);
96            }
97            else if (!P2PManager.Instance.IsP2PConnected() && P2PManager.Instance.IsP2PConnecting)
98            {
99                HandleAlreadyConnecting();
100            }
101            else
102            {
103                if (_settings.Autoconnect)
104                {
105                    HandleAutoconnect();
106                }
107                else
108                {
109                    GuiLogMessage("P2P network offline. Please check the autoconnect checkbox of the proxy plugin or connect manually first.",
110                                      NotificationLevel.Error);
111                    _settings.PeerStatusChanged(P2PPeerSettings.PeerStatus.Error);
112                    throw new ApplicationException("Workaround for wrong error handling... Workspace should be stopped now.");
113                }
114            }
115        }
116
117        private void HandleAlreadyConnecting()
118        {
119            P2PManager.OnP2PConnectionStateChangeOccurred += HandleConnectionStateChange;
120            _connectResetEvent = new AutoResetEvent(false);
121            _connectResetEvent.WaitOne();
122
123            if (P2PManager.Instance.IsP2PConnected())
124            {
125                GuiLogMessage("P2P connected.", NotificationLevel.Info);
126                _settings.PeerStatusChanged(P2PPeerSettings.PeerStatus.Online);
127            }
128            else
129            {
130                GuiLogMessage("P2P network could not be connected.",
131                              NotificationLevel.Error);
132                _settings.PeerStatusChanged(P2PPeerSettings.PeerStatus.Error);
133                throw new ApplicationException("Workaround for wrong error handling... Workspace should be stopped now.");
134            }
135        }
136
137        private void HandleAutoconnect()
138        {
139            P2PManager.OnP2PConnectionStateChangeOccurred += HandleConnectionStateChange;
140            _connectResetEvent = new AutoResetEvent(false);
141
142            new ConnectionWorker(P2PManager.Instance.P2PBase).Start();
143
144            _connectResetEvent.WaitOne();
145
146            if (P2PManager.Instance.IsP2PConnected())
147            {
148                GuiLogMessage("P2P network was connected due to plugin setting.",
149                              NotificationLevel.Info);
150                _settings.PeerStatusChanged(P2PPeerSettings.PeerStatus.Online);
151            }
152            else
153            {
154                GuiLogMessage("P2P network could not be connected.",
155                              NotificationLevel.Error);
156                _settings.PeerStatusChanged(P2PPeerSettings.PeerStatus.Error);
157                throw new ApplicationException("Workaround for wrong error handling... Workspace should be stopped now.");
158            }
159        }
160
161        void HandleConnectionStateChange(object sender, bool newState)
162        {
163            _connectResetEvent.Set();
164        }
165
166        public void StopPeer()
167        {
168            P2PManager.Instance.P2PBase.OnP2PMessageReceived -= P2PBaseOnP2PMessageReceived;
169            GuiLogMessage("Removed event registration, but peer cannot be stopped, it is running in CrypTool!", NotificationLevel.Info);
170        }
171
172        #endregion Start and Stop Peer
173
174        #region Variables
175
176        private IP2PControl _p2PSlave;
177        private P2PPeerSettings _settings;
178        private AutoResetEvent _connectResetEvent;
179
180        #endregion
181
182        #region Standard functionality
183
184        public P2PPeer()
185        {
186            _settings = new P2PPeerSettings();
187            _settings.OnPluginStatusChanged += SettingsOnPluginStatusChanged;
188        }
189
190        public event StatusChangedEventHandler OnPluginStatusChanged;
191
192        public ISettings Settings
193        {
194            set { _settings = (P2PPeerSettings) value; }
195            get { return _settings; }
196        }
197
198        public UserControl Presentation
199        {
200            get { return null; }
201        }
202
203        public UserControl QuickWatchPresentation
204        {
205            get { return null; }
206        }
207
208        public void PreExecution()
209        {
210            StartPeer();
211        }
212
213        public void Execute()
214        {
215        }
216
217        public void PostExecution()
218        {
219        }
220
221        public void Pause()
222        {
223        }
224
225        public void Stop()
226        {
227        }
228
229        public void Initialize()
230        {
231        }
232
233        public void Dispose()
234        {
235            StopPeer();
236        }
237
238        private void SettingsOnPluginStatusChanged(IPlugin sender, StatusEventArgs args)
239        {
240            if (OnPluginStatusChanged != null)
241                OnPluginStatusChanged(this, args);
242        }
243
244        // to forward event from overlay/dht MessageReceived-Event from P2PBase
245        private void P2PBaseOnP2PMessageReceived(PeerId sourceAddr, byte[] data)
246        {
247            if (OnPeerMessageReceived != null)
248                OnPeerMessageReceived(sourceAddr, data);
249        }
250
251        private static void SettingsTaskPaneAttributeChanged(ISettings settings, TaskPaneAttributeChangedEventArgs args)
252        {
253        }
254
255        #endregion
256    }
257
258
259    public class P2PPeerMaster : IP2PControl
260    {
261        private readonly Encoding _enc = Encoding.UTF8;
262        private readonly P2PPeer _p2PPeer;
263        private readonly AutoResetEvent _systemJoined;
264        private PeerId _peerId;
265        private string _sPeerName;
266        // used for every encoding stuff
267
268        public P2PPeerMaster(P2PPeer p2PPeer)
269        {
270            _p2PPeer = p2PPeer;
271            _systemJoined = new AutoResetEvent(false);
272
273            P2PManager.Instance.P2PBase.OnSystemJoined += P2PBaseOnSystemJoined;
274            P2PManager.Instance.OnPeerMessageReceived += P2PPeerOnPeerMessageReceived;
275            OnStatusChanged += P2PPeerMaster_OnStatusChanged;
276        }
277
278        #region Events and Event-Handling
279
280        // to forward event from overlay MessageReceived-Event from P2PBase
281        // analyzes the type of message and throws depend upon this anaysis an event
282        public event P2PPayloadMessageReceived OnPayloadMessageReceived;
283        public event P2PSystemMessageReceived OnSystemMessageReceived;
284
285        public event IControlStatusChangedEventHandler OnStatusChanged;
286
287        private void P2PBaseOnSystemJoined()
288        {
289            _systemJoined.Set();
290        }
291
292        private void P2PPeerOnPeerMessageReceived(PeerId sourceAddr, byte[] data)
293        {
294            switch (GetMessageType(data[0])) //analyses the first byte of data (index, which represents the MessageType)
295            {
296                case P2PMessageIndex.PubSub:
297                    if (data.Length == 2)
298                    {
299                        if (OnSystemMessageReceived != null)
300                            OnSystemMessageReceived(sourceAddr, GetPubSubType(data[1]));
301                    }
302                    else
303                    {
304                        throw (new Exception("Data seems to be from type 'PubSub', but is to long for it... Data: '" +
305                                             _enc.GetString(data) + "'"));
306                    }
307                    break;
308                case P2PMessageIndex.Payload:
309                    if (OnPayloadMessageReceived != null)
310                        OnPayloadMessageReceived(sourceAddr, GetMessagePayload(data));
311                    break;
312                default:
313                    // not implemented. System ignores these messages completely at present
314                    break;
315            }
316        }
317
318        private void P2PPeerMaster_OnStatusChanged(IControl sender, bool readyForExecution)
319        {
320            if (OnStatusChanged != null)
321                OnStatusChanged(sender, readyForExecution);
322        }
323
324        #endregion
325
326        #region IP2PControl Members
327
328        public bool PeerStarted()
329        {
330            return _p2PPeer.PeerStarted();
331        }
332
333        public bool DHTstore(string sKey, byte[] byteValue)
334        {
335            return P2PManager.Instance.IsP2PConnected() && P2PManager.Store(sKey, byteValue);
336        }
337
338        public bool DHTstore(string sKey, string sValue)
339        {
340            return P2PManager.Instance.IsP2PConnected() && P2PManager.Store(sKey, sValue);
341        }
342
343        public byte[] DHTload(string sKey)
344        {
345            return P2PManager.Instance.IsP2PConnected() ? P2PManager.Retrieve(sKey) : null;
346        }
347
348        public bool DHTremove(string sKey)
349        {
350            return P2PManager.Instance.IsP2PConnected() && P2PManager.Remove(sKey);
351        }
352
353        /// <summary>
354        /// This method only contacts the p2p system, if the peerID wasn't requested before
355        /// </summary>
356        /// <param name="sPeerName">returns the Peer Name</param>
357        /// <returns>returns the Peer ID</returns>
358        public PeerId GetPeerID(out string sPeerName)
359        {
360            if (SystemJoinedCompletely())
361            {
362                if (_peerId == null)
363                {
364                    _peerId = P2PManager.Instance.P2PBase.GetPeerId(out _sPeerName);
365                }
366                sPeerName = _sPeerName;
367                return _peerId;
368            }
369
370            sPeerName = _sPeerName;
371            return null;
372        }
373
374        public PeerId GetPeerID(byte[] byteId)
375        {
376            return P2PManager.Instance.P2PBase.GetPeerId(byteId);
377        }
378
379        // adds the P2PMessageIndex to the given byte-array
380        public void SendToPeer(byte[] data, PeerId destinationAddress)
381        {
382            byte[] newData = GenerateMessage(data, P2PMessageIndex.Payload);
383            SendReadilyMessage(newData, destinationAddress);
384        }
385
386        public void SendToPeer(string sData, PeerId destinationAddress)
387        {
388            byte[] data = GenerateMessage(sData, P2PMessageIndex.Payload);
389            SendReadilyMessage(data, destinationAddress);
390        }
391
392        public void SendToPeer(PubSubMessageType msgType, PeerId destinationAddress)
393        {
394            byte[] data = GenerateMessage(msgType);
395            SendReadilyMessage(data, destinationAddress);
396        }
397
398        #endregion
399
400        #region Communication protocol
401
402        /// <summary>
403        /// generates a ct2- and p2p-compatible and processable message
404        /// </summary>
405        /// <param name="payload">payload data in bytes</param>
406        /// <param name="msgIndex">type of message (system message, simple payload for a special use case, etc.)</param>
407        /// <returns>the message, which is processable by the ct2/p2p system</returns>
408        private static byte[] GenerateMessage(byte[] payload, P2PMessageIndex msgIndex)
409        {
410            // first byte is the index, if it is payload or Publish/Subscriber stuff
411            var retByte = new byte[1 + payload.Length];
412            retByte[0] = (byte) msgIndex;
413            payload.CopyTo(retByte, 1);
414            return retByte;
415        }
416
417        /// <summary>
418        /// generates a ct2- and p2p-compatible and processable message
419        /// </summary>
420        /// <param name="sPayload">payload data as a string</param>
421        /// <param name="msgIndex">type of message (system message, simple payload for a special use case, etc.)</param>
422        /// <returns>the message, which is processable by the ct2/p2p system</returns>
423        private byte[] GenerateMessage(string sPayload, P2PMessageIndex msgIndex)
424        {
425            return GenerateMessage(_enc.GetBytes(sPayload), msgIndex);
426        }
427
428        /// <summary>
429        /// generates a ct2- and p2p-compatible and processable message
430        /// </summary>
431        /// <param name="pubSubData">PubSubMessageType</param>
432        /// <returns>the message, which is processable by the ct2/p2p system</returns>
433        private static byte[] GenerateMessage(PubSubMessageType pubSubData)
434        {
435            var bytePubSubData = new[] {(byte) pubSubData};
436            return GenerateMessage(bytePubSubData, P2PMessageIndex.PubSub);
437        }
438
439        /// <summary>
440        /// returns the message type, e.g. PubSub or Payload message
441        /// </summary>
442        /// <param name="msgType">the FIRST byte of a raw message, received by the system</param>
443        /// <returns>the message type</returns>
444        private static P2PMessageIndex GetMessageType(byte msgType)
445        {
446            return (P2PMessageIndex) msgType;
447        }
448
449        /// <summary>
450        /// returns only the payload part of the message
451        /// </summary>
452        /// <param name="message">the raw message, received by the system, as an byte array (with the first index byte!!!)</param>
453        /// <returns>only the payload part of the message</returns>
454        private static byte[] GetMessagePayload(byte[] message)
455        {
456            if (message.Length > 1)
457            {
458                var retMsg = new byte[message.Length - 1];
459                // workaround because CopyTo doesn't work...
460                //for (int i = 0; i < message.Length-1; i++)
461                //{
462                //    retMsg[i] = message[i + 1];
463                //}
464                Buffer.BlockCopy(message, 1, retMsg, 0, retMsg.Length);
465                return retMsg;
466            }
467            return null;
468        }
469
470        #endregion
471
472        /// <summary>
473        /// workaround method. If the PAP functions are used, but the PAP system isn't
474        /// started yet. This could happen because of the plugin hierarchy and
475        /// when a p2p-using plugin uses PAP functions in the PreExecution method,
476        /// this could run into a race condition (peer plugin not computed by the CT-system
477        /// yet, but p2p-using plugin is already executed)
478        /// </summary>
479        /// <returns></returns>
480        private static bool SystemJoinedCompletely()
481        {
482            return P2PManager.Instance.IsP2PConnected() && !P2PManager.Instance.IsP2PConnecting;
483        }
484
485        private static void SendReadilyMessage(byte[] data, PeerId destinationAddress)
486        {
487            if (SystemJoinedCompletely())
488                P2PManager.Instance.P2PBase.SendToPeer(data, destinationAddress.ToByteArray());
489        }
490
491        /// <summary>
492        /// Converts a string to the PubSubMessageType if possible. Otherwise return null.
493        /// </summary>
494        /// <param name="data">Data</param>
495        /// <returns>PubSubMessageType if possible. Otherwise null.</returns>
496        private static PubSubMessageType GetPubSubType(byte data)
497        {
498            // Convert one byte data to PublishSubscribeMessageType-Enum
499            return (PubSubMessageType) data;
500        }
501    }
502}
Note: See TracBrowser for help on using the repository browser.