我有一个用 C# 编写的 SFTP 服务器。
有 SSH 层,然后是包含 SFTP 消息的“ssh 数据”字段。
当我将消息(SSH 数据)移动到 SFTP 层时,有基本的标头。
在我收到的一些消息中,实际长度与标头中的长度(预期长度)不同。
发生这种情况时,大多数情况下实际长度小于预期长度。
发生这种情况我该怎么办?
以及如果实际长度大于预期长度该怎么办。
SFTP层代码示例:
public void OnInput(byte[] data)
{
DataWorker reader = new DataWorker(data);
if (reader.DataAvailable >= 5)
{
try
{
var msglength = reader.ReadUInt32();
if (reader.DataAvailable != msglength)
{
//DoSome
}
var msgtype = (SftpMessageType)(int)reader.ReadByte();
switch (msgtype)
{
case SftpMessageType.SSH_FXP_INIT:
_logger.Log(LogLevel.Information, "FXP_INIT");
HandleInit(reader);
break;
//etc..
}
}
catch (Exception e)
{
_channel?.SendClose(1);
return;
}
}
else
{
_logger.Log(LogLevel.Error, $"no data avialble. data length is: {reader.DataAvailable}.");
}
}
我尝试聚合消息,大多数情况下都有效。
但是我不知道这是否是正确的解决方案,而且我也不知道如果实际长度大于预期长度该怎么办。
什么是“实际长度”?哪有这回事。这个(和任何)协议定义了长度,这是最重要的。您遇到的唯一其他长度是当前加载的数据包的长度。
通常应用协议是通过某种传输协议实现的。在这种情况下,我们考虑可靠的流协议,即 TCP 或 TLS。这种传输协议有“读”和“写”操作。
那么客户端和服务器之间是如何通信的呢?客户端构造消息 X 并对该消息调用“write”。但是服务器如何读取它呢?天真的方法是简单地调用“read”并处理它。但这是错误的,因为实际上存在三种可能性:
因此,当您编写流代码时,您需要处理上述所有内容。事实上,您的代码不会执行此操作。您只实现了第 1 点,这就是您看到这些不一致的原因。
发生这种情况我该怎么办?
妥善处理。给你具体的代码示例有点困难,因为我什至不知道你使用什么框架。无论哪种方式,通用算法可能如下所示:
while true:
header = new byte[5]
offset = 0
while offset < 5:
size = read(header, offset, 5-offset)
offset += size
length = get_length_from_header(header)
flag = get_flag_from_header(header)
# The next lines buffer entire message into memory.
# Be aware that this is not efficient for file upload,
# which should be streamed directly into FS instead.
message = new bytes[length]
offset = 0
while offset < length:
size = read(message, offset, length - offset)
offset += size
process_message(message, length, flag)
此处
read
操作接受一个数组、应写入数组的偏移量以及应读取的字节数。它返回读取的实际字节数。所以是标准的低级读取操作。
当然这还可以进一步优化,例如一开始只读取5个字节并不一定有效。我们可以读取大缓冲区,比如 4k 字节,然后分析我们读取的内容。但它变得更复杂,所以让我们保持原样。