D语言中的UDP套接字

3 投票
2 回答
1070 浏览
提问于 2025-04-18 06:57

我正在尝试把一个用Python写的程序转换成D语言;这个程序是用来发送Art-Net DMX数据包的。

Python代码:

import sys, socket, math, time
from ctypes import *

class ArtNetDMXOut(LittleEndianStructure):
    PORT = 0x1936
    _fields_ = [("id", c_char * 8),
                ("opcode", c_ushort),
                ("protverh", c_ubyte),
                ("protver", c_ubyte),
                ("sequence", c_ubyte),
                ("physical", c_ubyte),         
                ("universe", c_ushort),
                ("lengthhi", c_ubyte),
                ("length", c_ubyte),
                ("payload", c_ubyte * 512)]
    def __init__(self):
        self.id = b"Art-Net"
        self.opcode = 0x5000
        self.protver = 14
        self.universe = 0
        self.lengthhi = 2

def main():
    hostIP = "localhost"
    S = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    packet = ArtNetDMXOut()

    packet.payload[0] = 255
    S.sendto(packet, (hostIP, ArtNetDMXOut.PORT))

if __name__ == "__main__":
    main()

D语言代码:

import std.stdio;
import std.socket;

class ArtNetDMX{
    char id[8];
    ushort opCode;
    ubyte verH;
    ubyte ver;
    ubyte sequence;
    ubyte physical;
    ushort universe;
    ubyte lengthHi;
    ubyte length;
    ubyte data[511];

    this(){
        this.id = "ART-NET0";
        this.opCode = 0x5000;
        this.ver = 14;
        this.universe = 0;
        this.lengthHi = 2;
    }

}

void main() {
    auto s = new UdpSocket();
    auto addr = new InternetAddress("localhost", 6454);
    s.bind(addr);
    ArtNetDMX packet = new ArtNetDMX();

    packet.data[0] = 255;

    s.send(packet);
};

我的Python代码运行得非常顺利,但在D语言中,我在代码的send(packet);这一行遇到了错误,错误信息是function std.socket.Socket.send (const(void)[] buf, SocketFlags flags) is not callable using argument types (ArtNetDMX)

我这样做是对的吗?我哪里出错了?

2 个回答

4

Dlang是一种强类型语言。这意味着在使用socket.send方法时,你必须传入一个数组。你需要把对象(类型为ArtNetDMX)转换成数组。你可以使用Dlang的结构体来直接进行转换:

struct ArtNetDMX{
  // you can control alignment inside struct 
  // see http://dlang.org/attribute#align
  align (1): 
    char id[8] = "ART-NETO";
    ushort opCode = 0x5000;
    ubyte verH;
    ubyte ver = 14;
    ubyte sequence;
    ubyte physical;
    ushort universe = 0;
    ubyte lengthHi = 2;
    ubyte length;
    ubyte data[511];

}

void main() {
    auto s = new UdpSocket();
    auto addr = new InternetAddress("localhost", 6454);
    s.bind(addr);
    ubyte[ArtNetDMX.sizeof] bytes; //allocate memory on stack for ArtNetDMX struct
    ArtNetDMX *packet = cast(ArtNetDMX *)bytes.ptr;
    packet.data[0] = 255;//work with ArtNetDMX struct
    s.send(bytes);
};

顺便提一下,这段代码在处理网络字节顺序时对ushort字段有问题(可以参考htons函数)。

5

首先,你需要把 ArtNetDMX 定义为一个 struct,而不是 class。因为在 D 语言中,类是引用类型,类里面的字段布局没有保证。而且因为你要把它通过网络发送,所以要指定合适的对齐方式(通常如果每个字段都要紧凑在一起,就用 1):

struct ArtNetDMX {
    align(1):
    ....
}

在你的 main 函数里,你可以在栈上分配一个这个结构的实例:

ArtNetDMX packet;        // no `new` required

packet.id = "ART-NET0";  // initialize fields
packet.opCode = 0x5000;
packet.ver = 14;
packet.universe = 0;
packet.lengthHi = 2;

如果你经常需要进行相同的初始化,可以把这部分代码放到一个函数里:

ArtNetDMX createArtNetDMX()
{
    ArtNetDMX packet;

    packet.id = "ART-NET0";
    packet.opCode = 0x5000;
    packet.ver = 14;
    packet.universe = 0;
    packet.lengthHi = 2;

    return packet;
}

最后,Socket.send 需要的参数是一个切片(可以理解为数组或数组的一部分)。如果你要一次发送多个数据包,就把这些数据包放进一个数组里,然后直接发送这个数组。

因为你只发送一个数据包,所以可以把

s.send(packet);

替换成

s.send((&packet)[0..1]);

这样做是为了安全地把一个对象转换成一个只有一个元素的切片。

撰写回答