Uri.OriginalString is a bit misleading since it's computed for merged URI
[mono.git] / mcs / class / System / System.Net.Sockets / TcpClient.cs
1 // System.Net.Sockets.TcpClient.cs
2 //
3 // Author:
4 //      Phillip Pearson (pp@myelin.co.nz)
5 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
6 //      Sridhar Kulkarni (sridharkulkarni@gmail.com)
7 //
8 // Copyright (C) 2001, Phillip Pearson
9 //    http://www.myelin.co.nz
10 // Copyright (c) 2006 Novell, Inc. (http://www.novell.com)
11 //
12
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Net;
36
37 namespace System.Net.Sockets
38 {
39         public class TcpClient : IDisposable {
40                 enum Properties : uint {
41                         LingerState = 1,
42                         NoDelay = 2,
43                         ReceiveBufferSize = 4,
44                         ReceiveTimeout = 8,
45                         SendBufferSize = 16,
46                         SendTimeout = 32
47                 }
48
49                 // private data
50                 NetworkStream stream;
51                 bool active;
52                 Socket client;
53                 bool disposed;
54                 Properties values;
55                 int recv_timeout, send_timeout;
56                 int recv_buffer_size, send_buffer_size;
57                 LingerOption linger_state;
58                 bool no_delay;
59                 
60                 private void Init (AddressFamily family)
61                 {
62                         active = false;
63
64                         if(client != null) {
65                                 client.Close();
66                                 client = null;
67                         }
68
69                         client = new Socket(family, SocketType.Stream, ProtocolType.Tcp);
70                 }
71
72                 public TcpClient ()
73                 {
74                         Init(AddressFamily.InterNetwork);
75                         client.Bind(new IPEndPoint(IPAddress.Any, 0));
76                 }
77         
78 #if NET_1_1
79                 public TcpClient (AddressFamily family)
80                 {
81                         if (family != AddressFamily.InterNetwork &&
82                             family != AddressFamily.InterNetworkV6) {
83                                 throw new ArgumentException ("Family must be InterNetwork or InterNetworkV6", "family");
84                         }
85                         
86                         Init (family);
87                         IPAddress any = IPAddress.Any;
88                         if (family == AddressFamily.InterNetworkV6)
89                                 any = IPAddress.IPv6Any;
90                         client.Bind (new IPEndPoint (any, 0));
91                 }
92 #endif
93                 
94                 public TcpClient (IPEndPoint local_end_point)
95                 {
96                         Init(local_end_point.AddressFamily);
97                         client.Bind(local_end_point);
98                 }
99                 
100                 public TcpClient (string hostname, int port)
101                 {
102                         Connect(hostname, port);
103                 }
104                                 
105                 protected bool Active {
106                         get { return active; }
107                         set { active = value; }
108                 }
109                 
110 #if NET_2_0
111                 public Socket Client {
112 #else
113                 protected Socket Client {
114 #endif
115                         get { return client; }
116                         set {
117                                 client = value;
118                                 stream = null;
119                         }
120                 }
121
122 #if NET_2_0
123                 public int Available {
124                         get { return client.Available; }
125                 }
126
127                 public bool Connected {
128                         get { return client.Connected; }
129                 }
130
131 #if TARGET_JVM
132                 [MonoNotSupported ("Not supported as Socket.ExclusiveAddressUse is not supported")]
133 #endif
134                 public bool ExclusiveAddressUse {
135                         get {
136                                 return(client.ExclusiveAddressUse);
137                         }
138                         set {
139                                 client.ExclusiveAddressUse = value;
140                         }
141                 }
142 #endif
143                 internal void SetTcpClient (Socket s) 
144                 {
145                         Client = s;
146                 }
147
148                 public LingerOption LingerState {
149                         get {
150                                 if ((values & Properties.LingerState) != 0)
151                                         return linger_state;
152
153                                 return (LingerOption) client.GetSocketOption (SocketOptionLevel.Socket,
154                                                                         SocketOptionName.Linger);
155                         }
156                         set {
157                                 if (!client.Connected) {
158                                         linger_state = value;
159                                         values |= Properties.LingerState;
160                                         return;
161                                 }
162                                 client.SetSocketOption(
163                                         SocketOptionLevel.Socket,
164                                         SocketOptionName.Linger, value);
165                         }
166                 }
167                                 
168                 public bool NoDelay {
169                         get {
170                                 if ((values & Properties.NoDelay) != 0)
171                                         return no_delay;
172
173                                 return (bool)client.GetSocketOption(
174                                         SocketOptionLevel.Tcp,
175                                         SocketOptionName.NoDelay);
176                         }
177                         set {
178                                 if (!client.Connected) {
179                                         no_delay = value;
180                                         values |= Properties.NoDelay;
181                                         return;
182                                 }
183                                 client.SetSocketOption(
184                                         SocketOptionLevel.Tcp,
185                                         SocketOptionName.NoDelay, value ? 1 : 0);
186                         }
187                 }
188                                 
189                 public int ReceiveBufferSize {
190                         get {
191                                 if ((values & Properties.ReceiveBufferSize) != 0)
192                                         return recv_buffer_size;
193
194                                 return (int)client.GetSocketOption(
195                                         SocketOptionLevel.Socket,
196                                         SocketOptionName.ReceiveBuffer);
197                         }
198                         set {
199                                 if (!client.Connected) {
200                                         recv_buffer_size = value;
201                                         values |= Properties.ReceiveBufferSize;
202                                         return;
203                                 }
204                                 client.SetSocketOption(
205                                         SocketOptionLevel.Socket,
206                                         SocketOptionName.ReceiveBuffer, value);
207                         }
208                 }
209                         
210                 public int ReceiveTimeout {
211                         get {
212                                 if ((values & Properties.ReceiveTimeout) != 0)
213                                         return recv_timeout;
214
215                                 return (int)client.GetSocketOption(
216                                         SocketOptionLevel.Socket,
217                                         SocketOptionName.ReceiveTimeout);
218                         }
219                         set {
220                                 if (!client.Connected) {
221                                         recv_timeout = value;
222                                         values |= Properties.ReceiveTimeout;
223                                         return;
224                                 }
225                                 client.SetSocketOption(
226                                         SocketOptionLevel.Socket,
227                                         SocketOptionName.ReceiveTimeout, value);
228                         }
229                 }
230                 
231                 public int SendBufferSize {
232                         get {
233                                 if ((values & Properties.SendBufferSize) != 0)
234                                         return send_buffer_size;
235
236                                 return (int)client.GetSocketOption(
237                                         SocketOptionLevel.Socket,
238                                         SocketOptionName.SendBuffer);
239                         }
240                         set {
241                                 if (!client.Connected) {
242                                         send_buffer_size = value;
243                                         values |= Properties.SendBufferSize;
244                                         return;
245                                 }
246                                 client.SetSocketOption(
247                                         SocketOptionLevel.Socket,
248                                         SocketOptionName.SendBuffer, value);
249                         }
250                 }
251                 
252                 public int SendTimeout {
253                         get {
254                                 if ((values & Properties.SendTimeout) != 0)
255                                         return send_timeout;
256
257                                 return (int)client.GetSocketOption(
258                                         SocketOptionLevel.Socket,
259                                         SocketOptionName.SendTimeout);
260                         }
261                         set {
262                                 if (!client.Connected) {
263                                         send_timeout = value;
264                                         values |= Properties.SendTimeout;
265                                         return;
266                                 }
267                                 client.SetSocketOption(
268                                         SocketOptionLevel.Socket,
269                                         SocketOptionName.SendTimeout, value);
270                         }
271                 }
272                 
273                 
274                 // methods
275                 
276                 public void Close ()
277                 {
278                         ((IDisposable) this).Dispose ();
279                 }
280                 
281                 public void Connect (IPEndPoint remote_end_point)
282                 {
283                         try {
284                                 client.Connect(remote_end_point);
285                                 active = true;
286                         } finally {
287                                 CheckDisposed ();
288                         }
289                 }
290                 
291                 public void Connect (IPAddress address, int port)
292                 {
293                         Connect(new IPEndPoint(address, port));
294                 }
295
296                 void SetOptions ()
297                 {
298                         Properties props = values;
299                         values = 0;
300
301                         if ((props & Properties.LingerState) != 0)
302                                 LingerState = linger_state;
303                         if ((props & Properties.NoDelay) != 0)
304                                 NoDelay = no_delay;
305                         if ((props & Properties.ReceiveBufferSize) != 0)
306                                 ReceiveBufferSize = recv_buffer_size;
307                         if ((props & Properties.ReceiveTimeout) != 0)
308                                 ReceiveTimeout = recv_timeout;
309                         if ((props & Properties.SendBufferSize) != 0)
310                                 SendBufferSize = send_buffer_size;
311                         if ((props & Properties.SendTimeout) != 0)
312                                 SendTimeout = send_timeout;
313                 }
314
315                 public void Connect (string hostname, int port)
316                 {
317                         IPAddress [] addresses = Dns.GetHostAddresses (hostname);
318                         Connect (addresses, port);
319                 }
320
321 #if NET_2_0
322                 public
323 #else
324                 private
325 #endif
326                 void Connect (IPAddress[] ipAddresses, int port)
327                 {
328                         CheckDisposed ();
329                         
330                         if (ipAddresses == null) {
331                                 throw new ArgumentNullException ("ipAddresses");
332                         }
333                         
334                         for(int i = 0; i < ipAddresses.Length; i++) {
335                                 try {
336                                         IPAddress address = ipAddresses[i];
337
338                                         if (address.Equals (IPAddress.Any) ||
339                                             address.Equals (IPAddress.IPv6Any)) {
340                                                 throw new SocketException ((int)SocketError.AddressNotAvailable);
341                                         }
342
343                                         Init (address.AddressFamily);
344                                         
345                                         if (address.AddressFamily == AddressFamily.InterNetwork) {
346                                                 client.Bind (new IPEndPoint (IPAddress.Any, 0));
347 #if NET_1_1
348                                         } else if (address.AddressFamily == AddressFamily.InterNetworkV6) {
349                                                 client.Bind (new IPEndPoint (IPAddress.IPv6Any, 0));
350 #endif
351                                         } else {
352                                                 throw new NotSupportedException ("This method is only valid for sockets in the InterNetwork and InterNetworkV6 families");
353                                         }
354
355                                         Connect (new IPEndPoint (address, port));
356                                         
357                                         if (values != 0) {
358                                                 SetOptions ();
359                                         }
360                                         
361                                         break;
362                                 } catch (Exception e) {
363                                         /* Reinitialise the socket so
364                                          * other properties still work
365                                          * (see no-arg constructor)
366                                          */
367                                         Init (AddressFamily.InterNetwork);
368
369                                         /* This is the last known
370                                          * address, so re-throw the
371                                          * exception
372                                          */
373                                         if (i == ipAddresses.Length - 1) {
374                                                 throw e;
375                                         }
376                                 }
377                         }
378                 }
379                 
380 #if NET_2_0             
381                 public void EndConnect (IAsyncResult asyncResult)
382                 {
383                         client.EndConnect (asyncResult);
384                 }
385                 
386 #if TARGET_JVM
387                 [MonoNotSupported ("Not supported as Socket.BeginConnect is not supported")]
388 #endif
389                 public IAsyncResult BeginConnect (IPAddress address, int port,
390                                                   AsyncCallback callback,
391                                                   object state)
392                 {
393                         return(client.BeginConnect (address, port, callback,
394                                                     state));
395                 }
396                 
397 #if TARGET_JVM
398                 [MonoNotSupported ("Not supported as Socket.BeginConnect is not supported")]
399 #endif
400                 public IAsyncResult BeginConnect (IPAddress[] addresses,
401                                                   int port,
402                                                   AsyncCallback callback,
403                                                   object state)
404                 {
405                         return(client.BeginConnect (addresses, port, callback,
406                                                     state));
407                 }
408                 
409 #if TARGET_JVM
410                 [MonoNotSupported ("Not supported as Socket.BeginConnect is not supported")]
411 #endif
412                 public IAsyncResult BeginConnect (string host, int port,
413                                                   AsyncCallback callback,
414                                                   object state)
415                 {
416                         return(client.BeginConnect (host, port, callback,
417                                                     state));
418                 }
419 #endif
420                 
421                 void IDisposable.Dispose ()
422                 {
423                         Dispose (true);
424                         GC.SuppressFinalize (this);
425                 }
426
427                 protected virtual void Dispose (bool disposing)
428                 {
429                         if (disposed)
430                                 return;
431                         disposed = true;
432
433                         if (disposing) {
434                                 // release managed resources
435                                 NetworkStream s = stream;
436                                 stream = null;
437                                 if (s != null) {
438                                         // This closes the socket as well, as the NetworkStream
439                                         // owns the socket.
440                                         s.Close();
441                                         active = false;
442                                         s = null;
443                                 } else if (client != null){
444                                         client.Close ();
445                                         client = null;
446                                 }
447                         }
448                 }
449                 
450                 ~TcpClient ()
451                 {
452                         Dispose (false);
453                 }
454                 
455                 public NetworkStream GetStream()
456                 {
457                         try {
458                                 if (stream == null)
459                                         stream = new NetworkStream (client, true);
460                                 return stream;
461                         }
462                         finally { CheckDisposed (); }
463                 }
464                 
465                 private void CheckDisposed ()
466                 {
467                         if (disposed)
468                                 throw new ObjectDisposedException (GetType().FullName);
469                 }
470         }
471 }
472