如有可能,请引用TCP/IP规范中的具体段落。
TCP必须从因互联网通信系统而受损、丢失、重复或无序传递的数据中恢复。这通过为每个传输的八位字节分配一个序列号并要求接收TCP发出肯定确认(ACK)来实现。如果在超时时间内未收到ACK,则重新传输数据。在接收端,序列号用于正确排序可能乱序接收的段并消除重复。通过向每个传输的段添加校验和、在接收端进行检查并丢弃受损的段来处理受损。
-- RFC 793 - 传输控制协议,1.5部分
然而,如果它们是带有新序列号的相同数据包,则不会恢复。
关于原始的(1981年)TCP/IP规范的更多信息可以在RFC 793中找到,以及涉及TCP/IP协议扩展或修改的许多其他RFC。
您好,您似乎关心两个不同的问题:
回答1:
TCP保证字节序列的可靠有序传递。客户端应用程序通过write()
发送的任何数据将在服务器的read()
调用期间完全相同。
回答2:
重放攻击对TCP不起作用,因为每个连接都依赖于由客户端和服务器分别生成的两个随机32位数字。要使重放攻击起作用,攻击者必须猜测服务器为其正在发起的假连接生成的序列号(理论上,攻击者有1/2 ** 32的机会猜对)。如果攻击者猜错,她最多只会导致操作系统中的一些数据缓冲。
请注意,仅因为重放攻击无法起作用,并不意味着没有任何防止攻击者与您的服务器建立合法连接并向您的应用程序传输任何数据流的措施。这就是为什么始终重要的原因是验证输入。
TCP以下的层可能会遇到多个数据包或丢失数据包的情况。而TCP以上的层则不会遇到重复或丢失数据包的问题。
我不知道数据包重复的情况,但是在使用TCP/IP时我从未遇到过这种情况。我知道TCP/IP可以保证所有数据包都能按正确顺序到达,所以我无法理解为什么会出现这种情况。
这真的取决于你如何接收数据 - 尽管技术上协议不应该给你重复的数据包(即具有相同TCP校验和的数据包),但其他因素可能会导致你看到重复的数据包 - 例如,你使用的网络硬件;此外,如果你使用嗅探器来查看TCP流,而不仅仅是在应用程序中读取一个打开的套接字,即使嗅探器实际监视的TCP流没有重复的数据包,也有可能从嗅探器中获得重复的数据包。
举个现实世界的例子 - 目前我正在为一家主要股票交易所分析内部网络的TCP数据,并且我正在从多个嗅探器中获取数据并将其拼接回来。因此,在拉取数据时,我发现需要进行许多预处理步骤,包括查找和删除重复项。例如,在我刚刚读取的流中,大约有60,000个数据包,我已经找到并删除了95个重复的数据包。
我采取的策略是保持最近10个TCP校验和的滚动窗口,并忽略与这些校验和匹配的数据包。请注意,这对PSH数据包很有效,但对ACK数据包效果不佳 - 但我对这些数据包不太关心。
我编写了一个特殊的集合,用于跟踪这个滚动窗口的TCP校验和,这可能对其他人有所帮助:
/// <summary>
/// Combination of a double-linked-list and a hashset with a max bound;
/// Works like a bounded queue where new incoming items force old items to be dequeued;
/// Re-uses item containers to avoid GC'ing;
/// Public Add() and Contains() methods are fully thread safe through a ReaderWriterLockSlim;
/// </summary>
public class BoundedHashQueue<T>
{
private readonly int _maxSize = 100;
private readonly HashSet<T> _hashSet = new HashSet<T>();
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private readonly Item _head;
private readonly Item _tail;
private int _currentCount = 0;
public BoundedHashQueue(int maxSize)
{
_maxSize = maxSize;
_head = _tail = new Item();
}
private class Item
{
internal T Value;
internal Item Next;
internal Item Previous;
}
public void Add(T value)
{
_lock.Write(() =>
{
if (_currentCount == 0)
{
Item item = new Item();
item.Value = value;
_head.Next = item;
item.Previous = _head;
item.Next = _tail;
_tail.Previous = item;
_currentCount++;
}
else
{
Item item;
if (_currentCount >= _maxSize)
{
item = _tail.Previous;
_tail.Previous = item.Previous;
_tail.Previous.Next = _tail;
_hashSet.Remove(item.Value);
}
else
{
item = new Item();
_currentCount++;
}
item.Value = value;
item.Next = _head.Next;
item.Next.Previous = item;
item.Previous = _head;
_head.Next = item;
_hashSet.Add(value);
}
});
}
public bool Contains(T value)
{
return _lock.Read(() => _hashSet.Contains(value));
}
}}
你没有完全理解这个问题。 请查看此链接: http://en.wikipedia.org/wiki/Transmission_Control_Protocol
在这个页面上写着:
“TCP时间戳,定义在RFC 1323中,帮助TCP计算发送方和接收方之间的往返时间。时间戳选项包括4字节的时间戳值,其中发送方插入其时间戳时钟的当前值,以及4字节的回显回复时间戳值,其中接收方通常插入其已接收到的最近时间戳值。发送方使用回显回复时间戳来确认自己发送的段自发送以来经过的总时间[2]。
TCP时间戳也用于帮助处理TCP序列号遇到它们的2^32边界并“环绕”序列号空间的情况。这个方案被称为保护包装序列号或PAWS(详见RFC 1323)。”
问候, Joint(波兰)