a97ecf4e97bac3a8daaa3d00717b891c034f303b
[mono.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels.Tcp / TcpServerChannel.cs
1 //
2 // System.Runtime.Remoting.Channels.Tcp.TcpServerChannel.cs
3 //
4 // Author: Rodrigo Moya (rodrigo@ximian.com)
5 //         Lluis Sanchez Gual (lluis@ideary.com)
6 //
7 // 2002 (C) Copyright, Ximian, Inc.
8 //
9
10 using System.Collections;
11 using System.Runtime.Remoting.Messaging;
12 using System.Text.RegularExpressions;
13 using System.Net.Sockets;
14 using System.Net;
15 using System.Threading;
16 using System.IO;
17
18 namespace System.Runtime.Remoting.Channels.Tcp
19 {
20         public class TcpServerChannel : IChannelReceiver, IChannel
21         {
22                 int port = 0;
23                 string name = "tcp";
24                 string host;
25                 int priority = 1;
26                 Thread server_thread = null;
27                 TcpListener listener;
28                 TcpServerTransportSink sink;
29                 ChannelDataStore channel_data;
30                 int _maxConcurrentConnections = 100;
31                 ArrayList _activeConnections = new ArrayList();
32                 
33                 void Init (IServerChannelSinkProvider serverSinkProvider) 
34                 {
35                         if (serverSinkProvider == null) {
36                                 serverSinkProvider = new BinaryServerFormatterSinkProvider ();
37                         }
38
39                         host = Dns.GetHostByName(Dns.GetHostName()).HostName;
40                         
41                         string [] uris = null;
42                         
43                         if (port != 0) {
44                                 uris = new String [1];
45                                 uris [0] = GetChannelUri ();
46                         }
47
48                         // Gets channel data from the chain of channel providers
49
50                         channel_data = new ChannelDataStore (uris);
51                         IServerChannelSinkProvider provider = serverSinkProvider;\r
52                         while (provider != null)\r
53                         {\r
54                                 provider.GetChannelData(channel_data);\r
55                                 provider = provider.Next;\r
56                         }\r
57
58                         // Creates the sink chain that will process all incoming messages
59
60                         IServerChannelSink next_sink = ChannelServices.CreateServerChannelSinkChain (serverSinkProvider, this);\r
61                         sink = new TcpServerTransportSink (next_sink);
62                         
63                         listener = new TcpListener (port);
64                         StartListening (null);
65                 }
66                 
67                 public TcpServerChannel (int port)
68                 {
69                         this.port = port;
70                         Init (null);
71                 }
72
73                 public TcpServerChannel (IDictionary properties,
74                                          IServerChannelSinkProvider serverSinkProvider)
75                 {
76                         port = Int32.Parse ((string)properties ["port"]);
77                         Init (serverSinkProvider);
78                 }
79
80                 public TcpServerChannel (string name, int port,
81                                          IServerChannelSinkProvider serverSinkProvider)
82                 {
83                         this.name = name;
84                         this.port = port;
85                         Init (serverSinkProvider);
86                 }
87                 
88                 public TcpServerChannel (string name, int port)
89                 {
90                         this.name = name;
91                         this.port = port;
92                         Init (null);
93                 }
94                 
95                 public object ChannelData
96                 {
97                         get {
98                                 return channel_data;
99                         }
100                 }
101
102                 public string ChannelName
103                 {
104                         get {
105                                 return name;
106                         }
107                 }
108
109                 public int ChannelPriority
110                 {
111                         get {
112                                 return priority;
113                         }
114                 }
115
116                 public string GetChannelUri ()
117                 {
118                         return "tcp://" + host + ":" + port;
119                 }
120                 
121                 public string[] GetUrlsForUri (string uri)\r
122                 {\r
123                         if (!uri.StartsWith ("/")) uri = "/" + uri;
124 \r
125                         string [] chnl_uris = channel_data.ChannelUris;\r
126                         string [] result = new String [chnl_uris.Length];\r
127 \r
128                         for (int i = 0; i < chnl_uris.Length; i++) \r
129                                 result [i] = chnl_uris [i] + uri;\r
130                         \r
131                         return result;\r
132                 }\r
133
134                 public string Parse (string url, out string objectURI)
135                 {
136                         return TcpChannel.ParseChannelUrl (url, out objectURI);
137                 }
138
139                 void WaitForConnections ()
140                 {
141                         try
142                         {
143                                 while (true) 
144                                 {
145                                         TcpClient client = listener.AcceptTcpClient ();
146                                         CreateListenerConnection (client);
147                                 }
148                         }
149                         catch
150                         {}
151                 }
152
153                 internal void CreateListenerConnection (TcpClient client)
154                 {
155                         lock (_activeConnections)
156                         {
157                                 if (_activeConnections.Count >= _maxConcurrentConnections)
158                                         Monitor.Wait (_activeConnections);
159
160                                 if (server_thread == null) return;      // Server was stopped while waiting
161
162                                 ClientConnection reader = new ClientConnection (this, client, sink);
163                                 Thread thread = new Thread (new ThreadStart (reader.ProcessMessages));
164                                 thread.Start();
165                                 thread.IsBackground = true;
166                                 _activeConnections.Add (thread);
167                         }
168                 }
169
170                 internal void ReleaseConnection (Thread thread)
171                 {
172                         lock (_activeConnections)
173                         {
174                                 _activeConnections.Remove (thread);
175                                 Monitor.Pulse (_activeConnections);
176                         }
177                 }
178                 
179                 public void StartListening (object data)
180                 {
181                         if (server_thread == null) {
182                                 listener.Start ();
183                                 if (port == 0) {
184                                         port = ((IPEndPoint)listener.LocalEndpoint).Port;
185                                         channel_data.ChannelUris = new String [1];
186                                         channel_data.ChannelUris [0] = GetChannelUri ();
187                                 }
188
189                                 server_thread = new Thread (new ThreadStart (WaitForConnections));
190                                 server_thread.IsBackground = true;
191                                 server_thread.Start ();
192                         }
193                 }
194
195                 public void StopListening (object data)
196                 {
197                         if (server_thread == null) return;
198
199                         lock (_activeConnections)
200                         {
201                                 server_thread.Abort ();
202                                 server_thread = null;
203                                 listener.Stop ();
204
205                                 foreach (Thread thread in _activeConnections)
206                                         thread.Abort();
207
208                                 _activeConnections.Clear();
209                                 Monitor.PulseAll (_activeConnections);
210                         }
211                 }
212         }
213
214         class ClientConnection
215         {
216                 TcpClient _client;
217                 TcpServerTransportSink _sink;
218                 Stream _stream;
219                 TcpServerChannel _serverChannel;
220
221                 byte[] _buffer = new byte[TcpMessageIO.DefaultStreamBufferSize];
222
223                 public ClientConnection (TcpServerChannel serverChannel, TcpClient client, TcpServerTransportSink sink)
224                 {
225                         _serverChannel = serverChannel;
226                         _client = client;
227                         _sink = sink;
228                 }
229
230                 public Stream Stream
231                 {
232                         get { return _stream; }
233                 }
234
235                 public byte[] Buffer
236                 {
237                         get { return _buffer; }
238                 }
239
240                 public void ProcessMessages()
241                 {
242                         _stream = _client.GetStream();
243
244                         try
245                         {
246                                 bool end = false;
247                                 while (!end)
248                                 {
249                                         MessageType type = TcpMessageIO.ReceiveMessageType (_stream);
250
251                                         switch (type)
252                                         {
253                                                 case MessageType.MethodMessage:
254                                                         _sink.InternalProcessMessage (this);
255                                                         break;
256
257                                                 case MessageType.CancelSignal:
258                                                         end = true;
259                                                         break;
260                                         }
261                                 }
262                         }
263                         catch
264                         {}
265
266                         _stream.Close();
267                         _serverChannel.ReleaseConnection (Thread.CurrentThread);
268                 }
269         }
270 }