2009-06-12 Bill Holmes <billholmes54@gmail.com>
[mono.git] / mcs / class / System / System.Net.Sockets / UdpClient.cs
1 //
2 // System.Net.Sockets.UdpClient.cs
3 //
4 // Author:
5 //    Gonzalo Paniagua Javier <gonzalo@ximian.com>
6 //    Sridhar Kulkarni (sridharkulkarni@gmail.com)
7 //
8 // Copyright (C) Ximian, Inc. http://www.ximian.com
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Net;
34
35 namespace System.Net.Sockets
36 {
37         public class UdpClient : IDisposable
38         {
39                 private bool disposed = false;
40                 private bool active = false;
41                 private Socket socket;
42                 private AddressFamily family = AddressFamily.InterNetwork;
43 #if NET_2_0
44                 private byte[] recvbuffer;
45 #endif
46         
47 #region Constructors
48                 public UdpClient () : this(AddressFamily.InterNetwork)
49                 {
50                 }
51
52 #if NET_1_1
53                 public UdpClient(AddressFamily family)
54                 {
55                         if(family != AddressFamily.InterNetwork && family != AddressFamily.InterNetworkV6)
56                                 throw new ArgumentException("Family must be InterNetwork or InterNetworkV6", "family");
57
58                         this.family = family;
59                         InitSocket (null);
60                 }
61 #endif
62
63                 public UdpClient (int port)
64                 {
65                         if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
66                                 throw new ArgumentOutOfRangeException ("port");
67
68                         this.family = AddressFamily.InterNetwork;
69
70                         IPEndPoint localEP = new IPEndPoint (IPAddress.Any, port);
71                         InitSocket (localEP);
72                 }
73
74                 public UdpClient (IPEndPoint localEP)
75                 {
76                         if (localEP == null)
77                                 throw new ArgumentNullException ("localEP");
78
79                         this.family = localEP.AddressFamily;
80
81                         InitSocket (localEP);
82                 }
83
84 #if NET_1_1
85                 public UdpClient (int port, AddressFamily family)
86                 {
87                         if (family != AddressFamily.InterNetwork &&
88                             family != AddressFamily.InterNetworkV6) {
89                                 throw new ArgumentException ("Family must be InterNetwork or InterNetworkV6", "family");
90                         }
91                         
92                         if (port < IPEndPoint.MinPort ||
93                             port > IPEndPoint.MaxPort) {
94                                 throw new ArgumentOutOfRangeException ("port");
95                         }
96                         
97                         this.family = family;
98
99                         IPEndPoint localEP = new IPEndPoint (IPAddress.Any, port);
100                         InitSocket (localEP);
101                 }
102 #endif
103                 
104                 public UdpClient (string hostname, int port)
105                 {
106                         if (hostname == null)
107                                 throw new ArgumentNullException ("hostname");
108
109                         if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
110                                 throw new ArgumentOutOfRangeException ("port");
111
112                         InitSocket (null);
113                         Connect (hostname, port);
114                 }
115
116                 private void InitSocket (EndPoint localEP)
117                 {
118                         if(socket != null) {
119                                 socket.Close();
120                                 socket = null;
121                         }
122
123                         socket = new Socket (family, SocketType.Dgram, ProtocolType.Udp);
124
125                         if (localEP != null)
126                                 socket.Bind (localEP);
127                 }
128
129 #endregion // Constructors
130 #region Public methods
131 #region Close
132                 public void Close ()
133                 {
134                         ((IDisposable) this).Dispose ();        
135                 }
136 #endregion
137 #region Connect
138
139                 void DoConnect (IPEndPoint endPoint)
140                 {
141                         /* Catch EACCES and turn on SO_BROADCAST then,
142                          * as UDP sockets don't have it set by default
143                          */
144                         try {
145                                 socket.Connect (endPoint);
146                         } catch (SocketException ex) {
147                                 if (ex.ErrorCode == (int)SocketError.AccessDenied) {
148                                         socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
149                                         
150                                         socket.Connect (endPoint);
151                                 } else {
152                                         throw;
153                                 }
154                         }
155                 }
156                 
157                 public void Connect (IPEndPoint endPoint)
158                 {
159                         CheckDisposed ();
160                         if (endPoint == null)
161                                 throw new ArgumentNullException ("endPoint");
162
163                         DoConnect (endPoint);
164                         active = true;
165                 }
166
167                 public void Connect (IPAddress addr, int port)
168                 {
169                         if (addr == null)
170                                 throw new ArgumentNullException ("addr");
171
172                         if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
173                                 throw new ArgumentOutOfRangeException ("port");
174
175
176                         Connect (new IPEndPoint (addr, port));
177                 }
178
179                 public void Connect (string hostname, int port)
180                 {
181                         if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
182                                 throw new ArgumentOutOfRangeException ("port");
183
184                         IPAddress[] addresses = Dns.GetHostAddresses (hostname);
185                         for(int i=0; i<addresses.Length; i++) {
186                                 try {
187                                         this.family = addresses[i].AddressFamily;
188                                         Connect (new IPEndPoint (addresses[i], port));
189                                         break;
190                                 } catch(Exception e) {
191                                         if(i == addresses.Length - 1){
192                                                 if(socket != null) {
193                                                         socket.Close();
194                                                         socket = null;
195                                                 }
196                                                 /// This is the last entry, re-throw the exception
197                                                 throw e;
198                                         }
199                                 }
200                         }
201                 }
202 #endregion
203                 #region Multicast methods
204                 public void DropMulticastGroup (IPAddress multicastAddr)
205                 {
206                         CheckDisposed ();
207                         if (multicastAddr == null)
208                                 throw new ArgumentNullException ("multicastAddr");
209
210                         if(family == AddressFamily.InterNetwork)
211                                 socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.DropMembership,
212                                         new MulticastOption (multicastAddr));
213 #if NET_1_1
214                         else
215                                 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.DropMembership,
216                                         new IPv6MulticastOption (multicastAddr));
217 #endif
218                 }
219
220 #if NET_1_1
221                 public void DropMulticastGroup (IPAddress multicastAddr,
222                                                 int ifindex)
223                 {
224                         CheckDisposed ();
225
226                         /* LAMESPEC: exceptions haven't been specified
227                          * for this overload.
228                          */
229                         if (multicastAddr == null) {
230                                 throw new ArgumentNullException ("multicastAddr");
231                         }
232
233                         /* Does this overload only apply to IPv6?
234                          * Only the IPv6MulticastOption has an
235                          * ifindex-using constructor.  The MS docs
236                          * don't say.
237                          */
238                         if (family == AddressFamily.InterNetworkV6) {
239                                 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.DropMembership, new IPv6MulticastOption (multicastAddr, ifindex));
240                         }
241                 }
242 #endif
243                 
244                 public void JoinMulticastGroup (IPAddress multicastAddr)
245                 {
246                         CheckDisposed ();
247
248                         if(family == AddressFamily.InterNetwork)
249                                 socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.AddMembership,
250                                         new MulticastOption (multicastAddr));
251 #if NET_1_1
252                         else
253                                 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.AddMembership,
254                                         new IPv6MulticastOption (multicastAddr));
255 #endif
256                 }
257
258 #if NET_1_1
259                 public void JoinMulticastGroup (int ifindex,
260                                                 IPAddress multicastAddr)
261                 {
262                         CheckDisposed ();
263
264                         /* Does this overload only apply to IPv6?
265                          * Only the IPv6MulticastOption has an
266                          * ifindex-using constructor.  The MS docs
267                          * don't say.
268                          */
269                         if (family == AddressFamily.InterNetworkV6) {
270                                 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption (multicastAddr, ifindex));
271                         }
272                 }
273 #endif
274                 
275                 public void JoinMulticastGroup (IPAddress multicastAddr, int timeToLive)
276                 {
277                         CheckDisposed ();
278                         JoinMulticastGroup (multicastAddr);
279                         if (timeToLive < 0 || timeToLive > 255)
280                                 throw new ArgumentOutOfRangeException ("timeToLive");
281
282                         if(family == AddressFamily.InterNetwork)
283                                 socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive,
284                                         timeToLive);
285 #if NET_1_1
286                         else
287                                 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.MulticastTimeToLive,
288                                         timeToLive);
289 #endif
290                 }
291
292 #if NET_2_0
293                 public void JoinMulticastGroup (IPAddress multicastAddr,
294                                                 IPAddress localAddress)
295                 {
296                         CheckDisposed ();
297
298                         if (family == AddressFamily.InterNetwork) {
299                                 socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption (multicastAddr, localAddress));
300                         } else if (family == AddressFamily.InterNetworkV6) {
301                                 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new MulticastOption (multicastAddr, localAddress));
302                         }
303                 }
304 #endif
305                 
306                 #endregion
307 #region Data I/O
308                 public byte [] Receive (ref IPEndPoint remoteEP)
309                 {
310                         CheckDisposed ();
311
312                         byte [] recBuffer = new byte [65536]; // Max. size
313                         EndPoint endPoint = new IPEndPoint (IPAddress.Any, 0);
314                         int dataRead = socket.ReceiveFrom (recBuffer, ref endPoint);
315                         if (dataRead < recBuffer.Length)
316                                 recBuffer = CutArray (recBuffer, dataRead);
317
318                         remoteEP = (IPEndPoint) endPoint;
319                         return recBuffer;
320                 }
321
322                 int DoSend (byte[] dgram, int bytes, IPEndPoint endPoint)
323                 {
324                         /* Catch EACCES and turn on SO_BROADCAST then,
325                          * as UDP sockets don't have it set by default
326                          */
327                         try {
328                                 if (endPoint == null) {
329                                         return(socket.Send (dgram, 0, bytes,
330                                                             SocketFlags.None));
331                                 } else {
332                                         return(socket.SendTo (dgram, 0, bytes,
333                                                               SocketFlags.None,
334                                                               endPoint));
335                                 }
336                         } catch (SocketException ex) {
337                                 if (ex.ErrorCode == (int)SocketError.AccessDenied) {
338                                         socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
339                                         if (endPoint == null) {
340                                                 return(socket.Send (dgram, 0, bytes, SocketFlags.None));
341                                         } else {
342                                                 return(socket.SendTo (dgram, 0, bytes, SocketFlags.None, endPoint));
343                                         }
344                                 } else {
345                                         throw;
346                                 }
347                         }
348                 }
349                 
350                 public int Send (byte [] dgram, int bytes)
351                 {
352                         CheckDisposed ();
353                         if (dgram == null)
354                                 throw new ArgumentNullException ("dgram");
355
356                         if (!active)
357                                 throw new InvalidOperationException ("Operation not allowed on " + 
358                                                                      "non-connected sockets.");
359
360                         return(DoSend (dgram, bytes, null));
361                 }
362
363                 public int Send (byte [] dgram, int bytes, IPEndPoint endPoint)
364                 {
365                         CheckDisposed ();
366                         if (dgram == null)
367                                 throw new ArgumentNullException ("dgram is null");
368                         
369                         if (active) {
370                                 if (endPoint != null)
371                                         throw new InvalidOperationException ("Cannot send packets to an " +
372                                                                              "arbitrary host while connected.");
373
374                                 return(DoSend (dgram, bytes, null));
375                         }
376
377                         return(DoSend (dgram, bytes, endPoint));
378                 }
379
380                 public int Send (byte [] dgram, int bytes, string hostname, int port)
381                 {
382                         return Send (dgram, bytes, 
383                                      new IPEndPoint (Dns.GetHostAddresses (hostname) [0], port));
384                 }
385
386                 private byte [] CutArray (byte [] orig, int length)
387                 {
388                         byte [] newArray = new byte [length];
389                         Buffer.BlockCopy (orig, 0, newArray, 0, length);
390
391                         return newArray;
392                 }
393 #endregion
394
395 #if NET_2_0
396                 IAsyncResult DoBeginSend (byte[] datagram, int bytes,
397                                           IPEndPoint endPoint,
398                                           AsyncCallback requestCallback,
399                                           object state)
400                 {
401                         /* Catch EACCES and turn on SO_BROADCAST then,
402                          * as UDP sockets don't have it set by default
403                          */
404                         try {
405                                 if (endPoint == null) {
406                                         return(socket.BeginSend (datagram, 0, bytes, SocketFlags.None, requestCallback, state));
407                                 } else {
408                                         return(socket.BeginSendTo (datagram, 0, bytes, SocketFlags.None, endPoint, requestCallback, state));
409                                 }
410                         } catch (SocketException ex) {
411                                 if (ex.ErrorCode == (int)SocketError.AccessDenied) {
412                                         socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
413                                         if (endPoint == null) {
414                                                 return(socket.BeginSend (datagram, 0, bytes, SocketFlags.None, requestCallback, state));
415                                         } else {
416                                                 return(socket.BeginSendTo (datagram, 0, bytes, SocketFlags.None, endPoint, requestCallback, state));
417                                         }
418                                 } else {
419                                         throw;
420                                 }
421                         }
422                 }
423                 
424                 public IAsyncResult BeginSend (byte[] datagram, int bytes,
425                                                AsyncCallback requestCallback,
426                                                object state)
427                 {
428                         return(BeginSend (datagram, bytes, null,
429                                           requestCallback, state));
430                 }
431                 
432                 public IAsyncResult BeginSend (byte[] datagram, int bytes,
433                                                IPEndPoint endPoint,
434                                                AsyncCallback requestCallback,
435                                                object state)
436                 {
437                         CheckDisposed ();
438
439                         if (datagram == null) {
440                                 throw new ArgumentNullException ("datagram");
441                         }
442                         
443                         return(DoBeginSend (datagram, bytes, endPoint,
444                                             requestCallback, state));
445                 }
446                 
447                 public IAsyncResult BeginSend (byte[] datagram, int bytes,
448                                                string hostname, int port,
449                                                AsyncCallback requestCallback,
450                                                object state)
451                 {
452                         return(BeginSend (datagram, bytes, new IPEndPoint (Dns.GetHostAddresses (hostname) [0], port), requestCallback, state));
453                 }
454                 
455                 public int EndSend (IAsyncResult asyncResult)
456                 {
457                         CheckDisposed ();
458                         
459                         if (asyncResult == null) {
460                                 throw new ArgumentNullException ("asyncResult is a null reference");
461                         }
462                         
463                         return(socket.EndSend (asyncResult));
464                 }
465                 
466                 public IAsyncResult BeginReceive (AsyncCallback callback,
467                                                   object state)
468                 {
469                         CheckDisposed ();
470
471                         recvbuffer = new byte[8192];
472                         
473                         EndPoint ep;
474                         
475                         if (family == AddressFamily.InterNetwork) {
476                                 ep = new IPEndPoint (IPAddress.Any, 0);
477                         } else {
478                                 ep = new IPEndPoint (IPAddress.IPv6Any, 0);
479                         }
480                         
481                         return(socket.BeginReceiveFrom (recvbuffer, 0, 8192,
482                                                         SocketFlags.None,
483                                                         ref ep,
484                                                         callback, state));
485                 }
486                 
487                 public byte[] EndReceive (IAsyncResult asyncResult,
488                                           ref IPEndPoint remoteEP)
489                 {
490                         CheckDisposed ();
491                         
492                         if (asyncResult == null) {
493                                 throw new ArgumentNullException ("asyncResult is a null reference");
494                         }
495                         
496                         EndPoint ep;
497                         
498                         if (family == AddressFamily.InterNetwork) {
499                                 ep = new IPEndPoint (IPAddress.Any, 0);
500                         } else {
501                                 ep = new IPEndPoint (IPAddress.IPv6Any, 0);
502                         }
503                         
504                         int bytes = socket.EndReceiveFrom (asyncResult,
505                                                            ref ep);
506                         remoteEP = (IPEndPoint)ep;
507
508                         /* Need to copy into a new array here, because
509                          * otherwise the returned array length is not
510                          * 'bytes'
511                          */
512                         byte[] buf = new byte[bytes];
513                         Array.Copy (recvbuffer, buf, bytes);
514                         
515                         return(buf);
516                 }
517 #endif
518                                 
519 #region Properties
520                 protected bool Active {
521                         get { return active; }
522                         set { active = value; }
523                 }
524
525 #if NET_2_0
526                 public
527 #else
528                 protected
529 #endif
530                 Socket Client {
531                         get { return socket; }
532                         set { socket = value; }
533                 }
534
535 #if NET_2_0
536                 public int Available
537                 {
538                         get {
539                                 return(socket.Available);
540                         }
541                 }
542                 
543 #if TARGET_JVM
544                 [MonoNotSupported ("Not supported as Socket.DontFragment is not supported")]
545 #endif
546                 public bool DontFragment
547                 {
548                         get {
549                                 return(socket.DontFragment);
550                         }
551                         set {
552                                 socket.DontFragment = value;
553                         }
554                 }
555
556 #if TARGET_JVM
557                 [MonoNotSupported ("Not supported as Socket.EnableBroadcast is not supported")]
558 #endif
559                 public bool EnableBroadcast
560                 {
561                         get {
562                                 return(socket.EnableBroadcast);
563                         }
564                         set {
565                                 socket.EnableBroadcast = value;
566                         }
567                 }
568                 
569 #if TARGET_JVM
570                 [MonoNotSupported ("Not supported as Socket.ExclusiveAddressUse is not supported")]
571 #endif
572                 public bool ExclusiveAddressUse
573                 {
574                         get {
575                                 return(socket.ExclusiveAddressUse);
576                         }
577                         set {
578                                 socket.ExclusiveAddressUse = value;
579                         }
580                 }
581                 
582 #if TARGET_JVM
583                 [MonoNotSupported ("Not supported as Socket.MulticastLoopback is not supported")]
584 #endif
585                 public bool MulticastLoopback
586                 {
587                         get {
588                                 return(socket.MulticastLoopback);
589                         }
590                         set {
591                                 socket.MulticastLoopback = value;
592                         }
593                 }
594                 
595 #if TARGET_JVM
596                 [MonoNotSupported ("Not supported as Socket.Ttl is not supported")]
597 #endif
598                 public short Ttl
599                 {
600                         get {
601                                 return(socket.Ttl);
602                         }
603                         set {
604                                 socket.Ttl = value;
605                         }
606                 }
607 #endif
608
609 #endregion
610 #region Disposing
611                 void IDisposable.Dispose ()
612                 {
613                         Dispose (true);
614                         GC.SuppressFinalize (this);
615                 }
616
617 #if NET_2_0
618                 protected virtual
619 #endif
620                 void Dispose (bool disposing)
621                 {
622                         if (disposed)
623                                 return;
624                         disposed = true;
625
626                         if (disposing){
627                                 if (socket != null)
628                                         socket.Close ();
629
630                                 socket = null;
631                         }
632                 }
633                 
634                 ~UdpClient ()
635                 {
636                         Dispose (false);
637                 }
638                 
639                 private void CheckDisposed ()
640                 {
641                         if (disposed)
642                                 throw new ObjectDisposedException (GetType().FullName);
643                 }               
644 #endregion
645 #endregion
646         }
647 }
648