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