允许 D 应用程序和浏览器之间双向通信的工具链

发布于 2024-12-11 15:35:33 字数 114 浏览 3 评论 0原文

我希望有一个用 D 编程语言编写的应用程序更新其在浏览器中的显示。浏览器还应该将输入数据发送回应用程序。

我对编程还很陌生,并且对套接字/网络套接字/服务器如何组合在一起感到困惑。谁能建议一种方法?

I wish to have an app written in the D programming language update its display in a browser. The browser should also send input data back to the app.

I'm still quite new to programming and am confused with how sockets/websockets/servers all fit together. Can anyone suggest an approach?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

难忘№最初的完美 2024-12-18 15:35:33

非常感谢 gmfawcett 提供了他的基本 D 服务器示例的链接,我已将其与我在其他地方找到的版本 8 规范的简单 websocket 实现相结合(我相信目前仅适用于 Chrome 14/15)。它几乎是剪切和粘贴,但似乎工作得足够好,我希望它足以满足我的需求。

如果有人愿意快速浏览我的代码以发现任何明显的禁忌,请随意这样做 - 谢谢!

基本的 websocket impl: http://blog.vunie.com/implementing-websocket- Draft-10

Websocket v8 规范(协议 17):https://datatracker.ietf.org/doc/html/draft- ietf-hybi-thewebsocketprotocol-17

module wsserver;

import std.algorithm;
import std.base64;
import std.conv;
import std.stdio;
import std.socket;
import std.string;

//std.crypto: https://github.com/pszturmaj/phobos/tree/master/std/crypto
import crypto.hash.base;
import crypto.hash.sha;

struct WsServer
{
    private
    {
        Socket s;
        Socket conn;
        string subProtocol;
    }

    this(string host, ushort port = 8080, string subProtocol = "null")
    {
        this.subProtocol = subProtocol;

        s = new TcpSocket(AddressFamily.INET);
        s.bind(new InternetAddress(host, port));
        s.listen(8);

        conn = s.accept();

        writeln("point/refresh your browser to \"http://", host, "\" to intiate the websocket handshake");

        try
        {
           initHandshake(conn);
        }
        catch (Throwable e)
        {
            stderr.writeln("thrown: ", e);
        }
    }

    ~this()
    {
        conn.shutdown(SocketShutdown.BOTH);
        conn.close();

        s.shutdown(SocketShutdown.BOTH);
        s.close();
    }

    string data()
    {
        ubyte[8192] msgBuf;
        auto msgBufLen = conn.receive(msgBuf);

        auto firstByte = msgBuf[0];
        auto secondByte = msgBuf[1];

        // not sure these two checks are woking correctly!!!
        enforce((firstByte & 0x81), "Fragments not supported"); // enforce FIN bit is present
        enforce((secondByte & 0x80), "Masking bit not present"); // enforce masking bit is present

        auto msgLen = secondByte & 0x7f;

        ubyte[] mask, msg;

        if(msgLen < 126)
        {
            mask = msgBuf[2..6];
            msg = msgBuf[6..msgBufLen];
        }
        else if (msgLen == 126)
        {
            mask = msgBuf[4..8];
            msg = msgBuf[8..msgBufLen];
        }

        foreach (i, ref e; msg)
            e = msg[i] ^ mask[i%4];

        debug writeln("Client: " ~ cast(string) msg);

        return cast(string) msg;
    }

    void data(string msg)
    {
        ubyte[] newFrame;

        if (msg.length > 125)
            newFrame = new ubyte[4];
        else
            newFrame = new ubyte[2];

        newFrame[0] = 0x81;

        if (msg.length > 125)
        {
            newFrame[1] = 126;

            newFrame[2] = cast(ubyte) msg.length >> 8;
            newFrame[3] = msg.length & 0xFF;
        }
        else
            newFrame[1] = cast(ubyte) msg.length;

        conn.send(newFrame ~= msg);

        debug writeln("Server: " ~ msg);
    }

    private void initHandshake(Socket conn)
    {
        ubyte[8192] buf;  // big enough for some purposes...
        size_t position, headerEnd, len, newpos;

        // Receive the whole header before parsing it.
        while (true)
        {
            len = conn.receive(buf[position..$]);

            debug writeln(cast(string)buf);

            if (len == 0)               // empty request
              return;

            newpos = position + len;
            headerEnd = countUntil(buf[position..newpos], "\r\n\r\n");
            position = newpos;

            if (headerEnd >= 0)
                break;
        }

        // Now parse the header.
        auto lines = splitter(buf[0..headerEnd], "\r\n");
        string request_line = cast(string) lines.front;
        lines.popFront;

        // a very simple Header structure.
        struct Pair
        {
            string key, value;

            this(ubyte[] line)
            {
              auto tmp = countUntil(line, ": ");
              key = cast(string) line[0..tmp]; // maybe down-case these?
              value = cast(string) line[tmp+2..$];
            }
        }

        Pair[] headers;
        foreach(line; lines)
            headers ~= Pair(line);

        auto tmp            = splitter(request_line, ' ');
        string method       = tmp.front; tmp.popFront;
        string url          = tmp.front; tmp.popFront;
        string protocol     = tmp.front; tmp.popFront;

        enum GUID_v8 = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // version 8 spec... might change
        auto sha1 = new SHA1;
        sha1.put(strip(headers[5].value) ~ GUID_v8);
        auto respKey = to!string(Base64.encode(sha1.finish()));

        // Prepare a response, and send it
        string resp = join(["HTTP/1.1 101 Switching Protocols",
                            "Upgrade: websocket",
                            "Connection: Upgrade",
                            "Sec-WebSocket-Accept: " ~ respKey,
                            "Sec-WebSocket-Protocol: " ~ subProtocol,
                            ""],
                            "\r\n");

        conn.send(cast(ubyte[]) (resp ~ "\r\n"));

        debug writeln(resp);
    }
}

Many thanks to gmfawcett for the link to his basic D server example which I've mated with a bare-bones websocket implementation of the version 8 spec that I found elsewhere (currently only works in Chrome 14/15, I believe). It's pretty much cut'n'paste but seems to work well enough and I expect it will be sufficient in serving my needs.

If anyone has the inclination to cast a quick eye over my code for any glaring no-nos, please feel free to do so - and thanks!

Bare-bones websocket impl: http://blog.vunie.com/implementing-websocket-draft-10

Websocket v8 spec (protocol-17): https://datatracker.ietf.org/doc/html/draft-ietf-hybi-thewebsocketprotocol-17

module wsserver;

import std.algorithm;
import std.base64;
import std.conv;
import std.stdio;
import std.socket;
import std.string;

//std.crypto: https://github.com/pszturmaj/phobos/tree/master/std/crypto
import crypto.hash.base;
import crypto.hash.sha;

struct WsServer
{
    private
    {
        Socket s;
        Socket conn;
        string subProtocol;
    }

    this(string host, ushort port = 8080, string subProtocol = "null")
    {
        this.subProtocol = subProtocol;

        s = new TcpSocket(AddressFamily.INET);
        s.bind(new InternetAddress(host, port));
        s.listen(8);

        conn = s.accept();

        writeln("point/refresh your browser to \"http://", host, "\" to intiate the websocket handshake");

        try
        {
           initHandshake(conn);
        }
        catch (Throwable e)
        {
            stderr.writeln("thrown: ", e);
        }
    }

    ~this()
    {
        conn.shutdown(SocketShutdown.BOTH);
        conn.close();

        s.shutdown(SocketShutdown.BOTH);
        s.close();
    }

    string data()
    {
        ubyte[8192] msgBuf;
        auto msgBufLen = conn.receive(msgBuf);

        auto firstByte = msgBuf[0];
        auto secondByte = msgBuf[1];

        // not sure these two checks are woking correctly!!!
        enforce((firstByte & 0x81), "Fragments not supported"); // enforce FIN bit is present
        enforce((secondByte & 0x80), "Masking bit not present"); // enforce masking bit is present

        auto msgLen = secondByte & 0x7f;

        ubyte[] mask, msg;

        if(msgLen < 126)
        {
            mask = msgBuf[2..6];
            msg = msgBuf[6..msgBufLen];
        }
        else if (msgLen == 126)
        {
            mask = msgBuf[4..8];
            msg = msgBuf[8..msgBufLen];
        }

        foreach (i, ref e; msg)
            e = msg[i] ^ mask[i%4];

        debug writeln("Client: " ~ cast(string) msg);

        return cast(string) msg;
    }

    void data(string msg)
    {
        ubyte[] newFrame;

        if (msg.length > 125)
            newFrame = new ubyte[4];
        else
            newFrame = new ubyte[2];

        newFrame[0] = 0x81;

        if (msg.length > 125)
        {
            newFrame[1] = 126;

            newFrame[2] = cast(ubyte) msg.length >> 8;
            newFrame[3] = msg.length & 0xFF;
        }
        else
            newFrame[1] = cast(ubyte) msg.length;

        conn.send(newFrame ~= msg);

        debug writeln("Server: " ~ msg);
    }

    private void initHandshake(Socket conn)
    {
        ubyte[8192] buf;  // big enough for some purposes...
        size_t position, headerEnd, len, newpos;

        // Receive the whole header before parsing it.
        while (true)
        {
            len = conn.receive(buf[position..$]);

            debug writeln(cast(string)buf);

            if (len == 0)               // empty request
              return;

            newpos = position + len;
            headerEnd = countUntil(buf[position..newpos], "\r\n\r\n");
            position = newpos;

            if (headerEnd >= 0)
                break;
        }

        // Now parse the header.
        auto lines = splitter(buf[0..headerEnd], "\r\n");
        string request_line = cast(string) lines.front;
        lines.popFront;

        // a very simple Header structure.
        struct Pair
        {
            string key, value;

            this(ubyte[] line)
            {
              auto tmp = countUntil(line, ": ");
              key = cast(string) line[0..tmp]; // maybe down-case these?
              value = cast(string) line[tmp+2..$];
            }
        }

        Pair[] headers;
        foreach(line; lines)
            headers ~= Pair(line);

        auto tmp            = splitter(request_line, ' ');
        string method       = tmp.front; tmp.popFront;
        string url          = tmp.front; tmp.popFront;
        string protocol     = tmp.front; tmp.popFront;

        enum GUID_v8 = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // version 8 spec... might change
        auto sha1 = new SHA1;
        sha1.put(strip(headers[5].value) ~ GUID_v8);
        auto respKey = to!string(Base64.encode(sha1.finish()));

        // Prepare a response, and send it
        string resp = join(["HTTP/1.1 101 Switching Protocols",
                            "Upgrade: websocket",
                            "Connection: Upgrade",
                            "Sec-WebSocket-Accept: " ~ respKey,
                            "Sec-WebSocket-Protocol: " ~ subProtocol,
                            ""],
                            "\r\n");

        conn.send(cast(ubyte[]) (resp ~ "\r\n"));

        debug writeln(resp);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文