You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
265 lines
8.5 KiB
265 lines
8.5 KiB
15 years ago
|
// Copyright (c) 2004-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
|
||
|
//
|
||
|
// This program is free software; you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU General Public License version 2 as published by
|
||
|
// the Free Software Foundation
|
||
|
//
|
||
|
// There are special exceptions to the terms and conditions of the GPL
|
||
|
// as it is applied to this software. View the full text of the
|
||
|
// exception in file EXCEPTIONS in the directory of this software
|
||
|
// distribution.
|
||
|
//
|
||
|
// This program is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU General Public License
|
||
|
// along with this program; if not, write to the Free Software
|
||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
|
||
|
using System;
|
||
|
using System.IO;
|
||
|
using System.Net;
|
||
|
using System.Net.Sockets;
|
||
|
using System.Reflection;
|
||
|
using System.Diagnostics;
|
||
|
using MySql.Data.MySqlClient.Properties;
|
||
|
using System.Runtime.InteropServices;
|
||
|
|
||
|
namespace MySql.Data.Common
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Summary description for StreamCreator.
|
||
|
/// </summary>
|
||
|
internal class StreamCreator
|
||
|
{
|
||
|
string hostList;
|
||
|
uint port;
|
||
|
string pipeName;
|
||
|
uint timeOut;
|
||
|
uint keepalive;
|
||
|
|
||
|
public StreamCreator(string hosts, uint port, string pipeName, uint keepalive)
|
||
|
{
|
||
|
hostList = hosts;
|
||
|
if (hostList == null || hostList.Length == 0)
|
||
|
hostList = "localhost";
|
||
|
this.port = port;
|
||
|
this.pipeName = pipeName;
|
||
|
this.keepalive = keepalive;
|
||
|
}
|
||
|
|
||
|
public Stream GetStream(uint timeout)
|
||
|
{
|
||
|
timeOut = timeout;
|
||
|
|
||
|
if (hostList.StartsWith("/", StringComparison.OrdinalIgnoreCase))
|
||
|
return CreateSocketStream(null, true);
|
||
|
|
||
|
string[] dnsHosts = hostList.Split('&');
|
||
|
|
||
|
Random random = new Random((int)DateTime.Now.Ticks);
|
||
|
int index = random.Next(dnsHosts.Length);
|
||
|
int pos = 0;
|
||
|
bool usePipe = (pipeName != null && pipeName.Length != 0);
|
||
|
Stream stream = null;
|
||
|
|
||
|
while (pos < dnsHosts.Length)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
if (usePipe)
|
||
|
{
|
||
|
#if !CF
|
||
|
stream = NamedPipeStream.Create(pipeName, dnsHosts[index], timeout);
|
||
|
#endif
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IPHostEntry ipHE = GetHostEntry(dnsHosts[index]);
|
||
|
foreach (IPAddress address in ipHE.AddressList)
|
||
|
{
|
||
|
// MySQL doesn't currently support IPv6 addresses
|
||
|
if (address.AddressFamily == AddressFamily.InterNetworkV6)
|
||
|
continue;
|
||
|
stream = CreateSocketStream(address, false);
|
||
|
if (stream != null) break;
|
||
|
}
|
||
|
}
|
||
|
if (stream != null)
|
||
|
break;
|
||
|
index++;
|
||
|
if (index == dnsHosts.Length)
|
||
|
index = 0;
|
||
|
pos++;
|
||
|
}
|
||
|
catch (Exception)
|
||
|
{
|
||
|
// if on last host then throw
|
||
|
if (pos >= dnsHosts.Length - 1) throw;
|
||
|
// else continue
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return stream;
|
||
|
}
|
||
|
|
||
|
private IPHostEntry ParseIPAddress(string hostname)
|
||
|
{
|
||
|
IPHostEntry ipHE = null;
|
||
|
#if !CF
|
||
|
IPAddress addr;
|
||
|
if (IPAddress.TryParse(hostname, out addr))
|
||
|
{
|
||
|
ipHE = new IPHostEntry();
|
||
|
ipHE.AddressList = new IPAddress[1];
|
||
|
ipHE.AddressList[0] = addr;
|
||
|
}
|
||
|
#endif
|
||
|
return ipHE;
|
||
|
}
|
||
|
|
||
|
#if CF
|
||
|
IPHostEntry GetDnsHostEntry(string hostname)
|
||
|
{
|
||
|
return Dns.GetHostEntry(hostname);
|
||
|
}
|
||
|
#else
|
||
|
IPHostEntry GetDnsHostEntry(string hostname)
|
||
|
{
|
||
|
LowResolutionStopwatch stopwatch = new LowResolutionStopwatch();
|
||
|
|
||
|
try
|
||
|
{
|
||
|
stopwatch.Start();
|
||
|
return Dns.GetHostEntry(hostname);
|
||
|
}
|
||
|
catch (SocketException ex)
|
||
|
{
|
||
|
string message = String.Format(Resources.GetHostEntryFailed,
|
||
|
stopwatch.Elapsed, hostname, ex.SocketErrorCode,
|
||
|
ex.ErrorCode, ex.NativeErrorCode);
|
||
|
throw new Exception(message, ex);
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
stopwatch.Stop();
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
private IPHostEntry GetHostEntry(string hostname)
|
||
|
{
|
||
|
IPHostEntry ipHE = ParseIPAddress(hostname);
|
||
|
if (ipHE != null) return ipHE;
|
||
|
return GetDnsHostEntry(hostname);
|
||
|
}
|
||
|
|
||
|
#if !CF
|
||
|
|
||
|
private static EndPoint CreateUnixEndPoint(string host)
|
||
|
{
|
||
|
// first we need to load the Mono.posix assembly
|
||
|
Assembly a = Assembly.Load("Mono.Posix");
|
||
|
|
||
|
// then we need to construct a UnixEndPoint object
|
||
|
EndPoint ep = (EndPoint)a.CreateInstance("Mono.Posix.UnixEndPoint",
|
||
|
false, BindingFlags.CreateInstance, null,
|
||
|
new object[1] { host }, null, null);
|
||
|
return ep;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
private Stream CreateSocketStream(IPAddress ip, bool unix)
|
||
|
{
|
||
|
EndPoint endPoint;
|
||
|
#if !CF
|
||
|
if (!Platform.IsWindows() && unix)
|
||
|
endPoint = CreateUnixEndPoint(hostList);
|
||
|
else
|
||
|
#endif
|
||
|
endPoint = new IPEndPoint(ip, (int)port);
|
||
|
|
||
|
Socket socket = unix ?
|
||
|
new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP) :
|
||
|
new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||
|
if (keepalive > 0)
|
||
|
{
|
||
|
SetKeepAlive(socket, keepalive);
|
||
|
}
|
||
|
IAsyncResult ias = socket.BeginConnect(endPoint, null, null);
|
||
|
if (!ias.AsyncWaitHandle.WaitOne((int)timeOut * 1000, false))
|
||
|
{
|
||
|
socket.Close();
|
||
|
return null;
|
||
|
}
|
||
|
try
|
||
|
{
|
||
|
socket.EndConnect(ias);
|
||
|
}
|
||
|
catch (Exception)
|
||
|
{
|
||
|
socket.Close();
|
||
|
throw;
|
||
|
}
|
||
|
MyNetworkStream stream = new MyNetworkStream(socket, true);
|
||
|
GC.SuppressFinalize(socket);
|
||
|
GC.SuppressFinalize(stream);
|
||
|
return stream;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/// <summary>
|
||
|
/// Set keepalive + timeout on socket.
|
||
|
/// </summary>
|
||
|
/// <param name="s">socket</param>
|
||
|
/// <param name="time">keepalive timeout, in seconds</param>
|
||
|
private static void SetKeepAlive(Socket s, uint time)
|
||
|
{
|
||
|
|
||
|
#if !CF
|
||
|
uint on = 1;
|
||
|
uint interval = 1000; // default interval = 1 sec
|
||
|
|
||
|
uint timeMilliseconds;
|
||
|
if (time > UInt32.MaxValue / 1000)
|
||
|
timeMilliseconds = UInt32.MaxValue;
|
||
|
else
|
||
|
timeMilliseconds = time * 1000;
|
||
|
|
||
|
// Use Socket.IOControl to implement equivalent of
|
||
|
// WSAIoctl with SOL_KEEPALIVE_VALS
|
||
|
|
||
|
// the native structure passed to WSAIoctl is
|
||
|
//struct tcp_keepalive {
|
||
|
// ULONG onoff;
|
||
|
// ULONG keepalivetime;
|
||
|
// ULONG keepaliveinterval;
|
||
|
//};
|
||
|
// marshal the equivalent of the native structure into a byte array
|
||
|
|
||
|
byte[] inOptionValues = new byte[12];
|
||
|
BitConverter.GetBytes(on).CopyTo(inOptionValues, 0);
|
||
|
BitConverter.GetBytes(time).CopyTo(inOptionValues, 4);
|
||
|
BitConverter.GetBytes(interval).CopyTo(inOptionValues, 8);
|
||
|
try
|
||
|
{
|
||
|
// call WSAIoctl via IOControl
|
||
|
s.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
|
||
|
return;
|
||
|
}
|
||
|
catch (NotImplementedException)
|
||
|
{
|
||
|
// Mono throws not implemented currently
|
||
|
}
|
||
|
#endif
|
||
|
// Fallback if Socket.IOControl is not available ( Compact Framework )
|
||
|
// or not implemented ( Mono ). Keepalive option will still be set, but
|
||
|
// with timeout is kept default.
|
||
|
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
|
||
|
}
|
||
|
}
|
||
|
}
|