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
Line 
1using System;
2using System.Text;
3using System.Net.Sockets;
4using System.Threading;
5using System.Net;
6using System.Collections.Generic;
7
8class CryptoolServer
9{
10    #region Properties
11
12    public int Port {get;set;}
13
14    #endregion
15
16    #region Events
17
18    public delegate void JobCompletedDelegate(EndPoint ipep, JobResult j);
19    public event JobCompletedDelegate OnJobCompleted;
20
21    public delegate bool ClientConnectedDelegate(EndPoint ipep, String name, String password);
22    public ClientConnectedDelegate OnClientAuth;
23
24    public delegate void EndPointDelegate(EndPoint ipep);
25    public event EndPointDelegate OnClientDisconnected;
26
27    public event EndPointDelegate OnClientRequestedJob;
28
29    public delegate void StringDelegate(String str);
30    public event StringDelegate OnErrorLog;
31   
32    #endregion
33
34    #region Variables
35
36    private Dictionary<EndPoint, TcpClient> connectedClients = new Dictionary<EndPoint, TcpClient>();
37    private TcpListener tcpListener;
38    private bool running = false;
39
40    #endregion
41
42    ///<summary>
43    /// Starts the server. Will block as long as the server runs, you might want to start this in an additional thread.
44    ///</summary>
45    public void Run()
46    {
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
56        lock (this)
57        {
58            if (running)
59            {
60                throw new Exception("Invalid state: Already running");
61            }
62            running = true;
63        }
64
65        try
66        {
67            tcpListener = new TcpListener(IPAddress.Any, Port);
68            tcpListener.Start();
69            while (running)
70            {
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);
78            }
79        }
80        catch (ThreadInterruptedException)
81        {
82        }
83        catch (SocketException e)
84        {
85            if (running && OnErrorLog != null)
86            {
87                OnErrorLog("CryptoolServer: Got SocketException while running");
88            }
89        }
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        }
105    }
106   
107    public void SendJob(JobInput j, EndPoint i)
108    {
109        TcpClient client = null;
110        lock(connectedClients)
111        {
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            }
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);
128            wrapped.WriteBytes(j.Key);
129            wrapped.WriteInt(j.LargerThen ? 1 : 0);
130            wrapped.WriteInt(j.Size);
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
140        bool identified = false;
141        try
142        {
143            var wrapped = new PlatformIndependentWrapper(client);
144            while (true)
145            {
146                switch ((ClientOpcodes)wrapped.ReadInt())
147                {
148                    case ClientOpcodes.HELLO:
149                        {
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;
158                        }
159                        break;
160
161                    case ClientOpcodes.JOB_RESULT:
162                        {
163                            if (!identified)
164                            {
165                                if (OnErrorLog != null)
166                                {
167                                    OnErrorLog("Client '" + ep + "' tried to post result without identification");
168                                }
169                                return;
170                            }
171
172                            var jobGuid = wrapped.ReadString();
173                            var resultList = new List<KeyValuePair<float, int>>();
174                            var resultListLength = wrapped.ReadInt();
175                            for (int c = 0; c < resultListLength; c++)
176                            {
177                                var key = wrapped.ReadInt();
178                                var cost = wrapped.ReadFloat();
179                                resultList.Add(new KeyValuePair<float, int>(cost, key));
180                            }
181
182                            JobResult rs = new JobResult();
183                            rs.Guid = jobGuid;
184                            rs.ResultList = resultList;
185
186                            if (OnJobCompleted != null)
187                            {
188                                OnJobCompleted(ep, rs);
189                            }
190                        }
191                        break;
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;
210                }
211            }
212        }
213        catch (SocketException)
214        {
215            // left blank intentionally. Will be thrown on client disconnect.
216        }
217        catch (Exception e)
218        {
219            if (OnErrorLog != null)
220            {
221                OnErrorLog("Client '" + ep + "' caused exception " + e);
222            }
223        }
224        finally
225        {
226            // just to be sure..
227            client.Close();
228
229            lock (connectedClients)
230            {
231                connectedClients.Remove(ep);
232            }
233
234            if (OnClientDisconnected != null)
235                OnClientDisconnected(ep);
236        }
237    }
238
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    }
250}
Note: See TracBrowser for help on using the repository browser.