我制作了以下Win32套接字程序来浏览网页。我使用 Mingw 来避免对任何运行时的依赖。
要获取 IP 地址,我通过命令提示符 ping 诸如
www.google.com
、www.yahoo.com
之类的 url,然后在我的程序中使用这些 IP 地址。端口当然是80。
我可以使用
"GET /\r\n"
获取 Google、Yahoo 等的默认页面。我还可以获取非默认页面,甚至是目录内的页面,例如使用 http://yasini.com/newsite/index.aspx
来获取 "GET /newsite/index.aspx"
。
程序的输出以从网络服务器接收的 HTML 形式保存在硬盘上。稍后在 FireFox 中打开该文件以查看通信情况。
我做了一个测试网页,
http://a.domaindlx.com/trysite/hello.asp
,我可以在FireFox中打开它。然后我 ping 域 a.domaindlx.com
并获得 IP 地址 66.36.238.30
。然后我尝试使用 "GET /trysite/hello.asp"
访问上述页面,但我得到了这样的响应:
此地址未配置任何网站。
我知道这个响应是由网络服务器发送的,所以我能够连接到网络服务器。问题是网络服务器无法识别我尝试访问的 URL。我使用过不同的网页,HTML 和 ASP,但都无法访问。
当尝试直接在浏览器中使用其 IP 地址打开网站时,我收到相同的
"No website is configured..."
错误。
基本的难题是,为什么这些页面可以通过 FireFox 等浏览器访问,但不能通过我的代码访问?我的代码本质上是一个浏览器,这意味着它在端口 80 处打开与网络服务器的连接。
#include <windows.h>
#include <stdio.h>
WSADATA ws;
int d;
char aa[1000];
struct sockaddr_in a;
SOCKET s;
int li;
void abc(char *p)
{
FILE *fp = fopen("c:\\data.htm", "a+");
fprintf(fp, "%s\n", p);
fclose(fp);
}
_stdcall WinMain (HINSTANCE i, HINSTANCE j, char * k, int l)
{
d = WSAStartup(0x101, &ws);
sprintf(aa, "WSASTARTUP = %d", d);
abc(aa);
s = socket(AF_INET, SOCK_STREAM, 0);
sprintf(aa, "SOCKET = %d", s);
abc(aa);
a.sin_family = AF_INET;
a.sin_port = htons(80);
//a.sin_addr.s_addr = inet_addr("74.125.236.145");
a.sin_addr.s_addr = inet_addr("66.36.238.30"); //a.domaindlx.com
//a.sin_addr.s_addr = inet_addr("206.225.85.18"); //www.domaindlx.com
//a.sin_addr.s_addr = inet_addr("87.248.122.122"); //www.yahoo.com
//a.sin_addr.s_addr = inet_addr("72.167.153.9"); //www.yasini.com
d = connect(s, (struct sockaddr *) &a, sizeof(a));
strcpy(aa, "GET /trysite/hello.asp\r\n");
strcat(aa, "HTTP 1.0 \r\n\r\n");
send(s, aa, sizeof(aa), 0);
li = 1;
while(li != 0)
{
li = recv(s, aa, 1000, 0);
abc(aa);
}
}
您的代码有两个问题。
第一个问题是
\r\n
之前应该有一个空格,而不是HTTP 1.0
。 如果没有这个,您将发送 HTTP 0.9。
第二个问题是某些 IP 地址用于托管多个站点,需要发送
Host
标头。
如果您添加
Host
标头,告诉您“此地址没有配置网站”的网站可能会工作得更好。 您对该网站的请求应如下所示:
"GET /trysite/hello.asp HTTP 1.0\r\nHost: a.domaindlx.com\r\n\r\n"
许多网络服务器(尤其是提供共享托管的网络服务器)可以在同一物理 IP 上托管多个帐户,因此它们需要知道正在请求哪个特定网站才能访问正确的帐户。 要指定目标网站,您需要在 HTTP 请求中包含
Host
标头。
Host
标头在 HTTP 1.0(您正在使用的)中是可选的,但实际上在 HTTP 1.1 及以后版本中是必需的,否则请求将失败。
另请注意,当您调用
send()
发送请求时,您正在发送 aa
缓冲区的全部 1000 个字节,这是错误的。 您只需发送您实际填写的内容即可。
最后,一般来说,您并没有真正很好地管理套接字。 您需要更好的错误处理。
试试这个:
#include <windows.h>
#include <stdio.h>
void abc(char *p, int l = -1)
{
FILE *fp = fopen("c:\\data.htm", "a+");
if (fp)
{
if (l == -1) l = strlen(p);
fwrite(p, 1, l, fp);
fclose(fp);
}
}
int WINAPI WinMain (HINSTANCE i, HINSTANCE j, char * k, int l)
{
char aa[1000];
WSADATA ws;
int d = WSAStartup(0x101, &ws);
sprintf(aa, "WSASTARTUP = %d\n", d);
abc(aa);
if (d == 0)
{
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
sprintf(aa, "SOCKET = %d\n", s);
abc(aa);
if (s != INVALID_SOCKET)
{
char *host = "a.domaindlx.com";
char *file = "/trysite/hello.asp";
struct sockaddr_in a;
memset(&a, 0, sizeof(a));
a.sin_family = AF_INET;
a.sin_port = htons(80);
struct hostent *h = gethostbyname(host);
if (!h)
{
sprintf(aa, "gethostbyname(\"%s\") FAILED\n", host);
abc(aa);
}
else
{
sprintf(aa, "gethostbyname(\"%s\") TYPE = %d\n", host, h->h_addrtype);
abc(aa);
if (h->h_addrtype == AF_INET)
{
a.sin_addr = * (struct in_addr*) h->h_addr;
sprintf(aa, "gethostbyname(\"%s\") IP = %s\n", host, inet_ntoa(a.sin_addr));
abc(aa);
d = connect(s, (struct sockaddr *) &a, sizeof(a));
sprintf(aa, "CONNECT = %d\n", d);
abc(aa);
if (d == 0)
{
sprintf(aa,
"GET %s HTTP/1.0\r\n"
"Host: %s\r\n"
"Connection: close\r\n"
"\r\n",
file, host);
char *p = aa;
int t = strlen(aa);
int li;
do
{
li = send(s, p, t, 0);
if (li < 1)
break;
p += li;
t -= li;
}
while (t > 0);
if (t != 0)
{
abc("SEND FAILED\n");
}
else
{
abc("SEND OK\n");
do
{
li = recv(s, aa, sizeof(aa), 0);
if (li < 1)
break;
abc(aa, li);
}
while (true);
}
}
}
}
closesocket(s);
}
WSACleanup();
}
return 0;
}
我强烈建议您使用数据包嗅探器,例如Wireshark。 然后您可以准确地看到网络浏览器(或任何其他套接字应用程序)实际发送和接收的内容。 然后您可以根据需要在代码中进行匹配。