WinInet制作客户端相关

来源:互联网 发布:java web生产验证吗 编辑:程序博客网 时间:2024/06/03 23:57

目标:使用WinInet开发一个能够通过http,https与服务器通讯的客户端,代替webbrowser。

相关资料索引:

1、Networking and Internet(http://msdn.microsoft.com/en-us/library/windows/desktop/ee663286%28v=vs.85%29.aspx)

2、如何使用 WinInet API 发出 SSL 请求。(http://support.microsoft.com/kb/168151/zh-cn)

3、使用InternetOpenUrl挂起的一个解决方案 (http://blog.csdn.net/rbagglo/article/details/4877800)


资料内容:


如何使用 WinInet API 发出 SSL 请求。

使用 WinInet API 可以建立安全套接字层 (SSL) 或专用通信技术 (PCT) HTTP 会话。安全 HTTP(表示为 HTTPS://)在 TCP 端口 443 上发生。可以使用类似于下面这样的代码来建立 HTTPS 会话:

   ...   hOpen = InternetOpen (...);   Connect = InternetConnect (                hOpen,                      // InternetOpen handle                "MyHttpServer",             // Server  name      INTERNET_DEFAULT_HTTPS_PORT,// Default HTTPS port - 443                "",                         // User name                "",                         //  User password                INTERNET_SERVICE_HTTP,      // Service      0,                          // Flags      0                           // Context                   );   hReq = HttpOpenRequest (                hConnect,                   // InternetConnect handle      "GET",                      // Method      "",                         // Object name      HTTP_VERSION,               // Version      "",                         // Referrer                NULL,                       // Extra headers      INTERNET_FLAG_SECURE,       // Flags      0                           // Context                );   ...
使用 HTTPS 而不是 HTTP 时,请注意两个差异:
  • InternetConnect 使用 INTERNET_DEFAULT_HTTPS_PORT,而不是 INTERNET_INVALID_PORT_NUMBER 或 INTERNET_DEFAULT_HTTP_PORT
  • 除了所有其他的选项外,HttpOpenRequest 还使用 INTERNET_FLAG_SECURE 选项。
在 HttpOpenRequest 中或在 InternetOpenUrl 中,可以使用以下两个选项以忽略无效的证书错误:

  • INTERNET_FLAG_IGNORE_CERT_CN_INVALID:忽略因服务器的证书主机名与请求的主机名不匹配所导致的错误。
  • INTERNET_FLAG_IGNORE_CERT_DATE_INVALID:忽略由已失效的服务器证书导致的错误。
有关这些标志的更多信息,请参阅 Internet Client SDK 文档。

SSL 和 PCT 功能是由 Schannel.dll 提供的,它在您运行再发布程序 Wintdist.exe 或 Wint351.exe 时会正确安装。有关再发布 Schannel.dll 的信息,请参阅 Redist.txt 或 Axredist.txt。



使用InternetOpenUrl挂起的一个解决方案 (http://blog.csdn.net/rbagglo/article/details/4877800)

在某个同事留下的网络操作代码中,如果用户试图从网络打开一个实际并不存在的URL, 走到InternetOpenUrl这步,应用程序就一命呜呼了。这个API并没有按照文档上说的那样返回一个NULL给文件HANDLE,而是完全停止响应。他尝试使用InternetSetOption来设置超时的时限,代码里这样写

DWORD n = 10000;
InternetSetOption(p->Internet, INTERNET_OPTION_CONNECT_TIMEOUT, &n, sizeof(n));

但没有实际效果。从后文可以猜测,InternetOpenUrl再去调用了网络的IO处理,在IO处理中被卡死,罪过似乎并不在InternetOpenUrl和wininet.dll这层上.

于是他在前面某次工作记录里说已经解决了这个问题,然后又被测试组骂回来说没效果. 我搜到了开源代码里一个很好的算法, 自己测试了下,的确有效。让我们来看下它的算法,确实很精妙. 整个算法的核心是用异步方式来处理wininet.dll提供的那些函数

 



具体实现代码如下:

(1) 首先, 在InternetOpen时修改了参数
   它用的是  InternetOpen(T("MyApp"), p->AccessType, NULL,NULL, INTERNET_FLAG_ASYNC);  
   而会卡死的情况下用的是 InternetOpen(T("AmoiPlayer"),INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
   前面几个参数不是影响该性能的关键,关键点在最后一个参数上: INTERNET_FLAG_ASYNC. 在文档里对这个参数的解释是 Makes only asynchronous requests on handles descended from the handle returned from this function.  写得有些晦涩. 实际效果就是如果在InternetOpen中设置为INTERNET_FLAG_ASYNC, 那么在InternetOpenUrl时就会立刻返回,  如果为NULL就必须有具体的返回值才肯RETRUN.

(2) 注册一个CALLBACK函数  InternetSetStatusCallback(p->Internet, InternetCallback)
      因为我们OpenURL操作会立刻返回了,那么什么时候能得到有效或无效的HANDLE呢? 这就需要一个CALLBACK来做这件事情.  这里当然有必要看下这个CALLBACK函数里做了什么

static void CALLBACK InternetCallback(HINTERNET hInternet,
                               DWORD dwContext,
                               DWORD dwInternetStatus,
                               LPVOID lpvStatusInformation,
                               DWORD dwStatusInformationLength)
{
    if (dwInternetStatus == INTERNET_STATUS_REQUEST_COMPLETE)
    {
        INTERNET_ASYNC_RESULT *Result = (INTERNET_ASYNC_RESULT*)lpvStatusInformation;
        http *p = (http*)dwContext;
        p->CompleteResult = Result->dwResult;
        SetEvent(p->Complete);
    }
}
当等到INTERNET_STATUS_REQUEST_COMPLETE通知的时候, 把RESULT获取下来放在p->CompleteResult里面,然后SetEvent. 也就是说系统CALLBACK线程和其他什么线程进行通信了


(3) 在InternetOpenUrl的时候代码如下:

Result = (DWORD)InternetOpenUrl(p->Internet, URL, s, (DWORD)-1, INTERNET_FLAG_NO_CACHE_WRITE|Secure, (DWORD)p);

p->Handle = (HANDLE)Pending(p, Result , 30);

if (Result || GetLastError() != ERROR_IO_PENDING)
{
  p->Handle = Result;
}
else
{
  p->Pending = 1;
  if ( WaitForSingleObject(p->Complete,300000) == WAIT_OBJECT_0 )
  {
    p->Pending =0;
    p->Handle  = p->CompleteResult;
  }
  else
  {
    p->Handle = NULL;
  }
}


代码中把流程图中的左边线程, 从调用API触发CALLBACK开始, 到底端取得HANDEL这块算法封装为一个函数, 可以被其他对wininet.dll接口函数的操作共用, 如InternetReadFile等.


原创粉丝点击