Files
noisyfox e364457b1b Fix a memory leak.
Caused by not call SocketAsyncEventArgs.Dispose().
2016-09-03 15:17:16 +08:00

133 lines
3.7 KiB
C#

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Shadowsocks.Util
{
public static class SocketUtil
{
private class DnsEndPoint2 : DnsEndPoint
{
public DnsEndPoint2(string host, int port) : base(host, port)
{
}
public DnsEndPoint2(string host, int port, AddressFamily addressFamily) : base(host, port, addressFamily)
{
}
public override string ToString()
{
return this.Host + ":" + this.Port;
}
}
public static EndPoint GetEndPoint(string host, int port)
{
IPAddress ipAddress;
bool parsed = IPAddress.TryParse(host, out ipAddress);
if (parsed)
{
return new IPEndPoint(ipAddress, port);
}
// maybe is a domain name
return new DnsEndPoint2(host, port);
}
private class AutoReleaseAsyncResult : IAsyncResult
{
public bool IsCompleted { get; } = true;
public WaitHandle AsyncWaitHandle { get; } = null;
public object AsyncState { get; set; }
public bool CompletedSynchronously { get; } = true;
public TcpUserToken UserToken { get; set; }
~AutoReleaseAsyncResult()
{
UserToken.Dispose();
}
}
private class TcpUserToken
{
public AsyncCallback Callback { get; private set; }
public SocketAsyncEventArgs Args { get; private set; }
public object AsyncState { get; private set; }
public TcpUserToken(AsyncCallback callback, object state, SocketAsyncEventArgs args)
{
Callback = callback;
AsyncState = state;
Args = args;
}
public void Dispose()
{
Args?.Dispose();
Callback = null;
Args = null;
AsyncState = null;
}
}
private static void OnTcpConnectCompleted(object sender, SocketAsyncEventArgs args)
{
args.Completed -= OnTcpConnectCompleted;
TcpUserToken token = (TcpUserToken) args.UserToken;
AutoReleaseAsyncResult r = new AutoReleaseAsyncResult
{
AsyncState = token.AsyncState,
UserToken = token
};
token.Callback(r);
}
public static void BeginConnectTcp(EndPoint endPoint, AsyncCallback callback, object state)
{
var arg = new SocketAsyncEventArgs();
arg.RemoteEndPoint = endPoint;
arg.Completed += OnTcpConnectCompleted;
arg.UserToken = new TcpUserToken(callback, state, arg);
Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, arg);
}
public static Socket EndConnectTcp(IAsyncResult asyncResult)
{
var r = asyncResult as AutoReleaseAsyncResult;
if (r == null)
{
throw new ArgumentException("Invalid asyncResult.", nameof(asyncResult));
}
var tut = r.UserToken;
var arg = tut.Args;
if (arg.SocketError != SocketError.Success)
{
if (arg.ConnectByNameError != null)
{
throw arg.ConnectByNameError;
}
var ex = new SocketException((int)arg.SocketError);
throw ex;
}
var so = tut.Args.ConnectSocket;
so.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
return so;
}
}
}