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

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

Fixed exception on calling Shutdown before Run

File size: 8.4 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
39    enum State
40    {
41        Created,
42        Running,
43        Exiting,
44        Dead
45    }
46    private State state = State.Created;
47    #endregion
48
49    ///<summary>
50    /// Starts the server. Will block as long as the server runs, you might want to start this in an additional thread.
51    ///</summary>
52    public void Run()
53    {
54        if (OnJobCompleted == null ||
55            OnClientAuth == null ||
56            OnClientDisconnected == null ||
57            OnClientRequestedJob == null ||
58            OnErrorLog == null)
59        {
60            throw new Exception("One of the mandatory events was not bound");
61        }
62
63        try
64        {
65            lock (this)
66            {
67                if (state != State.Created)
68                {
69                    if (OnErrorLog != null)
70                    {
71                        OnErrorLog("Invalid state: " + state);
72                    }
73                    return;
74                }
75
76                // has to be in lock, otherwise a Stop call can cause an object
77                // disposed exception on tcpListener.start()
78                state = State.Running;
79                tcpListener = new TcpListener(IPAddress.Any, Port);
80                tcpListener.Start();
81            }
82
83
84            while (state == State.Running)
85            {
86                TcpClient client = tcpListener.AcceptTcpClient();
87                lock (connectedClients)
88                {
89                    connectedClients.Add(client.Client.RemoteEndPoint, client);
90                }
91                Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient));
92                clientThread.Start(client);
93            }
94        }
95        catch (Exception e)
96        {
97            if (state == State.Running && OnErrorLog != null)
98            {
99                OnErrorLog("CryptoolServer: Got Exception while running "+e.Message);
100            }
101        }
102        finally
103        {
104            try
105            {
106                state = State.Dead;
107                tcpListener.Stop();
108            }
109            catch (Exception)
110            {
111            }
112            lock (connectedClients)
113            {
114                foreach (var client in connectedClients)
115                    client.Value.Close();
116            }
117        }
118    }
119   
120    public void SendJob(JobInput j, EndPoint i)
121    {
122        TcpClient client = null;
123        lock(connectedClients)
124        {
125            if (!connectedClients.TryGetValue(i, out client))
126            {
127                if (OnErrorLog != null)
128                    OnErrorLog("Tried to send job to not present external client " + i);
129                return;
130            }
131        }
132
133        lock(client)
134        {
135            var wrapped = new PlatformIndependentWrapper(client);
136
137            wrapped.WriteInt((int)ServerOpcodes.NEW_JOB);
138            wrapped.WriteString(j.Guid);
139            wrapped.WriteString(j.Src);
140            wrapped.WriteInt(j.Key.Length);
141            wrapped.WriteBytes(j.Key);
142            wrapped.WriteInt(j.LargerThen ? 1 : 0);
143            wrapped.WriteInt(j.Size);
144            wrapped.WriteInt(j.ResultSize);
145        }
146    }
147
148    private void HandleClient(object obj)
149    {
150        var client = obj as TcpClient;
151        EndPoint ep = client.Client.RemoteEndPoint;
152
153        bool identified = false;
154        try
155        {
156            var wrapped = new PlatformIndependentWrapper(client);
157            while (true)
158            {
159                switch ((ClientOpcodes)wrapped.ReadInt())
160                {
161                    case ClientOpcodes.HELLO:
162                        {
163                            String name = wrapped.ReadString();
164                            String password = wrapped.ReadString();
165                            if (OnClientAuth == null || !OnClientAuth(ep, name, password))
166                            {
167                                wrapped.WriteInt((int)ServerOpcodes.WRONG_PASSWORD);
168                                return;
169                            }
170                            identified = true;
171                        }
172                        break;
173
174                    case ClientOpcodes.JOB_RESULT:
175                        {
176                            if (!identified)
177                            {
178                                if (OnErrorLog != null)
179                                {
180                                    OnErrorLog("Client '" + ep + "' tried to post result without identification");
181                                }
182                                return;
183                            }
184
185                            var jobGuid = wrapped.ReadString();
186                            var resultList = new List<KeyValuePair<float, int>>();
187                            var resultListLength = wrapped.ReadInt();
188                            for (int c = 0; c < resultListLength; c++)
189                            {
190                                var key = wrapped.ReadInt();
191                                var cost = wrapped.ReadFloat();
192                                resultList.Add(new KeyValuePair<float, int>(cost, key));
193                            }
194
195                            JobResult rs = new JobResult();
196                            rs.Guid = jobGuid;
197                            rs.ResultList = resultList;
198
199                            if (OnJobCompleted != null)
200                            {
201                                OnJobCompleted(ep, rs);
202                            }
203                        }
204                        break;
205
206                    case ClientOpcodes.JOB_REQUEST:
207                        {
208                            if (!identified)
209                            {
210                                if (OnErrorLog != null)
211                                {
212                                    OnErrorLog("Client '" + ep + "' tried to request job without identification");
213                                }
214                                return;
215                            }
216
217                            if (OnClientRequestedJob != null)
218                            {
219                                OnClientRequestedJob(ep);
220                            }
221                        }
222                        break;
223                }
224            }
225        }
226        catch (SocketException)
227        {
228            // left blank intentionally. Will be thrown on client disconnect.
229        }
230        catch (Exception e)
231        {
232            if (OnErrorLog != null)
233            {
234                OnErrorLog("Client '" + ep + "' caused exception " + e);
235            }
236        }
237        finally
238        {
239            // just to be sure..
240            client.Close();
241
242            lock (connectedClients)
243            {
244                connectedClients.Remove(ep);
245            }
246
247            if (OnClientDisconnected != null)
248                OnClientDisconnected(ep);
249        }
250    }
251
252    /// <summary>
253    /// Closes this server. Any concurrent call to Run() in any other thread will return.
254    /// </summary>
255    public void Shutdown()
256    {
257        lock (this)
258        {
259            if (state == State.Dead ||
260                state == State.Exiting)
261            {
262                return;
263            }
264            state = State.Exiting;
265            try
266            {
267                tcpListener.Stop();
268            }
269            catch (Exception)
270            {
271            }
272        }
273    }
274}
Note: See TracBrowser for help on using the repository browser.