XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
namespace XFESystem.Network;

/// <summary>
/// Bounded active-open TCP byte stream. One outstanding outgoing segment,
/// immediate cumulative ACKs, 64 KiB receive window, no SACK/window scaling.
/// Out-of-order data is ACKed and retransmitted by the peer, never fabricated.
/// </summary>
internal sealed class TcpConnection : Stream
{
    readonly Ipv4Network network;
    readonly uint destination;
    readonly ushort sourcePort, destinationPort;
    readonly byte[] received = new byte[65536], pending = new byte[1460];
    uint sendUnacknowledged, sendNext, receiveNext;
    uint sendWindowSequence, sendWindowAcknowledgement;
    int receiveHead, receiveCount, peerMss = 536;
    ushort peerWindow = 65535;
    bool established, remoteFin, disposed;
    string? failure;
    int readTimeout, writeTimeout;
    internal TcpConnection(Ipv4Network network, uint destination, ushort sourcePort, ushort destinationPort, uint initialSequence, int timeout)
    {
        this.network = network; this.destination = destination; this.sourcePort = sourcePort; this.destinationPort = destinationPort;
        sendUnacknowledged = initialSequence; sendNext = unchecked(initialSequence + 1); readTimeout = writeTimeout = timeout;
    }
    static bool Before(uint first, uint second) => unchecked((int)(first - second)) < 0;
    ushort Window => (ushort)Math.Min(65535, received.Length - receiveCount);
    void CheckOpen()
    {
        ObjectDisposedException.ThrowIf(disposed, this);
        if (failure != null) throw new IOException(failure);
    }
    internal void Connect(long deadline)
    {
        long nextSend = 0; int attempts = 0;
        while (!established)
        {
            CheckOpen(); Ipv4Network.CheckTimeout(network.Now, deadline, "TCP connect");
            if (network.Now >= nextSend)
            {
                if (attempts++ >= 7) throw new IOException("TCP connect exceeded retransmission limit.");
                network.SendTcp(destination, sourcePort, destinationPort, sendUnacknowledged, 0, 2, Window, default, deadline, syn: true);
                nextSend = network.Now + Math.Min(2000, 300 << Math.Min(attempts - 1, 3));
            }
            network.Poll(deadline); if (!established) network.Wait();
        }
        network.Trace("XFE_NETWORK_TCP|Address|" + NetworkPackets.Format(destination) + "|Port|" + destinationPort + "|Established|1\n");
    }
    void Ack(long deadline) => network.SendTcp(destination, sourcePort, destinationPort, sendNext, receiveNext, 0x10, Window, default, deadline);
    internal void Receive(uint source, ReadOnlySpan<byte> segment, long deadline)
    {
        if (disposed || source != destination || NetworkPackets.Read16(segment, 0) != destinationPort ||
            NetworkPackets.Read16(segment, 2) != sourcePort) return;
        int header = (segment[12] >> 4) * 4;
        if (header < 20 || header > segment.Length || (segment[12] & 15) != 0) return;
        byte flags = segment[13]; uint sequence = NetworkPackets.Read32(segment, 4), acknowledgement = NetworkPackets.Read32(segment, 8);
        if (!established)
        {
            // A refusal must acknowledge the actual SYN, not a guessed tuple.
            if ((flags & 0x14) == 0x14 && acknowledgement == sendNext) { failure = "TCP connection refused."; return; }
            if ((flags & 0x16) != 0x12 || acknowledgement != sendNext) return;
            for (int at = 20; at < header;)
            {
                int kind = segment[at]; if (kind == 0) break;
                if (kind == 1) { at++; continue; }
                if (at + 2 > header || segment[at + 1] < 2 || at + segment[at + 1] > header) return;
                if (kind == 2 && segment[at + 1] == 4)
                { int mss = NetworkPackets.Read16(segment, at + 2); if (mss < 64) return; peerMss = Math.Min(1460, mss); }
                at += segment[at + 1];
            }
            receiveNext = unchecked(sequence + 1); sendUnacknowledged = acknowledgement;
            peerWindow = NetworkPackets.Read16(segment, 14); sendWindowSequence = sequence; sendWindowAcknowledgement = acknowledgement;
            established = true; Ack(deadline); return;
        }
        ReadOnlySpan<byte> data = segment[header..];
        // RFC 5961 exact-sequence RST acceptance; other RSTs only elicit ACK.
        if ((flags & 4) != 0)
        { if (sequence == receiveNext) failure = "TCP peer reset the connection."; else Ack(deadline); return; }
        if ((flags & 0x12) != 0x10) { Ack(deadline); return; }
        // RFC 9293 receive acceptability precedes all send-side state changes.
        // A same-tuple ACK with a sequence outside our actual receive window
        // cannot acknowledge bytes or replace the peer's advertised window.
        uint segmentLength = (uint)data.Length + ((flags & 1) != 0 ? 1u : 0u);
        uint window = Window;
        bool firstInWindow = unchecked(sequence - receiveNext) < window;
        bool lastInWindow = segmentLength != 0 && unchecked(sequence + segmentLength - 1 - receiveNext) < window;
        bool acceptable = segmentLength == 0 ? (window == 0 ? sequence == receiveNext : firstInWindow) : window != 0 && (firstInWindow || lastInWindow);
        if (!acceptable) { Ack(deadline); return; }
        if (Before(sendNext, acknowledgement)) { Ack(deadline); return; }
        if (!Before(acknowledgement, sendUnacknowledged))
        {
            if (Before(sendUnacknowledged, acknowledgement)) sendUnacknowledged = acknowledgement;
            // SND.WL1/SND.WL2 prevent an older acceptable packet from undoing a
            // later window update. Stale ACKs do not update the window either.
            if (Before(sendWindowSequence, sequence) || sendWindowSequence == sequence && !Before(acknowledgement, sendWindowAcknowledgement))
            {
                peerWindow = NetworkPackets.Read16(segment, 14);
                sendWindowSequence = sequence; sendWindowAcknowledgement = acknowledgement;
            }
        }
        if (remoteFin) { if (data.Length != 0 || (flags & 1) != 0) Ack(deadline); return; }
        if (Before(receiveNext, sequence)) { Ack(deadline); return; }
        uint skipped = unchecked(receiveNext - sequence);
        if (skipped > (uint)data.Length) { if (data.Length != 0 || (flags & 1) != 0) Ack(deadline); return; }
        data = data[(int)skipped..];
        if (data.Length > received.Length - receiveCount) { Ack(deadline); return; }
        for (int i = 0; i < data.Length; i++) received[(receiveHead + receiveCount + i) & 65535] = data[i];
        receiveCount += data.Length; receiveNext = unchecked(receiveNext + (uint)data.Length);
        bool fin = (flags & 1) != 0;
        if (fin) { receiveNext = unchecked(receiveNext + 1); remoteFin = true; }
        if (data.Length != 0 || fin) Ack(deadline);
    }
    public override int Read(byte[] buffer, int offset, int count)
    { ArgumentNullException.ThrowIfNull(buffer); return Read(buffer.AsSpan(offset, count)); }
    public override int Read(Span<byte> buffer)
    {
        CheckOpen(); if (buffer.Length == 0) return 0;
        long deadline = network.Deadline(readTimeout);
        while (receiveCount == 0)
        {
            CheckOpen(); if (remoteFin) return 0;
            Ipv4Network.CheckTimeout(network.Now, deadline, "TCP read"); network.Poll(deadline);
            if (receiveCount == 0 && !remoteFin) network.Wait();
        }
        int count = Math.Min(buffer.Length, receiveCount); bool reopenWindow = receiveCount > 32768;
        for (int i = 0; i < count; i++) buffer[i] = received[(receiveHead + i) & 65535];
        receiveHead = (receiveHead + count) & 65535; receiveCount -= count;
        if (reopenWindow) Ack(deadline);
        return count;
    }
    public override void Write(byte[] buffer, int offset, int count)
    { ArgumentNullException.ThrowIfNull(buffer); Write(buffer.AsSpan(offset, count)); }
    public override void Write(ReadOnlySpan<byte> buffer)
    {
        CheckOpen(); long deadline = network.Deadline(writeTimeout);
        while (buffer.Length != 0)
        {
            if (remoteFin) throw new IOException("TCP peer has closed the receive stream.");
            while (peerWindow == 0)
            { CheckOpen(); Ipv4Network.CheckTimeout(network.Now, deadline, "TCP window"); network.Poll(deadline); network.Wait(); }
            int count = Math.Min(buffer.Length, Math.Min(peerMss, peerWindow)); buffer[..count].CopyTo(pending);
            uint sequence = sendNext; sendNext = unchecked(sendNext + (uint)count);
            long nextSend = 0; int attempts = 0;
            while (Before(sendUnacknowledged, sendNext))
            {
                CheckOpen(); Ipv4Network.CheckTimeout(network.Now, deadline, "TCP write");
                if (network.Now >= nextSend)
                {
                    if (attempts++ >= 7) throw new IOException("TCP data exceeded retransmission limit.");
                    network.SendTcp(destination, sourcePort, destinationPort, sequence, receiveNext, 0x18, Window, pending.AsSpan(0, count), deadline);
                    nextSend = network.Now + Math.Min(2000, 300 << Math.Min(attempts - 1, 3));
                }
                network.Poll(deadline); if (Before(sendUnacknowledged, sendNext)) network.Wait();
            }
            buffer = buffer[count..];
        }
    }
    internal void Abort(long deadline)
    {
        if (disposed) return;
        try { if (established && network.Now < deadline) network.SendTcp(destination, sourcePort, destinationPort, sendNext, receiveNext, 0x14, 0, default, deadline); }
        catch (IOException) { }
        finally { disposed = true; network.Released(this); }
    }
    protected override void Dispose(bool disposing)
    {
        if (!disposed)
        {
            try
            {
                if (established && failure == null)
                {
                    // Close owns one budget, including TX completion and ACK
                    // polling. It never adds a second hidden 500 ms TX timeout.
                    long deadline = network.Now + Math.Min(250, Math.Min(readTimeout, writeTimeout));
                    uint finSequence = sendNext; sendNext = unchecked(sendNext + 1);
                    network.SendTcp(destination, sourcePort, destinationPort, finSequence, receiveNext, 0x11, Window, default, deadline);
                    while (Before(sendUnacknowledged, sendNext) && failure == null && network.Now < deadline)
                    { network.Poll(deadline); network.Wait(); }
                }
            }
            catch (IOException) { }
            finally { disposed = true; network.Released(this); }
        }
        base.Dispose(disposing);
    }
    public override bool CanRead => !disposed;
    public override bool CanWrite => !disposed;
    public override bool CanSeek => false;
    public override bool CanTimeout => true;
    public override int ReadTimeout { get => readTimeout; set { if (value is < 1 or > 120000) throw new ArgumentOutOfRangeException(nameof(value)); readTimeout = value; } }
    public override int WriteTimeout { get => writeTimeout; set { if (value is < 1 or > 120000) throw new ArgumentOutOfRangeException(nameof(value)); writeTimeout = value; } }
    public override long Length => throw new NotSupportedException();
    public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
    public override void Flush() => CheckOpen();
    public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
    public override void SetLength(long value) => throw new NotSupportedException();
}