You've already forked linux-packaging-mono
Imported Upstream version 4.0.0~alpha1
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
This commit is contained in:
478
external/referencesource/System.ServiceModel.Channels/System/ServiceModel/Channels/UdpSocket.cs
vendored
Normal file
478
external/referencesource/System.ServiceModel.Channels/System/ServiceModel/Channels/UdpSocket.cs
vendored
Normal file
@ -0,0 +1,478 @@
|
||||
//----------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime;
|
||||
using System.Threading;
|
||||
#if DEBUG
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
class UdpSocket
|
||||
{
|
||||
int openCount;
|
||||
int timeToLive;
|
||||
int pendingReceiveCount;
|
||||
Socket socket;
|
||||
#if DEBUG
|
||||
StackTrace disposeStack;
|
||||
#endif
|
||||
|
||||
public UdpSocket(Socket socket, int interfaceIndex)
|
||||
{
|
||||
Fx.Assert(socket != null, "Socket argument cannot be null");
|
||||
|
||||
this.openCount = 0;
|
||||
this.pendingReceiveCount = 0;
|
||||
|
||||
this.ThisLock = new object();
|
||||
|
||||
this.socket = socket;
|
||||
this.InterfaceIndex = interfaceIndex;
|
||||
this.timeToLive = socket.Ttl;
|
||||
}
|
||||
|
||||
enum TransferDirection : byte
|
||||
{
|
||||
Send,
|
||||
Receive,
|
||||
}
|
||||
|
||||
public AddressFamily AddressFamily
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.socket.AddressFamily;
|
||||
}
|
||||
}
|
||||
|
||||
public int PendingReceiveCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.pendingReceiveCount;
|
||||
}
|
||||
}
|
||||
|
||||
//value of UdpConstants.Defaults.InterfaceIndex (-1) if not a multicast socket
|
||||
internal int InterfaceIndex
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
internal bool IsDisposed
|
||||
{ get { return this.openCount < 0; } }
|
||||
|
||||
internal object ThisLock
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
//must be called under a lock in receive loop
|
||||
public IAsyncResult BeginReceiveFrom(byte[] buffer, int offset, int size, ref EndPoint remoteEndPoint, AsyncCallback callback, object state)
|
||||
{
|
||||
UdpUtility.ValidateBufferBounds(buffer, offset, size);
|
||||
|
||||
ThrowIfNotOpen();
|
||||
|
||||
bool success = false;
|
||||
IAsyncResult asyncResult = null;
|
||||
try
|
||||
{
|
||||
this.pendingReceiveCount++;
|
||||
|
||||
asyncResult = new ReceiveFromAsyncResult(
|
||||
this.socket,
|
||||
new ArraySegment<byte>(buffer, offset, size),
|
||||
remoteEndPoint,
|
||||
size - offset,
|
||||
this.timeToLive,
|
||||
callback,
|
||||
state);
|
||||
success = true;
|
||||
|
||||
return asyncResult;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
this.pendingReceiveCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
bool cleanup = false;
|
||||
lock (this.ThisLock)
|
||||
{
|
||||
if (this.IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//UdpUtility.CreateListenSocketsOnUniquePort can create a socket and then close it without ever calling
|
||||
//UdpSocket.Open() if it fails to bind on both IPv4 and IPv6. If this happens, then openCount will still be at zero.
|
||||
if (this.openCount > 0)
|
||||
{
|
||||
this.openCount--;
|
||||
}
|
||||
|
||||
if (this.openCount == 0)
|
||||
{
|
||||
cleanup = true;
|
||||
this.openCount = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (cleanup)
|
||||
{
|
||||
#if DEBUG
|
||||
if (!Fx.FastDebug)
|
||||
{
|
||||
disposeStack = new StackTrace();
|
||||
}
|
||||
#endif
|
||||
//non-zero sendTimeout causes the socket to block on a receive while looking for an EOF, which will never come
|
||||
this.socket.Close(0);
|
||||
}
|
||||
}
|
||||
|
||||
//must be called under a lock in receive loop
|
||||
public ArraySegment<byte> EndReceiveFrom(IAsyncResult result, ref EndPoint remoteEndPoint)
|
||||
{
|
||||
this.pendingReceiveCount--;
|
||||
return ReceiveFromAsyncResult.End(result, ref remoteEndPoint);
|
||||
}
|
||||
|
||||
internal EndPoint CreateIPAnyEndPoint()
|
||||
{
|
||||
if (this.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
return new IPEndPoint(IPAddress.Any, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new IPEndPoint(IPAddress.IPv6Any, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
lock (this.ThisLock)
|
||||
{
|
||||
if (this.IsDisposed)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new ObjectDisposedException("UdpSocket"));
|
||||
}
|
||||
this.openCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public int SendTo(byte[] buffer, int offset, int size, EndPoint remoteEndPoint)
|
||||
{
|
||||
ThrowIfNotOpen();
|
||||
UdpUtility.ValidateBufferBounds(buffer, offset, size);
|
||||
|
||||
try
|
||||
{
|
||||
int count = this.socket.SendTo(buffer, offset, size, SocketFlags.None, remoteEndPoint);
|
||||
Fx.Assert(count == size, "Bytes sent on the wire should be the same as the bytes specified");
|
||||
|
||||
return count;
|
||||
}
|
||||
catch (SocketException socketException)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(ConvertNetworkError(socketException, size - offset, TransferDirection.Send, this.timeToLive));
|
||||
}
|
||||
}
|
||||
|
||||
public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, EndPoint remoteEndPoint, AsyncCallback callback, object state)
|
||||
{
|
||||
ThrowIfNotOpen();
|
||||
UdpUtility.ValidateBufferBounds(buffer, offset, size);
|
||||
|
||||
return new SendToAsyncResult(this.socket, buffer, offset, size, remoteEndPoint, this.timeToLive, callback, state);
|
||||
}
|
||||
|
||||
public int EndSendTo(IAsyncResult result)
|
||||
{
|
||||
return SendToAsyncResult.End(result);
|
||||
}
|
||||
|
||||
static Exception ConvertNetworkError(SocketException socketException, ReceiveFromAsyncResult result)
|
||||
{
|
||||
return ConvertNetworkError(socketException, result.MessageSize, TransferDirection.Receive, result.TimeToLive);
|
||||
}
|
||||
|
||||
// size: sending => the size of the data being sent
|
||||
// Receiving => the max message size we can receive
|
||||
// remoteEndPoint: remote endpoint reported when error occured
|
||||
static Exception ConvertNetworkError(SocketException socketException, int size, TransferDirection direction, int timeToLive)
|
||||
{
|
||||
Exception result = null;
|
||||
|
||||
if (socketException.ErrorCode == UnsafeNativeMethods.ERROR_INVALID_HANDLE)
|
||||
{
|
||||
//This would likely indicate a bug in our ref-counting
|
||||
//for instance, a channel is closing the socket multiple times...
|
||||
Fx.Assert("The socket appears to have been closed unexpectedly. This probably indicates incorrect ref counting (i.e. a channel is closing the socket multiple times)");
|
||||
result = new CommunicationObjectAbortedException(socketException.Message, socketException);
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMessage;
|
||||
switch (socketException.SocketErrorCode)
|
||||
{
|
||||
case SocketError.MessageSize: //10040
|
||||
errorMessage = (direction == TransferDirection.Send ? SR.UdpMaxMessageSendSizeExceeded(size) : SR.MaxReceivedMessageSizeExceeded(size));
|
||||
Exception inner = new QuotaExceededException(errorMessage, socketException);
|
||||
result = new ProtocolException(errorMessage, inner);
|
||||
break;
|
||||
case SocketError.NetworkReset: //10052
|
||||
//ICMP: Time Exceeded (TTL expired)
|
||||
//see http://tools.ietf.org/html/rfc792
|
||||
result = new CommunicationException(SR.IcmpTimeExpired(timeToLive), socketException);
|
||||
break;
|
||||
case SocketError.ConnectionReset: //10054
|
||||
//ICMP: Destination Unreachable (target host/port/etc not reachable)
|
||||
//see http://tools.ietf.org/html/rfc792
|
||||
result = new CommunicationException(SR.IcmpDestinationUnreachable, socketException);
|
||||
break;
|
||||
default:
|
||||
errorMessage = (direction == TransferDirection.Send ? SR.UdpSendException : SR.UdpReceiveException);
|
||||
result = new CommunicationException(errorMessage, socketException);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Fx.Assert(result != null, "we should never return null");
|
||||
return result;
|
||||
}
|
||||
|
||||
void ThrowIfDisposed()
|
||||
{
|
||||
if (this.IsDisposed)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new ObjectDisposedException(this.GetType().ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
void ThrowIfNotOpen()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (this.openCount == 0)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ObjectNotOpen));
|
||||
}
|
||||
}
|
||||
|
||||
class SendToAsyncResult : TypedAsyncResult<int>
|
||||
{
|
||||
Socket socket;
|
||||
int offset;
|
||||
int size;
|
||||
int timeToLive;
|
||||
|
||||
static AsyncCallback onSendToComplete = Fx.ThunkCallback(OnSendToComplete);
|
||||
|
||||
public SendToAsyncResult(Socket socket, byte[] buffer, int offset, int size, EndPoint remoteEndPoint, int timeToLive, AsyncCallback callback, object state)
|
||||
: base(callback, state)
|
||||
{
|
||||
this.socket = socket;
|
||||
this.offset = offset;
|
||||
this.size = size;
|
||||
this.timeToLive = timeToLive;
|
||||
int count = 0;
|
||||
|
||||
try
|
||||
{
|
||||
IAsyncResult socketAsyncResult = this.socket.BeginSendTo(buffer, offset, size, SocketFlags.None, remoteEndPoint, onSendToComplete, this);
|
||||
|
||||
if (!socketAsyncResult.CompletedSynchronously)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
count = this.socket.EndSendTo(socketAsyncResult);
|
||||
}
|
||||
catch (SocketException socketException)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(ConvertNetworkError(socketException, this.size - this.offset, TransferDirection.Send, this.timeToLive));
|
||||
}
|
||||
|
||||
this.Complete(count, true);
|
||||
}
|
||||
|
||||
static void OnSendToComplete(IAsyncResult result)
|
||||
{
|
||||
if (result.CompletedSynchronously)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendToAsyncResult thisPtr = (SendToAsyncResult)result.AsyncState;
|
||||
Exception completionException = null;
|
||||
int count = 0;
|
||||
|
||||
try
|
||||
{
|
||||
count = thisPtr.socket.EndSendTo(result);
|
||||
}
|
||||
catch (SocketException socketException)
|
||||
{
|
||||
completionException = ConvertNetworkError(socketException, thisPtr.size - thisPtr.offset, TransferDirection.Send, thisPtr.timeToLive);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (Fx.IsFatal(ex))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
completionException = ex;
|
||||
}
|
||||
|
||||
if (completionException != null)
|
||||
{
|
||||
thisPtr.Complete(false, completionException);
|
||||
}
|
||||
else
|
||||
{
|
||||
thisPtr.Complete(count, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ReceiveFromAsyncResult : TypedAsyncResult<ArraySegment<byte>>
|
||||
{
|
||||
static AsyncCallback onReceiveMessageFromCallback = Fx.ThunkCallback(new AsyncCallback(OnReceiveMessageFrom));
|
||||
Socket socket;
|
||||
|
||||
public ReceiveFromAsyncResult(Socket socket, ArraySegment<byte> buffer, EndPoint remoteEndPoint, int messageSize, int timeToLive, AsyncCallback userCallback, object userState) :
|
||||
base(userCallback, userState)
|
||||
{
|
||||
this.RemoteEndPoint = remoteEndPoint;
|
||||
this.MessageSize = messageSize;
|
||||
this.socket = socket;
|
||||
this.Buffer = buffer;
|
||||
this.TimeToLive = timeToLive;
|
||||
|
||||
ArraySegment<byte> data = default(ArraySegment<byte>);
|
||||
|
||||
try
|
||||
{
|
||||
IAsyncResult socketAsyncResult = this.socket.BeginReceiveFrom(this.Buffer.Array,
|
||||
this.Buffer.Offset,
|
||||
this.Buffer.Count,
|
||||
SocketFlags.None,
|
||||
ref remoteEndPoint,
|
||||
onReceiveMessageFromCallback,
|
||||
this);
|
||||
|
||||
if (!socketAsyncResult.CompletedSynchronously)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
data = EndReceiveFrom(socketAsyncResult);
|
||||
}
|
||||
catch (SocketException socketException)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(UdpSocket.ConvertNetworkError(socketException, this));
|
||||
}
|
||||
|
||||
Complete(data, true);
|
||||
}
|
||||
|
||||
public EndPoint RemoteEndPoint
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public int TimeToLive
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
//used when generating error messages for the user...
|
||||
internal int MessageSize
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
ArraySegment<byte> Buffer
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public static ArraySegment<byte> End(IAsyncResult result, ref EndPoint remoteEndPoint)
|
||||
{
|
||||
ArraySegment<byte> data = TypedAsyncResult<ArraySegment<byte>>.End(result);
|
||||
ReceiveFromAsyncResult receiveFromResult = (ReceiveFromAsyncResult)result;
|
||||
remoteEndPoint = receiveFromResult.RemoteEndPoint;
|
||||
return data;
|
||||
}
|
||||
|
||||
static void OnReceiveMessageFrom(IAsyncResult result)
|
||||
{
|
||||
if (result.CompletedSynchronously)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReceiveFromAsyncResult asyncResult = (ReceiveFromAsyncResult)result.AsyncState;
|
||||
|
||||
Exception completionException = null;
|
||||
ArraySegment<byte> data = default(ArraySegment<byte>);
|
||||
|
||||
try
|
||||
{
|
||||
data = asyncResult.EndReceiveFrom(result);
|
||||
}
|
||||
catch (SocketException socketException)
|
||||
{
|
||||
completionException = UdpSocket.ConvertNetworkError(socketException, asyncResult);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (Fx.IsFatal(exception))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
completionException = exception;
|
||||
}
|
||||
|
||||
if (completionException != null)
|
||||
{
|
||||
asyncResult.Complete(false, completionException);
|
||||
}
|
||||
else
|
||||
{
|
||||
asyncResult.Complete(data, false);
|
||||
}
|
||||
}
|
||||
|
||||
ArraySegment<byte> EndReceiveFrom(IAsyncResult result)
|
||||
{
|
||||
EndPoint remoteEndPoint = this.RemoteEndPoint;
|
||||
int count = this.socket.EndReceiveFrom(result, ref remoteEndPoint);
|
||||
this.RemoteEndPoint = remoteEndPoint;
|
||||
return new ArraySegment<byte>(this.Buffer.Array, this.Buffer.Offset, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user