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

时间:2011-10-23 23:54:29

标签: sockets websocket d

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

我对编程还很陌生,并且对插座/ websockets /服务器如何组合在一起感到困惑。任何人都可以建议一种方法吗?

1 个答案:

答案 0 :(得分:2)

非常感谢gmfawcett链接到他的基本D服务器示例,我与其他地方发现的版本8规范的简单websocket实现交配(目前仅适用于Chrome 14/15,我相信) 。它几乎是'cut'n'paste,但似乎运作良好,我希望它足以满足我的需求。

如果有人倾向于快速关注我的代码以获得任何明显的no-nos,请随意这样做 - 谢谢!

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

Websocket v8规范(协议-17):http://tools.ietf.org/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);
    }
}