Windows CE 5.0 HTTPD <-> .NET应用程序

发布于 2024-11-16 19:57:22 字数 265 浏览 3 评论 0 原文

我想知道将 Windows CE 5.0 设备的 HTTPD Web 服务器耦合到同一 Windows CE 设备上运行的 .NET 应用程序的最实用方法是什么?

我的第一个想法是构建一个 ISAPI 扩展,将传入的 http 请求转发到 .NET 应用程序。不知道该怎么做!可能是共享内存、COM、TCP/IP 套接字?

另一种方法是在 .NET 应用程序本身内实现独立的 HTTP 服务器并避免使用 HTTPD。

有什么经验或想法吗?

谢谢 克里斯

I'm what is the most practical way of coupling a HTTPD Web server of a Windows CE 5.0 Device to a .NET application that runs on the same Windows CE device?

My first idea was to build an ISAPI extension that would forward incoming http requests to a .NET application. Not sure how to do it! May be shared memory, COM, TCP/IP Sockets?

Another way could be, implementing a standalone HTTP server within the .NET application itself and avoid using the HTTPD.

Any experience or ideas?

Thanks
Chris

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

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

发布评论

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

评论(2

琉璃繁缕 2024-11-23 19:57:22

在 CE Web 服务器中运行 .NET 代码的关键是将服务器 dll 加载到 .NET 进程中。几年前我做了一个概念验证来证明这一点。

该设计乍一看可能有点复杂,但有几个优点:

  • .NET 代码和非托管 ISAPI 扩展可以在 Web 服务器中并行运行
  • 加密和身份验证等 Web 服务器功能仍然有效
  • 资源继续由 Web 服务器管理,包括线程池
  • 性能方面可能胜过任何基于单独进程和 IPC 的解决方案

首先,我们需要阻止 Windows CE 自动启动 Web 服务器。将其添加到注册表中:

[HKEY_LOCAL_MACHINE\Services\HTTPD]
    "Flags"=dword:4  ; DEVFLAGS_NOLOAD

当我们这样做时,添加另一个键以将“/dotnet”映射到我们的自定义 ISAPI 处理程序:

[HKEY_LOCAL_MACHINE\Comm\HTTPD\VROOTS\/dotnet]
    @="\\Windows\\HttpdHostUnmanaged.dll"

从以下源代码创建一个名为 HttpdHostProc.exe 的 .NET exe:

using System;
using System.Runtime.InteropServices;
using System.Text;

class HttpdHostProc
{
    static void Main(string[] args)
    {
        GetExtensionVersionDelegate pInit =
            new GetExtensionVersionDelegate(GetExtensionVersion);
        TerminateExtensionDelegate pDeinit
            = new TerminateExtensionDelegate(TerminateExtension);
        HttpExtensionProcDelegate pProc =
            new HttpExtensionProcDelegate(HttpExtensionProc);

        Init(pInit, pDeinit, pProc);

        int state = SERVICE_INIT_STOPPED | SERVICE_NET_ADDR_CHANGE_THREAD;
        int context = HTP_Init(state);

        HTP_IOControl(context, IOCTL_SERVICE_REGISTER_SOCKADDR,
             IntPtr.Zero, 0, IntPtr.Zero, 0, IntPtr.Zero);

        HTP_IOControl(context, IOCTL_SERVICE_STARTED,
             IntPtr.Zero, 0, IntPtr.Zero, 0, IntPtr.Zero);

        RunHttpd(context, 80);
    }

    static int GetExtensionVersion(IntPtr pVer)
    {
        OutputDebugString("GetExtensionVersion from .NET\r\n");
        return 1;
    }

    static int TerminateExtension(int dwFlags)
    {
        OutputDebugString("TerminateExtension from .NET\r\n");
        return 1;
    }

    static int HttpExtensionProc(IntPtr pECB)
    {
        OutputDebugString("HttpExtensionProc from .NET\r\n");

        var response = "<html><head></head><body><p>Hello .NET!</p></body></html>";
        var bytes = Encoding.UTF8.GetBytes(response);
        var length = bytes.Length;

        var unmanagedbuffer = Marshal.AllocHGlobal(length);
        Marshal.Copy(bytes, 0, unmanagedbuffer, length);

        var retval = WriteClient(pECB, unmanagedbuffer, ref length);

        Marshal.FreeHGlobal(unmanagedbuffer);

        return retval;
    }

    delegate int GetExtensionVersionDelegate(IntPtr pVer);
    delegate int TerminateExtensionDelegate(int dwFlags);
    delegate int HttpExtensionProcDelegate(IntPtr pECB);

    [DllImport("HttpdHostUnmanaged.dll", SetLastError = true)]
    extern static void Init(
        GetExtensionVersionDelegate pInit,
        TerminateExtensionDelegate pDeinit,
        HttpExtensionProcDelegate pProc
    );

    [DllImport("HttpdHostUnmanaged.dll", SetLastError = true)]
    extern static int RunHttpd(int context, int port);

    [DllImport("HttpdHostUnmanaged.dll", SetLastError = true)]
    extern static int WriteClient(IntPtr pECB, IntPtr Buffer, ref int lpdwBytes);

    [DllImport("coredll.dll")]
    extern static void OutputDebugString(string msg);

    [DllImport("httpd.dll", SetLastError = true)]
    extern static int HTP_Init(int dwData);

    [DllImport("httpd.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    extern static bool HTP_IOControl(int dwData, int dwCode, IntPtr pBufIn,
        int dwLenIn, IntPtr pBufOut, int dwLenOut, IntPtr pdwActualOut);

    const int IOCTL_SERVICE_STARTED = 0x01040038;
    const int IOCTL_SERVICE_REGISTER_SOCKADDR = 0x0104002c;

    const int SERVICE_INIT_STOPPED = 0x00000001;
    const int SERVICE_NET_ADDR_CHANGE_THREAD = 0x00000008;
}

一些注释:

  • 现在 函数加载并初始化我们的非托管 dll,它将充当托管和非托管代码之间的垫脚石,
  • 然后通过调用其 HTP_Init 函数来初始化并启动 Web 服务器,然后是几个 ioctl
  • 最后,它在我们的非托管 dll 中调用 RunHttpd,它将接受传入请求并将它们转发到 Web 服务器。

如果您曾经进行过任何 ISAPI 编程,那么下面的三个函数 - GetExtensionVersion、TerminateExtension、HttpExtensionProc - 应该看起来很熟悉。如果不是,您真正需要知道的是传入请求是由 HttpExtensionProc 处理的。

继续讨论非托管 dll,HttpdHostUnmanagement.dll:

#include <winsock2.h>
#include <httpext.h>

typedef BOOL (* pfHTP_IOControl)(DWORD dwData, DWORD dwCode, PBYTE pBufIn,
    DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut);

typedef BOOL (* PFN_WRITE_CLIENT)(HCONN ConnID, LPVOID Buffer,
    LPDWORD lpdwBytes, DWORD dwReserved);

static PFN_GETEXTENSIONVERSION g_pInit;
static PFN_TERMINATEEXTENSION g_pDeinit;
static PFN_HTTPEXTENSIONPROC g_pProc;

BOOL APIENTRY DllMain(HANDLE, DWORD, LPVOID)
{
    return TRUE;
}

extern "C" void Init(
    PFN_GETEXTENSIONVERSION pInit,
    PFN_TERMINATEEXTENSION pDeinit,
    PFN_HTTPEXTENSIONPROC pProc)
{
    // Store pointers to .NET delegates
    g_pInit = pInit;
    g_pDeinit = pDeinit;
    g_pProc = pProc;
}

extern "C" BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
    pVer->dwExtensionVersion = HSE_VERSION;
    strncpy(pVer->lpszExtensionDesc, "HttpdHostUnmanaged",
        HSE_MAX_EXT_DLL_NAME_LEN);

    // Call .NET GetExtensionVersion
    return g_pInit(pVer);
}

extern "C" BOOL WINAPI TerminateExtension(DWORD dwFlags)
{
    // Call .NET TerminateExtension
    return g_pDeinit(dwFlags);
}

extern "C" DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)
{
    // Call .NET HttpExtensionProc
    return g_pProc(pECB);
}

extern "C" DWORD WINAPI WriteClient(EXTENSION_CONTROL_BLOCK *pECB,
    LPVOID Buffer, LPDWORD lpdwBytes)
{
    return pECB->WriteClient(pECB->ConnID, Buffer, lpdwBytes, 0);
}

extern "C" int WINAPI RunHttpd(DWORD context, int port)
{
    // Load web server and start accepting connections.
    // When a connection comes in,
    // pass it to httpd using IOCTL_SERVICE_CONNECTION.

    HMODULE hDll = LoadLibrary(L"httpd.dll");
    if(!hDll)
    {
        return -1;
    }

    pfHTP_IOControl Ioctl =
        (pfHTP_IOControl)GetProcAddress(hDll, L"HTP_IOControl");
    if(!Ioctl)
    {
        FreeLibrary(hDll);
        return -2;
    }

    WSADATA Data;
    int status = WSAStartup(MAKEWORD(1, 1), &Data);
    if(status != 0)
    {
        FreeLibrary(hDll);
        return status;
    }

    SOCKET s = socket(PF_INET, SOCK_STREAM, 0);
    if(s == INVALID_SOCKET)
    {
        status = WSAGetLastError();
        goto exit;
    }

    SOCKADDR_IN sAddr;
    memset(&sAddr, 0, sizeof(sAddr));
    sAddr.sin_port = htons(port);
    sAddr.sin_family = AF_INET;
    sAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if(bind(s, (LPSOCKADDR)&sAddr, sizeof(sAddr)) == SOCKET_ERROR)
    {
        status = WSAGetLastError();
        goto exit;
    }

    if(listen(s, SOMAXCONN) == SOCKET_ERROR)
    {
        status = WSAGetLastError();
        goto exit;
    }

    for(;;)
    {
        SOCKADDR_IN addr;
        int cbAddr = sizeof(addr);

        SOCKET conn = accept(s, (LPSOCKADDR)&addr, &cbAddr);

        if(conn == INVALID_SOCKET)
        {
            status = WSAGetLastError();
            goto exit;
        }

        DWORD IOCTL_SERVICE_CONNECTION = 0x01040034;
        Ioctl(context, IOCTL_SERVICE_CONNECTION,
            (PBYTE)&conn, sizeof(conn), NULL, 0, NULL);
    }

exit:
    FreeLibrary(hDll);

    if(s != INVALID_SOCKET)
    {
        closesocket(s);
    }

    WSACleanup();
    return status;
}

其中有许多不太有趣的函数,用于将调用转发到 .NET 或从 .NET 转发调用。

如上所述,RunHttpd 函数只是接受传入连接,并通过另一个 ioctl 将它们传递到 Web 服务器进行处理。

要进行全部测试,请在设备上启动 HttpdHostProc.exe,然后在浏览器中打开 http:///dotnet。 CE 设备应使用一段包含消息“Hello .NET!”的 HTML 进行响应。

此代码在带有 .NET Compact Framework 3.5 的 Windows Embedded Compact 7.0 上运行,但也可能适用于其他版本。

我针对 Pocket PC 2003 SDK 构建了非托管 dll,因为这就是我碰巧安装的。也许任何 Windows CE SDK 都可以,但您可能必须调整编译器设置,例如我必须使用 /GS-(禁用缓冲区安全检查)来构建 PPC2003。

在 .NET 中实现 RunHttpd 函数也是很诱人的,但请注意,这存在一些潜在的问题:

  • 在我的测试中,.NET CF 套接字上的 Handle 属性返回了一种伪句柄,这导致了不适用于本机套接字 API
  • 套接字的生命周期将由 .NET 运行时管理,这使得将套接字的所有权传递给 Web 服务器变得特别困难

如果您不介意使用 /unsafe 进行编译,则性能可能会得到提高通过传递固定缓冲区稍微到 WriteClient,而不是将所有响应数据复制到 HttpExtensionProc 中的非托管缓冲区。

EXTENSION_CONTROL_BLOCK 结构包含许多有用的字段和函数,显然需要将它们包含在完整的实现中。

编辑

只是为了澄清如何处理请求:

  1. 传入的请求在 RunHttpd 中被接受,它使用 ioctl 将它们转发到 Web 服务器
  2. 根据我们之前设置的注册表中的 vroot 条目,Web 服务器调用 HttpdHostUnmanagement.dll 来处理 /dotnet 的请求
  3. 如果这是 /dotnet 的第一个请求,则 Web 服务器将通过调用 HttpdHostUnmanagement.dll 中的 GetExtensionVersion 的非托管版本来启动。非托管 GetExtensionVersion 回调到 GetExtensionVersion 的 .NET 版本。 GetExtensionVersion 是初始化任何所需资源的一个方便的地方,因为它只被调用一次(相应的清理函数是 TerminateExtension,当 Web 服务器决定卸载 HttpdHostUnmanagement.dll 时调用该函数)
  4. 接下来,Web 服务器调用非托管 HttpExtensionProc
  5. 非托管 HttpExtensionProc 回调 .NET 版本的 HttpExtensionProc
  6. 托管 HttpExtensionProc 生成响应,并调用非托管 HttpExtensionProc WriteClient 将其传递给客户端

The key to running .NET code in the CE web server is to load the server dll into a .NET process. I did a proof of concept a few years back to demonstrate this.

The design may look a bit convoluted at first sight, but has several advantages:

  • .NET code and unmanaged ISAPI extensions can run side by side in the web server
  • Web server features like encryption and authentication still work
  • Resources continue to be managed by the web server, including the thread pool
  • Performance-wise probably beats any solution based on separate processes and IPC

First off, we'll need to prevent Windows CE from starting the web server automatically. Add this to the registry:

[HKEY_LOCAL_MACHINE\Services\HTTPD]
    "Flags"=dword:4  ; DEVFLAGS_NOLOAD

While we're at it, add another key to map '/dotnet' to our custom ISAPI handler:

[HKEY_LOCAL_MACHINE\Comm\HTTPD\VROOTS\/dotnet]
    @="\\Windows\\HttpdHostUnmanaged.dll"

Now create a .NET exe called HttpdHostProc.exe from the following source code:

using System;
using System.Runtime.InteropServices;
using System.Text;

class HttpdHostProc
{
    static void Main(string[] args)
    {
        GetExtensionVersionDelegate pInit =
            new GetExtensionVersionDelegate(GetExtensionVersion);
        TerminateExtensionDelegate pDeinit
            = new TerminateExtensionDelegate(TerminateExtension);
        HttpExtensionProcDelegate pProc =
            new HttpExtensionProcDelegate(HttpExtensionProc);

        Init(pInit, pDeinit, pProc);

        int state = SERVICE_INIT_STOPPED | SERVICE_NET_ADDR_CHANGE_THREAD;
        int context = HTP_Init(state);

        HTP_IOControl(context, IOCTL_SERVICE_REGISTER_SOCKADDR,
             IntPtr.Zero, 0, IntPtr.Zero, 0, IntPtr.Zero);

        HTP_IOControl(context, IOCTL_SERVICE_STARTED,
             IntPtr.Zero, 0, IntPtr.Zero, 0, IntPtr.Zero);

        RunHttpd(context, 80);
    }

    static int GetExtensionVersion(IntPtr pVer)
    {
        OutputDebugString("GetExtensionVersion from .NET\r\n");
        return 1;
    }

    static int TerminateExtension(int dwFlags)
    {
        OutputDebugString("TerminateExtension from .NET\r\n");
        return 1;
    }

    static int HttpExtensionProc(IntPtr pECB)
    {
        OutputDebugString("HttpExtensionProc from .NET\r\n");

        var response = "<html><head></head><body><p>Hello .NET!</p></body></html>";
        var bytes = Encoding.UTF8.GetBytes(response);
        var length = bytes.Length;

        var unmanagedbuffer = Marshal.AllocHGlobal(length);
        Marshal.Copy(bytes, 0, unmanagedbuffer, length);

        var retval = WriteClient(pECB, unmanagedbuffer, ref length);

        Marshal.FreeHGlobal(unmanagedbuffer);

        return retval;
    }

    delegate int GetExtensionVersionDelegate(IntPtr pVer);
    delegate int TerminateExtensionDelegate(int dwFlags);
    delegate int HttpExtensionProcDelegate(IntPtr pECB);

    [DllImport("HttpdHostUnmanaged.dll", SetLastError = true)]
    extern static void Init(
        GetExtensionVersionDelegate pInit,
        TerminateExtensionDelegate pDeinit,
        HttpExtensionProcDelegate pProc
    );

    [DllImport("HttpdHostUnmanaged.dll", SetLastError = true)]
    extern static int RunHttpd(int context, int port);

    [DllImport("HttpdHostUnmanaged.dll", SetLastError = true)]
    extern static int WriteClient(IntPtr pECB, IntPtr Buffer, ref int lpdwBytes);

    [DllImport("coredll.dll")]
    extern static void OutputDebugString(string msg);

    [DllImport("httpd.dll", SetLastError = true)]
    extern static int HTP_Init(int dwData);

    [DllImport("httpd.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    extern static bool HTP_IOControl(int dwData, int dwCode, IntPtr pBufIn,
        int dwLenIn, IntPtr pBufOut, int dwLenOut, IntPtr pdwActualOut);

    const int IOCTL_SERVICE_STARTED = 0x01040038;
    const int IOCTL_SERVICE_REGISTER_SOCKADDR = 0x0104002c;

    const int SERVICE_INIT_STOPPED = 0x00000001;
    const int SERVICE_NET_ADDR_CHANGE_THREAD = 0x00000008;
}

A few comments:

  • The Main function loads and initialises our unmanaged dll, which will act as a stepping stone between managed and unmanaged code
  • It then initialises and starts the web server by calling its HTP_Init function, followed by a couple of ioctls
  • Finally, it calls RunHttpd in our unmanaged dll, which will accept incoming requests and forward them to the web server.

The three functions further down - GetExtensionVersion, TerminateExtension, HttpExtensionProc - should look familiar if you've ever done any ISAPI programming. If not, all you really need to know is that incoming requests are handled by HttpExtensionProc.

Moving on to the unmanaged dll, HttpdHostUnmanaged.dll:

#include <winsock2.h>
#include <httpext.h>

typedef BOOL (* pfHTP_IOControl)(DWORD dwData, DWORD dwCode, PBYTE pBufIn,
    DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut);

typedef BOOL (* PFN_WRITE_CLIENT)(HCONN ConnID, LPVOID Buffer,
    LPDWORD lpdwBytes, DWORD dwReserved);

static PFN_GETEXTENSIONVERSION g_pInit;
static PFN_TERMINATEEXTENSION g_pDeinit;
static PFN_HTTPEXTENSIONPROC g_pProc;

BOOL APIENTRY DllMain(HANDLE, DWORD, LPVOID)
{
    return TRUE;
}

extern "C" void Init(
    PFN_GETEXTENSIONVERSION pInit,
    PFN_TERMINATEEXTENSION pDeinit,
    PFN_HTTPEXTENSIONPROC pProc)
{
    // Store pointers to .NET delegates
    g_pInit = pInit;
    g_pDeinit = pDeinit;
    g_pProc = pProc;
}

extern "C" BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
    pVer->dwExtensionVersion = HSE_VERSION;
    strncpy(pVer->lpszExtensionDesc, "HttpdHostUnmanaged",
        HSE_MAX_EXT_DLL_NAME_LEN);

    // Call .NET GetExtensionVersion
    return g_pInit(pVer);
}

extern "C" BOOL WINAPI TerminateExtension(DWORD dwFlags)
{
    // Call .NET TerminateExtension
    return g_pDeinit(dwFlags);
}

extern "C" DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)
{
    // Call .NET HttpExtensionProc
    return g_pProc(pECB);
}

extern "C" DWORD WINAPI WriteClient(EXTENSION_CONTROL_BLOCK *pECB,
    LPVOID Buffer, LPDWORD lpdwBytes)
{
    return pECB->WriteClient(pECB->ConnID, Buffer, lpdwBytes, 0);
}

extern "C" int WINAPI RunHttpd(DWORD context, int port)
{
    // Load web server and start accepting connections.
    // When a connection comes in,
    // pass it to httpd using IOCTL_SERVICE_CONNECTION.

    HMODULE hDll = LoadLibrary(L"httpd.dll");
    if(!hDll)
    {
        return -1;
    }

    pfHTP_IOControl Ioctl =
        (pfHTP_IOControl)GetProcAddress(hDll, L"HTP_IOControl");
    if(!Ioctl)
    {
        FreeLibrary(hDll);
        return -2;
    }

    WSADATA Data;
    int status = WSAStartup(MAKEWORD(1, 1), &Data);
    if(status != 0)
    {
        FreeLibrary(hDll);
        return status;
    }

    SOCKET s = socket(PF_INET, SOCK_STREAM, 0);
    if(s == INVALID_SOCKET)
    {
        status = WSAGetLastError();
        goto exit;
    }

    SOCKADDR_IN sAddr;
    memset(&sAddr, 0, sizeof(sAddr));
    sAddr.sin_port = htons(port);
    sAddr.sin_family = AF_INET;
    sAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if(bind(s, (LPSOCKADDR)&sAddr, sizeof(sAddr)) == SOCKET_ERROR)
    {
        status = WSAGetLastError();
        goto exit;
    }

    if(listen(s, SOMAXCONN) == SOCKET_ERROR)
    {
        status = WSAGetLastError();
        goto exit;
    }

    for(;;)
    {
        SOCKADDR_IN addr;
        int cbAddr = sizeof(addr);

        SOCKET conn = accept(s, (LPSOCKADDR)&addr, &cbAddr);

        if(conn == INVALID_SOCKET)
        {
            status = WSAGetLastError();
            goto exit;
        }

        DWORD IOCTL_SERVICE_CONNECTION = 0x01040034;
        Ioctl(context, IOCTL_SERVICE_CONNECTION,
            (PBYTE)&conn, sizeof(conn), NULL, 0, NULL);
    }

exit:
    FreeLibrary(hDll);

    if(s != INVALID_SOCKET)
    {
        closesocket(s);
    }

    WSACleanup();
    return status;
}

There are a number of not terribly interesting functions in there that forward calls to and from .NET.

As mentioned above, the RunHttpd function just accepts incoming connections and passes them to the web server for processing by means of another ioctl.

To test it all, launch HttpdHostProc.exe on the device, then open http://<ipaddr>/dotnet in a browser. The CE device should respond with a bit of HTML containing the message "Hello .NET!".

This code runs on Windows Embedded Compact 7.0 with .NET Compact Framework 3.5, but would probably work on other versions as well.

I built the unmanaged dll against the Pocket PC 2003 SDK, since that's what I happened to have installed. Probably any Windows CE SDK would do, but you might have to adjust compiler settings, for instance I had to build with /GS- (buffer security checks disabled) for PPC2003.

It is tempting to implement the RunHttpd function in .NET as well, but be warned there are a couple of potential issues with that:

  • In my testing, the Handle property on a .NET CF socket returned a sort of pseudo-handle, which did not work with native socket APIs
  • The lifetime of the socket would be managed by the .NET runtime, making it particularly difficult to pass ownership of the socket to the web server

If you don't mind compiling with /unsafe, performance could probably be improved slightly by passing fixed buffers to WriteClient, rather than copying all the response data to an unmanaged buffer in HttpExtensionProc.

The EXTENSION_CONTROL_BLOCK structure contains a number of useful fields and functions that obviously would need to be included in a full implementation.

EDIT

Just to clarify how requests are handled:

  1. Incoming requests are accepted in RunHttpd, which forwards them to the web server using an ioctl
  2. As per the vroot entry in the registry that we set up earlier, the web server calls into HttpdHostUnmanaged.dll to handle requests for /dotnet
  3. If this is the first request for /dotnet, the web server starts by calling the unmanaged version of GetExtensionVersion in HttpdHostUnmanaged.dll. The unmanaged GetExtensionVersion calls back into the .NET version of GetExtensionVersion. GetExtensionVersion is a conveninent place to initialise any resources required, as it is only called once (the corresponding clean-up function is TerminateExtension, which is called when/if the web server decides to unload HttpdHostUnmanaged.dll)
  4. Next, the web server calls the unmanaged HttpExtensionProc
  5. The unmanaged HttpExtensionProc calls back into the .NET version of HttpExtensionProc
  6. The managed HttpExtensionProc generates the response, and calls the unmanaged WriteClient to deliver it to the client
久伴你 2024-11-23 19:57:22

在我看来,基于过去尝试使用内置 HTTPD 服务器的经验,内置服务器绝对无法尝试做任何有用的事情。调试任何东西都是一件令人头疼的事情,并且与任何设备硬件/系统的互操作都是痛苦的。

由于 CF 中没有 EE 托管支持,因此 Web 服务器无法加载托管程序集(来自 ISAPI 或其他任何内容)。这意味着您的托管代码必须位于单独的进程中,并且要进行通信,您必须使用 IPC - 类似于 P2PMessageQueueMemoryMappedFile 、套接字等。

编写自己的 HTTPD 服务器也是一种选择,但这不是一项简单的任务。听起来很简单,但一旦深入其中,就会涉及到很多内容。我知道这是因为我们几年前在一个项目上做出了这个决定,并且我们最终创建的服务器支持 ASP.NET 的一个子集,并且我们实际上将其变成了 商业产品因为a)它真的很有用,b)因为它需要大量的工作来实际编写。然而,它确实提供了托管托管代码并能够在 Studio 中调试而不是像托管开发人员所期望的 printf 那样的好处。

In my opinion, based on trying to use the built-in HTTPD server in the past, is that the built-in server absolutely sucks for trying to do anything useful. It's a major headache to debug anything and interop with any device hardware/system is painful.

Since there is no EE Hosting support in the CF, the web server cannot load managed assemblies (from ISAPI or anything else). That means your managed code has to be in a separate process and to communicate you have to use IPC - something like a P2PMessageQueue, MemoryMappedFile, socket, etc.

Writing your own HTTPD server is also an option, but it's not a trivial task. It sounds simple, but there's a lot involved once you dive into it. I know that because we made that decision several years ago on a project, and the server we ended up creating supported a subset of ASP.NET and we actually turned it into a commercial product because a) it was really useful and b) becasue it took a lot of work to actually write. It does, however, give that benefit of hosting managed code and being able to debug in Studio instead of printf as a managed developer expects.

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