我正在尝试为程序创建套接字管理器。
我的问题是,根据我的需要,我需要从套接字中检索确切数量的数据到缓冲区。
1)使用静态数组
制作这样的静态缓冲区对我有用,但我不能总是知道要接收的数据的大小。
ubyte[2] buffer;
socket.receive(buffer);
这不起作用:
int size = buffer[0]; // for example ...
ubyte[size] buffer2; // crash at compilation because size is not known
socket.receive(buffer2);
所以我的第一个问题是:是否有可能在编译时不知道其大小的静态数组?
我搜索了一种使用动态缓冲区的方法,但我遇到了其他问题。
2)使用切片从缓冲区中提取数据
ubyte[] buffer;
socket.receive(buffer);
ubyte[2] header = buffer[0..2];
这有效,但如何从主缓冲区中删除提取的切片? 使用带有元组的std.algorithm中的remove函数,这样:
buffer = remove(buffer,tuple(0,2));
对我不起作用我不知道为什么,我在编译时遇到这个错误:
错误:未定义的标识符元组
这里有什么问题?
此外,我的套接字是非阻塞的,因为我希望它是异步的。 如果套接字试图同时在其上推送数据,那么在主缓冲区上执行删除会导致问题吗?
感谢您阅读我,对不起我的英语不好,我不是母语人士。
答案 0 :(得分:2)
警告:我之前没有使用过std.socket,所以这是未经测试的。我有使用C语言的套接字和一般的D经验,但还没有使用过这两个;)
这只是猜测你想要什么,但这里有:
import std.socket;
import std.bitmanip;
Socket socket = new Socket(AddressFamily.INET, SocketType.STREAM);
// ... bind, connect, etc ...
ubyte[2] header;
socket.receive(header);
ushort size = std.bitmanip.bigEndianToNative!ushort(header);
ubyte[] content = new ubyte[size];
socket.receive(content);
// ... do your stuff ...
即使在运行时,您也需要以某种方式知道传入数据的大小。在我的示例中,大小打包在流出来的前2个字节中。使用该信息,我们动态分配缓冲区以保留其余信息。为简单起见,此示例是同步的:它假定接收将在内容填充之前阻塞。这个例子也公然忽略了返回值,这很糟糕,但我很简单。
由于您打算进行异步通信,因此您可能希望编写自己的接收函数,根据需要多次调用receive来填充缓冲区,或者如果不满足某些已定义的条件则中止。
在您的示例1中,问题正是您所期望的:在编译时无法知道编译时的大小,无法在编译时(静态数组)调整大小。
你的问题的答案是否定的,在编译时不知道它们的大小就不可能有静态数组。
在你的例子2中,“ubyte [] buffer;”在传递给receive之前被初始化为null。这可能会给你一个断言崩溃或段错误。通常当D中的函数请求缓冲区时,它会期望已经分配了内存;函数的职责是用你想要的所有好东西填充已经分配的内存。唯一的例外可能是如果参数声明为ref,ex:“void foo(ref ubyte [] buffer)”,这表明如果你不这样做,函数将为你分配缓冲区(需要引用)实现这一点)。这就是为什么我的例子在从receive()获取东西之前做了“新的ubyte [size]”评估。
要回答关于切片的问题,这是将数组切成小方块的简单方法:
ubyte[] someArray = myArraySource();
ubyte[] header = someArray[0 .. 2];
someArray = someArray[2 .. $]; // Remove the "header" by slicing out everything else.
回答你的上一个问题:不,它不应该导致问题。你应该没事。
详细说明: 为了通过套接字进行双向通信,我不相信你的缓冲区操作对套接字本身很重要。您应该能够在不影响套接字的情况下从主缓冲区中“删除”内容,因为套接字不会持久保存对该缓冲区的任何引用。这是你的缓冲区,因为你分配了它(我希望)。这不依赖于套接字的同步和异步性质;它只是套接字不关心你的缓冲区那么多。一旦你收到/发送了你的字节,那么套接字已经完成了它的工作,至少在下次调用它之前。
希望有所帮助!