using Cosmos.Sys.Network.TCPIP.TCP;
using System;
namespace Cosmos.Sys.Network
{
///
/// Represents an established TCP connection between this host and another
///
public class TcpClient
{
internal TCPConnection connection;
internal ClientDataReceived dataCallback;
internal ClientDisconnected disconnectCallback;
internal UInt16 mss = 1360;
protected static UInt16 NextLocalPort = 33000;
public TcpClient(IPv4Address dest, UInt16 port)
{
IPv4Address source = TCPIPStack.FindNetwork(dest);
if( source == null )
{
throw new ArgumentException("Destination host unreachable", "dest");
}
this.connection = new TCPConnection(dest, port, source, NextLocalPort++, 0x5656, TCPConnection.State.SYN_SENT);
this.connection.client = this;
TCPIPStack.tcpSockets.Add(connection);
TCPPacket packet = new TCPPacket(connection, connection.LocalSequenceNumber, 0, 0x02, 8192);
TCPIP.IPv4OutgoingBuffer.AddPacket(packet);
}
internal TcpClient(TCPConnection connection)
{
this.connection = connection;
this.connection.client = this;
}
///
/// Data Received callback function
///
public ClientDataReceived DataReceived
{
get { return this.dataCallback; }
set { this.dataCallback = value; }
}
///
/// Data Received callback function
///
public ClientDisconnected Disconnect
{
get { return this.disconnectCallback; }
set { this.disconnectCallback = value; }
}
///
/// Send a string to the remote host
///
/// String to be sent
public void SendString(string data)
{
byte[] dataBuffer = new byte[data.Length];
for (int b = 0; b < data.Length; b++)
{
dataBuffer[b] = (byte)data[b];
}
this.SendData(dataBuffer);
}
///
/// Send a raw byte buffer to the remote host
///
/// Byte buffer with data to send
public void SendData(byte[] data)
{
if (data.Length < mss)
{
TCPPacket packet = new TCPPacket(connection.LocalIP, connection.RemoteIP, connection.LocalPort, connection.RemotePort,
connection.LocalSequenceNumber, connection.RemoteSequenceNumber, 0x18, 8192, data);
TCPIP.IPv4OutgoingBuffer.AddPacket(packet);
connection.LocalSequenceNumber += (uint)data.Length;
return;
}
int remaining_bytes = data.Length;
int data_idx = 0;
byte[] new_buffer;
byte tcp_flags = 0x10;
while (remaining_bytes > 0)
{
if (remaining_bytes > mss)
{
new_buffer = new byte[mss];
}
else
{
new_buffer = new byte[remaining_bytes];
tcp_flags = 0x18;
}
for (int b = 0; b < new_buffer.Length; b++)
{
new_buffer[b] = data[data_idx];
data_idx++;
remaining_bytes--;
}
TCPPacket packet = new TCPPacket(connection.LocalIP, connection.RemoteIP, connection.LocalPort, connection.RemotePort,
connection.LocalSequenceNumber, connection.RemoteSequenceNumber, tcp_flags, 8192, new_buffer);
TCPIP.IPv4OutgoingBuffer.AddPacket(packet);
connection.LocalSequenceNumber += (uint)new_buffer.Length;
}
}
///
/// Close down an active connection
///
public void Close()
{
TCPPacket packet = new TCPPacket(connection, connection.LocalSequenceNumber, connection.RemoteSequenceNumber + 1, 0x11, 8192);
TCPIP.IPv4OutgoingBuffer.AddPacket(packet);
connection.ConnectionState = TCPConnection.State.TIMEWAIT;
}
internal void dataReceived(byte[] data)
{
if (this.dataCallback != null)
{
this.dataCallback(this, data);
}
}
///
/// Remote IP Endpoint
///
public IPv4EndPoint RemoteEndpoint
{
get { return this.connection.RemoteEndpoint; }
}
///
/// Local IP Endpoint
///
public IPv4EndPoint LocalEndpoint
{
get { return this.connection.LocalEndpoint; }
}
///
/// Returns true if the current connection is active
///
public bool Connected
{
get { return (this.connection.ConnectionState == TCPConnection.State.ESTABLISHED); }
}
internal void disconnect()
{
if (this.disconnectCallback != null)
{
this.disconnectCallback(this);
}
}
}
}