2 // System.Net.Sockets.UdpClient.cs
5 // Gonzalo Paniagua Javier <gonzalo@ximian.com>
6 // Sridhar Kulkarni (sridharkulkarni@gmail.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright (C) Ximian, Inc. http://www.ximian.com
10 // Copyright 2011 Xamarin Inc.
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:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
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.
37 using System
.Threading
.Tasks
;
40 namespace System
.Net
.Sockets
42 public class UdpClient
: IDisposable
44 private bool disposed
= false;
45 private bool active
= false;
46 private Socket socket
;
47 private AddressFamily family
= AddressFamily
.InterNetwork
;
48 private byte[] recvbuffer
;
50 public UdpClient () : this(AddressFamily
.InterNetwork
)
54 public UdpClient(AddressFamily family
)
56 if(family
!= AddressFamily
.InterNetwork
&& family
!= AddressFamily
.InterNetworkV6
)
57 throw new ArgumentException ("Family must be InterNetwork or InterNetworkV6", "family");
63 public UdpClient (int port
)
65 if (port
< IPEndPoint
.MinPort
|| port
> IPEndPoint
.MaxPort
)
66 throw new ArgumentOutOfRangeException ("port");
68 this.family
= AddressFamily
.InterNetwork
;
70 IPEndPoint localEP
= new IPEndPoint (IPAddress
.Any
, port
);
74 public UdpClient (IPEndPoint localEP
)
77 throw new ArgumentNullException ("localEP");
79 this.family
= localEP
.AddressFamily
;
84 public UdpClient (int port
, AddressFamily family
)
86 if (family
!= AddressFamily
.InterNetwork
&& family
!= AddressFamily
.InterNetworkV6
)
87 throw new ArgumentException ("Family must be InterNetwork or InterNetworkV6", "family");
89 if (port
< IPEndPoint
.MinPort
||
90 port
> IPEndPoint
.MaxPort
) {
91 throw new ArgumentOutOfRangeException ("port");
98 if (family
== AddressFamily
.InterNetwork
)
99 localEP
= new IPEndPoint (IPAddress
.Any
, port
);
101 localEP
= new IPEndPoint (IPAddress
.IPv6Any
, port
);
102 InitSocket (localEP
);
105 public UdpClient (string hostname
, int port
)
107 if (hostname
== null)
108 throw new ArgumentNullException ("hostname");
110 if (port
< IPEndPoint
.MinPort
|| port
> IPEndPoint
.MaxPort
)
111 throw new ArgumentOutOfRangeException ("port");
114 Connect (hostname
, port
);
117 private void InitSocket (EndPoint localEP
)
124 socket
= new Socket (family
, SocketType
.Dgram
, ProtocolType
.Udp
);
127 socket
.Bind (localEP
);
133 ((IDisposable
) this).Dispose ();
138 void DoConnect (IPEndPoint endPoint
)
140 /* Catch EACCES and turn on SO_BROADCAST then,
141 * as UDP sockets don't have it set by default
144 socket
.Connect (endPoint
);
145 } catch (SocketException ex
) {
146 if (ex
.ErrorCode
== (int)SocketError
.AccessDenied
) {
147 socket
.SetSocketOption (SocketOptionLevel
.Socket
, SocketOptionName
.Broadcast
, 1);
149 socket
.Connect (endPoint
);
156 public void Connect (IPEndPoint endPoint
)
159 if (endPoint
== null)
160 throw new ArgumentNullException ("endPoint");
162 DoConnect (endPoint
);
166 public void Connect (IPAddress addr
, int port
)
169 throw new ArgumentNullException ("addr");
171 if (port
< IPEndPoint
.MinPort
|| port
> IPEndPoint
.MaxPort
)
172 throw new ArgumentOutOfRangeException ("port");
175 Connect (new IPEndPoint (addr
, port
));
178 public void Connect (string hostname
, int port
)
180 if (port
< IPEndPoint
.MinPort
|| port
> IPEndPoint
.MaxPort
)
181 throw new ArgumentOutOfRangeException ("port");
183 IPAddress
[] addresses
= Dns
.GetHostAddresses (hostname
);
184 for(int i
=0; i
<addresses
.Length
; i
++) {
186 this.family
= addresses
[i
].AddressFamily
;
187 Connect (new IPEndPoint (addresses
[i
], port
));
189 } catch(Exception e
) {
190 if(i
== addresses
.Length
- 1){
195 /// This is the last entry, re-throw the exception
202 #region Multicast methods
203 public void DropMulticastGroup (IPAddress multicastAddr
)
206 if (multicastAddr
== null)
207 throw new ArgumentNullException ("multicastAddr");
209 if(family
== AddressFamily
.InterNetwork
)
210 socket
.SetSocketOption (SocketOptionLevel
.IP
, SocketOptionName
.DropMembership
,
211 new MulticastOption (multicastAddr
));
213 socket
.SetSocketOption (SocketOptionLevel
.IPv6
, SocketOptionName
.DropMembership
,
214 new IPv6MulticastOption (multicastAddr
));
217 public void DropMulticastGroup (IPAddress multicastAddr
,
222 /* LAMESPEC: exceptions haven't been specified
225 if (multicastAddr
== null) {
226 throw new ArgumentNullException ("multicastAddr");
229 /* Does this overload only apply to IPv6?
230 * Only the IPv6MulticastOption has an
231 * ifindex-using constructor. The MS docs
234 if (family
== AddressFamily
.InterNetworkV6
) {
235 socket
.SetSocketOption (SocketOptionLevel
.IPv6
, SocketOptionName
.DropMembership
, new IPv6MulticastOption (multicastAddr
, ifindex
));
239 public void JoinMulticastGroup (IPAddress multicastAddr
)
243 if (multicastAddr
== null)
244 throw new ArgumentNullException ("multicastAddr");
246 if(family
== AddressFamily
.InterNetwork
)
247 socket
.SetSocketOption (SocketOptionLevel
.IP
, SocketOptionName
.AddMembership
,
248 new MulticastOption (multicastAddr
));
250 socket
.SetSocketOption (SocketOptionLevel
.IPv6
, SocketOptionName
.AddMembership
,
251 new IPv6MulticastOption (multicastAddr
));
254 public void JoinMulticastGroup (int ifindex
,
255 IPAddress multicastAddr
)
259 if (multicastAddr
== null)
260 throw new ArgumentNullException ("multicastAddr");
262 if (family
== AddressFamily
.InterNetworkV6
)
263 socket
.SetSocketOption (SocketOptionLevel
.IPv6
, SocketOptionName
.AddMembership
, new IPv6MulticastOption (multicastAddr
, ifindex
));
265 throw new SocketException ((int) SocketError
.OperationNotSupported
);
268 public void JoinMulticastGroup (IPAddress multicastAddr
, int timeToLive
)
271 if (multicastAddr
== null)
272 throw new ArgumentNullException ("multicastAddr");
273 if (timeToLive
< 0 || timeToLive
> 255)
274 throw new ArgumentOutOfRangeException ("timeToLive");
276 JoinMulticastGroup (multicastAddr
);
277 if(family
== AddressFamily
.InterNetwork
)
278 socket
.SetSocketOption (SocketOptionLevel
.IP
, SocketOptionName
.MulticastTimeToLive
,
281 socket
.SetSocketOption (SocketOptionLevel
.IPv6
, SocketOptionName
.MulticastTimeToLive
,
285 public void JoinMulticastGroup (IPAddress multicastAddr
,
286 IPAddress localAddress
)
290 if (family
== AddressFamily
.InterNetwork
)
291 socket
.SetSocketOption (SocketOptionLevel
.IP
, SocketOptionName
.AddMembership
, new MulticastOption (multicastAddr
, localAddress
));
293 throw new SocketException ((int) SocketError
.OperationNotSupported
);
298 public byte [] Receive (ref IPEndPoint remoteEP
)
302 byte [] recBuffer
= new byte [65536]; // Max. size
303 EndPoint endPoint
= new IPEndPoint (IPAddress
.Any
, 0);
304 int dataRead
= socket
.ReceiveFrom (recBuffer
, ref endPoint
);
305 if (dataRead
< recBuffer
.Length
)
306 recBuffer
= CutArray (recBuffer
, dataRead
);
308 remoteEP
= (IPEndPoint
) endPoint
;
312 int DoSend (byte[] dgram
, int bytes
, IPEndPoint endPoint
)
314 /* Catch EACCES and turn on SO_BROADCAST then,
315 * as UDP sockets don't have it set by default
318 if (endPoint
== null) {
319 return(socket
.Send (dgram
, 0, bytes
,
322 return(socket
.SendTo (dgram
, 0, bytes
,
326 } catch (SocketException ex
) {
327 if (ex
.ErrorCode
== (int)SocketError
.AccessDenied
) {
328 socket
.SetSocketOption (SocketOptionLevel
.Socket
, SocketOptionName
.Broadcast
, 1);
329 if (endPoint
== null) {
330 return(socket
.Send (dgram
, 0, bytes
, SocketFlags
.None
));
332 return(socket
.SendTo (dgram
, 0, bytes
, SocketFlags
.None
, endPoint
));
340 public int Send (byte [] dgram
, int bytes
)
344 throw new ArgumentNullException ("dgram");
347 throw new InvalidOperationException ("Operation not allowed on " +
348 "non-connected sockets.");
350 return(DoSend (dgram
, bytes
, null));
353 public int Send (byte [] dgram
, int bytes
, IPEndPoint endPoint
)
357 throw new ArgumentNullException ("dgram is null");
360 if (endPoint
!= null)
361 throw new InvalidOperationException ("Cannot send packets to an " +
362 "arbitrary host while connected.");
364 return(DoSend (dgram
, bytes
, null));
367 return(DoSend (dgram
, bytes
, endPoint
));
370 public int Send (byte [] dgram
, int bytes
, string hostname
, int port
)
372 return Send (dgram
, bytes
,
373 new IPEndPoint (Dns
.GetHostAddresses (hostname
) [0], port
));
376 private byte [] CutArray (byte [] orig
, int length
)
378 byte [] newArray
= new byte [length
];
379 Buffer
.BlockCopy (orig
, 0, newArray
, 0, length
);
385 IAsyncResult
DoBeginSend (byte[] datagram
, int bytes
,
387 AsyncCallback requestCallback
,
390 /* Catch EACCES and turn on SO_BROADCAST then,
391 * as UDP sockets don't have it set by default
394 if (endPoint
== null) {
395 return(socket
.BeginSend (datagram
, 0, bytes
, SocketFlags
.None
, requestCallback
, state
));
397 return(socket
.BeginSendTo (datagram
, 0, bytes
, SocketFlags
.None
, endPoint
, requestCallback
, state
));
399 } catch (SocketException ex
) {
400 if (ex
.ErrorCode
== (int)SocketError
.AccessDenied
) {
401 socket
.SetSocketOption (SocketOptionLevel
.Socket
, SocketOptionName
.Broadcast
, 1);
402 if (endPoint
== null) {
403 return(socket
.BeginSend (datagram
, 0, bytes
, SocketFlags
.None
, requestCallback
, state
));
405 return(socket
.BeginSendTo (datagram
, 0, bytes
, SocketFlags
.None
, endPoint
, requestCallback
, state
));
413 public IAsyncResult
BeginSend (byte[] datagram
, int bytes
,
414 AsyncCallback requestCallback
,
417 return(BeginSend (datagram
, bytes
, null,
418 requestCallback
, state
));
421 public IAsyncResult
BeginSend (byte[] datagram
, int bytes
,
423 AsyncCallback requestCallback
,
428 if (datagram
== null) {
429 throw new ArgumentNullException ("datagram");
432 return(DoBeginSend (datagram
, bytes
, endPoint
,
433 requestCallback
, state
));
436 public IAsyncResult
BeginSend (byte[] datagram
, int bytes
,
437 string hostname
, int port
,
438 AsyncCallback requestCallback
,
441 return(BeginSend (datagram
, bytes
, new IPEndPoint (Dns
.GetHostAddresses (hostname
) [0], port
), requestCallback
, state
));
444 public int EndSend (IAsyncResult asyncResult
)
448 if (asyncResult
== null) {
449 throw new ArgumentNullException ("asyncResult is a null reference");
452 return(socket
.EndSend (asyncResult
));
455 public IAsyncResult
BeginReceive (AsyncCallback requestCallback
, object state
)
459 recvbuffer
= new byte[8192];
463 if (family
== AddressFamily
.InterNetwork
) {
464 ep
= new IPEndPoint (IPAddress
.Any
, 0);
466 ep
= new IPEndPoint (IPAddress
.IPv6Any
, 0);
469 return(socket
.BeginReceiveFrom (recvbuffer
, 0, 8192,
472 requestCallback
, state
));
475 public byte[] EndReceive (IAsyncResult asyncResult
, ref IPEndPoint remoteEP
)
479 if (asyncResult
== null) {
480 throw new ArgumentNullException ("asyncResult is a null reference");
485 if (family
== AddressFamily
.InterNetwork
) {
486 ep
= new IPEndPoint (IPAddress
.Any
, 0);
488 ep
= new IPEndPoint (IPAddress
.IPv6Any
, 0);
491 int bytes
= socket
.EndReceiveFrom (asyncResult
,
493 remoteEP
= (IPEndPoint
)ep
;
495 /* Need to copy into a new array here, because
496 * otherwise the returned array length is not
499 byte[] buf
= new byte[bytes
];
500 Array
.Copy (recvbuffer
, buf
, bytes
);
506 protected bool Active
{
507 get { return active; }
508 set { active = value; }
511 public Socket Client
{
512 get { return socket; }
513 set { socket = value; }
519 return(socket
.Available
);
523 public bool DontFragment
526 return(socket
.DontFragment
);
529 socket
.DontFragment
= value;
533 public bool EnableBroadcast
536 return(socket
.EnableBroadcast
);
539 socket
.EnableBroadcast
= value;
543 public bool ExclusiveAddressUse
546 return(socket
.ExclusiveAddressUse
);
549 socket
.ExclusiveAddressUse
= value;
553 public bool MulticastLoopback
556 return(socket
.MulticastLoopback
);
559 socket
.MulticastLoopback
= value;
575 void IDisposable
.Dispose ()
578 GC
.SuppressFinalize (this);
581 protected virtual void Dispose (bool disposing
)
600 private void CheckDisposed ()
603 throw new ObjectDisposedException (GetType().FullName
);
609 public Task
<UdpReceiveResult
> ReceiveAsync ()
611 return Task
<UdpReceiveResult
>.Factory
.FromAsync (BeginReceive
, r
=> {
612 IPEndPoint remoteEndPoint
= null;
613 return new UdpReceiveResult (EndReceive (r
, ref remoteEndPoint
), remoteEndPoint
);
617 public Task
<int> SendAsync (byte[] datagram
, int bytes
)
619 return Task
<int>.Factory
.FromAsync (BeginSend
, EndSend
, datagram
, bytes
, null);
622 public Task
<int> SendAsync (byte[] datagram
, int bytes
, IPEndPoint endPoint
)
624 return Task
<int>.Factory
.FromAsync (BeginSend
, EndSend
, datagram
, bytes
, endPoint
, null);
627 public Task
<int> SendAsync (byte[] datagram
, int bytes
, string hostname
, int port
)
629 var t
= Tuple
.Create (datagram
, bytes
, hostname
, port
, this);
631 return Task
<int>.Factory
.FromAsync ((callback
, state
) => {
632 var d
= (Tuple
<byte[], int, string, int, UdpClient
>) state
;
633 return d
.Item5
.BeginSend (d
.Item1
, d
.Item2
, d
.Item3
, d
.Item4
, callback
, null);