source: trunk/CrypPlugins/KeySearcher/Server/Server.cs @ 2417

Last change on this file since 2417 was 2417, checked in by schwittmann, 11 years ago

Several ExternalClient improvements:

  • Separated connected and request job events
  • Added basic authentication
  • Lookup DNS again after getting disconnected
  • Fixed some socket exceptions
  • Prevent slow clients from submitting old jobs by using unique job identifiers
File size: 7.8 KB
RevLine 
[2208]1using System;
2using System.Text;
3using System.Net.Sockets;
4using System.Threading;
5using System.Net;
6using System.Collections.Generic;
7
8class CryptoolServer
9{
[2417]10    #region Properties
11
[2208]12    public int Port {get;set;}
[2417]13
14    #endregion
15
16    #region Events
17
[2208]18    public delegate void JobCompletedDelegate(EndPoint ipep, JobResult j);
19    public event JobCompletedDelegate OnJobCompleted;
20
[2417]21    public delegate bool ClientConnectedDelegate(EndPoint ipep, String name, String password);
22    public ClientConnectedDelegate OnClientAuth;
[2208]23
[2417]24    public delegate void EndPointDelegate(EndPoint ipep);
25    public event EndPointDelegate OnClientDisconnected;
[2208]26
[2417]27    public event EndPointDelegate OnClientRequestedJob;
[2208]28
[2417]29    public delegate void StringDelegate(String str);
30    public event StringDelegate OnErrorLog;
31   
32    #endregion
33
34    #region Variables
35
[2208]36    private Dictionary<EndPoint, TcpClient> connectedClients = new Dictionary<EndPoint, TcpClient>();
[2411]37    private TcpListener tcpListener;
38    private bool running = false;
[2208]39
[2417]40    #endregion
41
[2208]42    ///<summary>
[2411]43    /// Starts the server. Will block as long as the server runs, you might want to start this in an additional thread.
[2208]44    ///</summary>
45    public void Run()
46    {
[2417]47        if (OnJobCompleted == null ||
48            OnClientAuth == null ||
49            OnClientDisconnected == null ||
50            OnClientRequestedJob == null ||
51            OnErrorLog == null)
52        {
53            throw new Exception("One of the mandatory events was not bound");
54        }
55
[2411]56        lock (this)
57        {
58            if (running)
59            {
60                throw new Exception("Invalid state: Already running");
61            }
62            running = true;
63        }
64
[2214]65        try
[2208]66        {
[2214]67            tcpListener = new TcpListener(IPAddress.Any, Port);
68            tcpListener.Start();
[2411]69            while (running)
[2208]70            {
[2214]71                TcpClient client = tcpListener.AcceptTcpClient();
72                lock (connectedClients)
73                {
74                    connectedClients.Add(client.Client.RemoteEndPoint, client);
75                }
76                Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient));
77                clientThread.Start(client);
[2208]78            }
79        }
[2214]80        catch (ThreadInterruptedException)
81        {
82        }
[2411]83        catch (SocketException e)
84        {
[2417]85            if (running && OnErrorLog != null)
[2411]86            {
[2417]87                OnErrorLog("CryptoolServer: Got SocketException while running");
[2411]88            }
89        }
[2417]90        finally
91        {
92            try
93            {
94                tcpListener.Stop();
95            }
96            catch (Exception)
97            {
98            }
99            lock (connectedClients)
100            {
101                foreach (var client in connectedClients)
102                    client.Value.Close();
103            }
104        }
[2208]105    }
[2214]106   
[2208]107    public void SendJob(JobInput j, EndPoint i)
108    {
109        TcpClient client = null;
110        lock(connectedClients)
111        {
[2417]112            if (!connectedClients.TryGetValue(i, out client))
113            {
114                if (OnErrorLog != null)
115                    OnErrorLog("Tried to send job to not present external client " + i);
116                return;
117            }
[2208]118        }
119
120        lock(client)
121        {
122            var wrapped = new PlatformIndependentWrapper(client);
123
124            wrapped.WriteInt((int)ServerOpcodes.NEW_JOB);
125            wrapped.WriteString(j.Guid);
126            wrapped.WriteString(j.Src);
127            wrapped.WriteInt(j.Key.Length);
[2202]128            wrapped.WriteBytes(j.Key);
[2208]129            wrapped.WriteInt(j.LargerThen ? 1 : 0);
[2202]130            wrapped.WriteInt(j.Size);
[2208]131            wrapped.WriteInt(j.ResultSize);
132        }
133    }
134
135    private void HandleClient(object obj)
136    {
137        var client = obj as TcpClient;
138        EndPoint ep = client.Client.RemoteEndPoint;
139
[2417]140        bool identified = false;
[2208]141        try
142        {
143            var wrapped = new PlatformIndependentWrapper(client);
[2417]144            while (true)
[2208]145            {
[2417]146                switch ((ClientOpcodes)wrapped.ReadInt())
[2208]147                {
148                    case ClientOpcodes.HELLO:
149                        {
[2417]150                            String name = wrapped.ReadString();
151                            String password = wrapped.ReadString();
152                            if (OnClientAuth == null || !OnClientAuth(ep, name, password))
153                            {
154                                wrapped.WriteInt((int)ServerOpcodes.WRONG_PASSWORD);
155                                return;
156                            }
157                            identified = true;
[2208]158                        }
159                        break;
160
161                    case ClientOpcodes.JOB_RESULT:
162                        {
[2417]163                            if (!identified)
164                            {
165                                if (OnErrorLog != null)
166                                {
167                                    OnErrorLog("Client '" + ep + "' tried to post result without identification");
168                                }
169                                return;
170                            }
171
[2202]172                            var jobGuid = wrapped.ReadString();
[2214]173                            var resultList = new List<KeyValuePair<float, int>>();
[2208]174                            var resultListLength = wrapped.ReadInt();
[2202]175                            for (int c = 0; c < resultListLength; c++)
176                            {
177                                var key = wrapped.ReadInt();
178                                var cost = wrapped.ReadFloat();
[2214]179                                resultList.Add(new KeyValuePair<float, int>(cost, key));
[2208]180                            }
181
182                            JobResult rs = new JobResult();
[2202]183                            rs.Guid = jobGuid;
[2208]184                            rs.ResultList = resultList;
185
[2417]186                            if (OnJobCompleted != null)
[2208]187                            {
188                                OnJobCompleted(ep, rs);
189                            }
190                        }
191                        break;
[2417]192
193                    case ClientOpcodes.JOB_REQUEST:
194                        {
195                            if (!identified)
196                            {
197                                if (OnErrorLog != null)
198                                {
199                                    OnErrorLog("Client '" + ep + "' tried to request job without identification");
200                                }
201                                return;
202                            }
203
204                            if (OnClientRequestedJob != null)
205                            {
206                                OnClientRequestedJob(ep);
207                            }
208                        }
209                        break;
[2208]210                }
211            }
212        }
[2417]213        catch (SocketException)
[2208]214        {
[2417]215            // left blank intentionally. Will be thrown on client disconnect.
[2208]216        }
[2417]217        catch (Exception e)
[2208]218        {
[2417]219            if (OnErrorLog != null)
220            {
221                OnErrorLog("Client '" + ep + "' caused exception " + e);
222            }
[2208]223        }
[2417]224        finally
225        {
226            // just to be sure..
227            client.Close();
[2208]228
[2417]229            lock (connectedClients)
230            {
231                connectedClients.Remove(ep);
232            }
233
234            if (OnClientDisconnected != null)
235                OnClientDisconnected(ep);
236        }
[2208]237    }
238
[2411]239    /// <summary>
240    /// Closes this server. Any concurrent call to Run() in any other thread will return.
241    /// </summary>
242    public void Shutdown()
243    {
244        lock (this)
245        {
246            running = false;
247            tcpListener.Stop();
248        }
249    }
[2208]250}
Note: See TracBrowser for help on using the repository browser.