blob: 824c4aa40a5c3cb14cf28bbf30d8a29746f1cb48 [file] [log] [blame]
// $Id$
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
namespace Org.Apache.Etch.Bindings.Csharp.Util
{
/// <summary>
/// Constants and methods related to tcp transport.
/// </summary>
abstract public class TcpTransport : Connection<SessionData>, TransportData
{
/// <summary>
/// Constructs the TcpTransport. Pulls common parameters off the uri.
/// </summary>
/// <param name="uri"></param>
/// <param name="resources"></param>
protected TcpTransport(URL uri, Resources resources)
{
options = new TcpOptions(uri, resources);
}
private readonly TcpOptions options;
protected override void Stop0()
{
try
{
Close(false);
}
catch
{
// ignore
}
base.Stop0();
}
protected Socket socket;
/// <summary>
/// Checks the connection socket for being open.
/// </summary>
/// <returns>the connection socket</returns>
protected Socket CheckSocket()
{
Socket s = socket;
if (s == null)
throw new IOException("socket closed");
return s;
}
public override void Close(bool reset)
{
Socket s = socket;
if (s != null)
{
try
{
try
{
if (reset)
s.LingerState = new LingerOption(false, 0);
else
{
Flush();
ShutdownOutput();
}
}
finally
{
s.Close();
}
}
catch
{
// ignore.
}
finally
{
stream = null;
socket = null;
}
}
}
/// <summary>
/// Sends some data to the remote end. The output data is buffered
/// until the buffer is full or the buffer is flushed.
/// </summary>
/// <param name="buf">the bytes to be sent</param>
/// Exception:
/// throws Exception if there is a problem transmitting the
/// data. Such a problem causes the current connection to be
/// reset.
/// <seealso cref="Flush()"/>
/// <seealso cref="TcpOptions.autoFlush"/>
public void Send(byte[] buf)
{
Send(buf, 0, buf.Length);
}
/// <summary>
/// Sends some data to the remote end. The output data is buffered
/// until the buffer is full or the buffer is flushed.
/// </summary>
/// <param name="buf">the bytes to be sent</param>
/// <param name="off">the offset into buf of the first byte to send</param>
/// <param name="len">the number of bytes to send</param>
public void Send(byte[] buf, int off, int len)
{
try
{
Stream s = checkStream();
s.Write(buf, off, len);
if (options.autoFlush)
{
s.Flush();
}
}
catch (Exception)
{
Close(true);
throw;
}
}
public void Flush()
{
try
{
checkStream().Flush();
}
catch (Exception)
{
Close(true);
throw;
}
}
protected Stream checkStream()
{
Stream ns = stream;
if (ns == null)
throw new IOException("net stream closed");
return ns;
}
private void FireData(FlexBuffer buf)
{
session.SessionData(null, buf);
}
protected Stream stream;
public override EndPoint LocalAddress()
{
return CheckSocket().LocalEndPoint;
}
public override EndPoint RemoteAddress()
{
return CheckSocket().RemoteEndPoint;
}
public void TransportData(Who recipient, FlexBuffer buf)
{
Send(buf.GetBuf(), buf.Index(), buf.Avail());
}
protected override void SetUpSocket()
{
Socket s = CheckSocket();
s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, options.keepAlive);
s.LingerState = new LingerOption(options.lingerTime >= 0, options.lingerTime >= 0 ? options.lingerTime : 0);
s.NoDelay = options.noDelay;
//s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TypeOfService, trafficClass);
stream = new NetworkStream(socket);
// TODO do something about buffering this stream.
}
public void ShutdownInput()
{
CheckSocket().Shutdown(SocketShutdown.Receive);
}
public void ShutdownOutput()
{
CheckSocket().Shutdown(SocketShutdown.Send);
}
abstract protected Socket NewSocket();
abstract protected bool IsServer();
public override object TransportQuery(object query)
{
if (query.Equals(TransportConsts.IS_SERVER))
return IsServer();
return base.TransportQuery(query);
}
[MethodImpl(MethodImplOptions.Synchronized)]
protected override bool OpenSocket(bool reconnect)
{
// if a one time connection from a server socket listener, just
// return the existing socket.
if (!reconnect && socket != null)
return true;
// if a one time connection from a server socket listener, and
// this is a reconnect, then bail.
if (reconnect && IsServer())
return false;
// if a reconnect but no retries allowed, then bail.
if (reconnect && options.reconnectDelay == 0)
return false;
// ok, we don't have an existing socket, and this is either the first
// connection attempt or a reconnect with delay > 0.
bool first = true;
while (IsStarted())
{
// if reconnect is false and first is true, this is our
// very first attempt to connect. otherwise, we are trying
// to reconnect a broken link or establish a link where we
// have already failed at least once.
if (reconnect || !first)
{
if (options.reconnectDelay == 0)
return false;
System.Threading.Monitor.Wait(this, options.reconnectDelay);
if (!IsStarted())
break;
}
// try to open a socket.
try
{
socket = NewSocket();
return true;
}
catch (Exception e)
{
if (first)
{
first = false;
FireException("open", e);
}
}
}
return false;
}
protected override void ReadSocket()
{
Stream ns = checkStream();
FlexBuffer buf = new FlexBuffer(new byte[8192]);
try
{
while (IsStarted())
{
int n = ns.Read(buf.GetBuf(), 0, buf.Length());
if (n <= 0)
break;
buf.SetLength(n);
buf.SetIndex(0);
FireData(buf);
}
}
catch (Exception e)
{
if (e.Message == null)
throw;
if (e.Message.Contains("connection was aborted"))
return;
if (e.Message.Contains("blocking operation"))
return;
if (e.Message.Contains("socket closed"))
return;
if (e.Message.Contains("read operation failed"))
return;
throw;
}
}
}
}