2004-05-13 Dick Porter <dick@ximian.com>
[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 //
6 // Copyright (C) 2001, Phillip Pearson
7 //    http://www.myelin.co.nz
8 //
9
10 // NB: This is untested (probably buggy) code - take care if using it
11
12 using System;
13 using System.Net;
14
15 namespace System.Net.Sockets
16 {
17         /// <remarks>
18         /// A slightly more abstracted way to create an
19         /// outgoing network connections than a Socket.
20         /// </remarks>
21         public class TcpClient : IDisposable
22         {
23                 // private data
24                 
25                 private NetworkStream stream;
26                 private bool active;
27                 private Socket client;
28                 private bool disposed = false;
29                 
30                 // constructor
31
32                 /// <summary>
33                 /// Some code that is shared between the constructors.
34                 /// </summary>
35                 private void Init (AddressFamily family)
36                 {
37                         active = false;
38
39                         if(client != null) {\r
40                                 client.Close();\r
41                                 client = null;\r
42                         }
43
44                         client = new Socket(family, SocketType.Stream, ProtocolType.Tcp);
45                 }
46
47                 /// <summary>
48                 /// Constructs a new TcpClient with no connection set up
49                 /// </summary>
50                 public TcpClient ()
51                 {
52                         Init(AddressFamily.InterNetwork);
53                         client.Bind(new IPEndPoint(IPAddress.Any, 0));
54                 }
55         
56 #if NET_1_1
57                 public TcpClient (AddressFamily family)
58                 {
59                         if (family != AddressFamily.InterNetwork &&
60                             family != AddressFamily.InterNetworkV6) {
61                                 throw new ArgumentException ("Family must be InterNetwork or InterNetworkV6", "family");
62                         }
63                         
64                         Init (family);
65                         client.Bind (new IPEndPoint (IPAddress.Any, 0));
66                 }
67 #endif
68                 
69                 /// <summary>
70                 /// Constructs a new TcpClient with a specified local endpoint.
71                 /// Use this if you want to have your connections originating
72                 /// from a certain port, or a certain IP (on a multi homed
73                 /// system).
74                 /// </summary>
75                 /// <param name="local_end_point">The aforementioned local endpoint</param>
76                 public TcpClient (IPEndPoint local_end_point)
77                 {
78                         Init(local_end_point.AddressFamily);
79                         client.Bind(local_end_point);
80                 }
81                 
82                 /// <summary>
83                 /// Constructs a new TcpClient and connects to a specified
84                 /// host on a specified port.  A quick way to set up a network
85                 /// connection.
86                 /// </summary>
87                 /// <param name="hostname">The host to connect to, e.g.
88                 /// 192.168.0.201 or www.myelin.co.nz</param>
89                 /// <param name="port">The port to connect to, e.g. 80 for HTTP</param>
90                 public TcpClient (string hostname, int port)
91                 {
92                         Connect(hostname, port);
93                 }
94                                 
95                 /// <summary>
96                 /// A flag that is 'true' if the TcpClient has an active connection
97                 /// </summary>
98                 protected bool Active
99                 {
100                         get { return active; }
101                         set { active = value; }
102                 }
103                 
104                 /// <summary>
105                 /// The socket that all network comms passes through
106                 /// </summary>
107                 protected Socket Client
108                 {
109                         get { return client; }
110                         set {
111                                 client = value;
112                                 stream = null;
113                         }
114                 }
115
116                 /// <summary>
117                 /// Internal function to allow TcpListener.AcceptTcpClient
118                 /// to work (it needs to be able to set protected property
119                 /// 'Client')
120                 /// </summary>
121                 /// <param name="s"></param>
122                 internal void SetTcpClient (Socket s) 
123                 {
124                         Client = s;
125                 }
126                 
127                 /// <summary>
128                 /// If set, the socket will remain open after it has been
129                 /// instructed to close, in order to send data that remains
130                 /// in the buffer.
131                 /// </summary>
132                 public LingerOption LingerState
133                 {
134                         get {
135                                 return (LingerOption)client.GetSocketOption(
136                                         SocketOptionLevel.Socket,
137                                         SocketOptionName.Linger);
138                         }
139                         set {
140                                 client.SetSocketOption(
141                                         SocketOptionLevel.Socket,
142                                         SocketOptionName.Linger, value);
143                         }
144                 }
145                                 
146                 /// <summary>
147                 /// <p>If set, outbound data will be sent at once rather than collected
148                 /// until enough is available to fill a packet.</p>
149                 /// 
150                 /// <p>This is the TCP_NODELAY sockopt from BSD sockets and WinSock.
151                 /// For more information, look up the Nagle algorithm.</p>
152                 /// </summary>
153                 public bool NoDelay
154                 {
155                         get {
156                                 return (bool)client.GetSocketOption(
157                                         SocketOptionLevel.Tcp,
158                                         SocketOptionName.NoDelay);
159                         }
160                         set {
161                                 client.SetSocketOption(
162                                         SocketOptionLevel.Tcp,
163                                         SocketOptionName.NoDelay, value);
164                         }
165                 }
166                                 
167                 /// <summary>
168                 /// How big the receive buffer is (from the connection socket)
169                 /// </summary>
170                 public int ReceiveBufferSize
171                 {
172                         get {
173                                 return (int)client.GetSocketOption(
174                                         SocketOptionLevel.Socket,
175                                         SocketOptionName.ReceiveBuffer);
176                         }
177                         set {
178                                 client.SetSocketOption(
179                                         SocketOptionLevel.Socket,
180                                         SocketOptionName.ReceiveBuffer, value);
181                         }
182                 }
183                         
184                 /// <summary>
185                 /// How long before the socket will time out on a 
186                 /// Receive() call
187                 /// </summary>
188                 public int ReceiveTimeout
189                 {
190                         get {
191                                 return (int)client.GetSocketOption(
192                                         SocketOptionLevel.Socket,
193                                         SocketOptionName.ReceiveTimeout);
194                         }
195                         set {
196                                 client.SetSocketOption(
197                                         SocketOptionLevel.Socket,
198                                         SocketOptionName.ReceiveTimeout, value);
199                         }
200                 }
201                 
202                 /// <summary>
203                 /// How big the send buffer is (from the connection socket)
204                 /// </summary>
205                 public int SendBufferSize
206                 {
207                         get {
208                                 return (int)client.GetSocketOption(
209                                         SocketOptionLevel.Socket,
210                                         SocketOptionName.SendBuffer);
211                         }
212                         set {
213                                 client.SetSocketOption(
214                                         SocketOptionLevel.Socket,
215                                         SocketOptionName.SendBuffer, value);
216                         }
217                 }
218                 
219                 /// <summary>
220                 /// How long before the socket will time out on a
221                 /// Send() call
222                 /// </summary>
223                 public int SendTimeout
224                 {
225                         get {
226                                 return (int)client.GetSocketOption(
227                                         SocketOptionLevel.Socket,
228                                         SocketOptionName.SendTimeout);
229                         }
230                         set {
231                                 client.SetSocketOption(
232                                         SocketOptionLevel.Socket,
233                                         SocketOptionName.SendTimeout, value);
234                         }
235                 }
236                 
237                 
238                 // methods
239                 
240                 /// <summary>
241                 /// Closes the socket and disposes of all managed resources.
242                 /// 
243                 /// Throws SocketException if something goes wrong while
244                 /// closing the socket.
245                 /// </summary>
246                 public void Close ()
247                 {
248                         ((IDisposable) this).Dispose ();
249                 }
250                 
251                 /// <summary>
252                 /// Connects to a specified remote endpoint
253                 /// 
254                 /// Throws SocketException if something goes wrong while
255                 /// connecting.
256                 /// </summary>
257                 /// <param name="remote_end_point">The aforementioned endpoint</param>
258                 public void Connect (IPEndPoint remote_end_point)
259                 {
260                         try {
261                                 client.Connect(remote_end_point);
262                                 stream = new NetworkStream(client, true);
263                                 active = true;
264                         } finally {
265                                 CheckDisposed ();
266                         }
267                 }
268                 
269                 /// <summary>
270                 /// Connects to an IP address on a port
271                 /// 
272                 /// Throws SocketException if something goes wrong while
273                 /// connecting.
274                 /// </summary>
275                 /// <param name="address">The IP address (get it from Dns.GetHostByName)</param>
276                 /// <param name="port">The port to connect to, e.g. 80 for HTTP</param>
277                 public void Connect (IPAddress address, int port)
278                 {
279                         Connect(new IPEndPoint(address, port));
280                 }
281                 
282                 /// <summary>
283                 /// Resolves a fully qualified domain name to an IP address
284                 /// and connects to it on a specified port
285                 /// 
286                 /// Throws SocketException if something goes wrong while
287                 /// connecting.
288                 /// </summary>
289                 /// <param name="hostname">The hostname, e.g. www.myelin.co.nz</param>
290                 /// <param name="port">The port, e.g. 80 for HTTP</param>
291                 [MonoTODO]
292                 public void Connect (string hostname, int port)
293                 {
294                         CheckDisposed ();
295
296                         IPHostEntry host = Dns.GetHostByName(hostname);
297
298                         for(int i=0; i<host.AddressList.Length; i++)
299                         {\r
300                                 try {\r
301                                         Init(host.AddressList[i].AddressFamily);
302
303                                         if(host.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
304                                                 client.Bind(new IPEndPoint(IPAddress.Any, 0));
305 #if NET_1_1
306                                         else if(host.AddressList[i].AddressFamily == AddressFamily.InterNetworkV6)
307                                                 client.Bind(new IPEndPoint(IPAddress.IPv6Any, 0));
308 #endif
309
310                                         Connect(new IPEndPoint(host.AddressList[i], port));
311                                         break;
312                                 }\r
313                                 catch(Exception e) {\r
314                                         if(client != null) {\r
315                                                 client.Close();\r
316                                                 client = null;\r
317                                         }\r
318 \r
319                                         /// This is the last known address, re-throw the exception\r
320                                         if(i == host.AddressList.Length-1)\r
321                                                 throw e;\r
322                                 }\r
323                         }
324                 }
325                 
326                 /// <summary>
327                 /// Gets rid of all managed resources
328                 /// </summary>
329                 void IDisposable.Dispose ()
330                 {
331                         Dispose (true);
332                         GC.SuppressFinalize (this);
333                 }
334
335                 /// <summary>
336                 /// Gets rid of all unmanaged resources
337                 /// </summary>
338                 /// <param name="disposing">If this is true, it gets rid of all
339                 /// managed resources as well</param>
340                 protected virtual void Dispose (bool disposing)
341                 {
342                         if (disposed)
343                                 return;
344                         disposed = true;
345
346                         if (disposing){
347                                 // release managed resources
348                                 NetworkStream s = stream;
349                                 stream = null;
350                                 if (s != null) {
351                                         // This closes the socket as well, as the NetworkStream
352                                         // owns the socket.
353                                         s.Close();
354                                         active = false;
355                                         s = null;
356                                 } else if (client != null){
357                                         client.Close ();
358                                 }
359                                 client = null;
360                         }
361                 }
362                 
363                 /// <summary>
364                 /// Destructor - just calls Dispose()
365                 /// </summary>
366                 ~TcpClient ()
367                 {
368                         Dispose (false);
369                 }
370                 
371                 /// <returns>A NetworkStream object connected to the
372                 /// connection socket</returns>
373                 public NetworkStream GetStream()
374                 {
375                         try {
376                                 if (stream == null)
377                                 {
378                                         stream = new NetworkStream (client, true);
379                                 }
380                                 return stream;
381                         }
382                         finally { CheckDisposed (); }
383                 }
384                 
385                 private void CheckDisposed ()
386                 {
387                         if (disposed)
388                                 throw new ObjectDisposedException (GetType().FullName);
389                 }               
390         }
391 }