我正在创建一个HTTP客户端,它根据命令行参数下载网页。它接受参数,查找域名以获取IP地址,创建套接字,连接到服务器并发送GET请求并等待回复。这一切都很好但是当我使用缓冲区和while循环读取我的回复时,我也收到一些不可读的字符。如果您运行代码并查看html,您将在页面上看到不可读的字符。
我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int socket_desc, i, bytes_read;
char server_reply[1024], ip[100], request[100];;
char *hostname = argv[1];
struct sockaddr_in server;
struct hostent *he;
struct in_addr **addr_list;
FILE *fp;
if ((he = gethostbyname(hostname)) == NULL) {
//gethostbyname failed
herror("gethostbyname\n");
return 1;
}
addr_list = (struct in_addr **) he->h_addr_list;
for(i = 0; addr_list[i] != NULL; i++) {
//Return the first one;
strcpy(ip , inet_ntoa(*addr_list[i]) );
}
//Create socket
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1) {
printf("Could not create socket!\n");
}
server.sin_addr.s_addr = inet_addr(ip);
server.sin_family = AF_INET;
server.sin_port = htons(80);
//Connect to remote server
if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) {
printf("connect error!\n");
return 1;
}
printf("Connected...\n");
//Send some data
snprintf(request, 99, "GET / HTTP/1.1\r\n"
"Host: %s\r\n"
"\r\n\r\n", hostname
);
if (send(socket_desc, request, strlen(request), 0) < 0) {
puts("Send failed!\n");
return 1;
}
puts("Data Sent...\n");
//Receive a reply from the server
fp = fopen("/home/localusr/Desktop/ouput.html", "w+");
while (bytes_read = read(socket_desc, server_reply, sizeof(server_reply)) > 0) {
fputs(server_reply, fp);
memset(server_reply, 0, sizeof(server_reply));
}
do {
bytes_read = read(socket_desc, server_reply, sizeof(server_reply));
fputs(server_reply, fp);
memset(server_reply, 0, sizeof(server_reply));
} while (bytes_read > 0);
printf("reply received...\n");
fclose(fp);
close(socket_desc);
return 0;
}
抱歉这段代码很糟糕。任何帮助非常感谢。我正在使用Ubuntu机器并使用gcc编译我的代码。
编辑:
orb.ws.require.lib--> <script type="text/javascript">/*
be2
be2不应该在那里。 *也获得'@'符号
答案 0 :(得分:1)
编辑: 要在此处发表评论:
请注意,例如www.bbc.co.uk响应标题说“Transfer-Encoding:chunked”,这意味着每个块的长度都是十六进制数字,后跟数据后跟\ r \ n。
也就是说,根据你的例子:
be2\r\n => 0xbe2\r\n => 3042\r\n
或“此处跟随3042字节”(在\ r \ n又称CRLF或十六进制0d0a
之后)。
Example块:
e\r\nStack Exchange
| | ||||||||||||||
| | +............+
| | |
| | +-------- 14 bytes
| +----------------- \r\n
+-------------------- 0x0e == 14 dec in hex
<强> 旧: 强>
您可以通过以下方式正确终止读取字节,而不是memset
等。
while ( (bytes_read = read(socket_desc, server_reply, sizeof(server_reply) - 1)) > 0) {
server_reply[bytes_read] = 0x00;
此后bytes_read
之外的任何内容都不会fputs
。
当您将memset
整个缓冲区移至0
但 读入整个缓冲区时 - 除非读取次数少于缓冲区,否则memset无效尺寸。您只需在完整(1024)读取时覆盖所有零,然后写入1024 +垃圾直到第一个零。
read()
返回读取的字节数。通过将server_reply[bytes_read]
设置为0
,您实际上会终止实际数据。将它变成C字符串。如果不将最后一个字节设置为零,fputs()
将在bytes_read
之后继续输出垃圾,直到第一个零或崩溃。
换句话说; read()
读取最多size
个字节,如果全部为零字节则不关心。如果告诉read()
读取356GiB数据,文件描述符提供356GiB的零(如0x00字节,而不是ASCII 0) - 这就是你得到的。
您的套接字 不 以0结束传递。它像服务器一样提供零字节作为数据的一部分。假设您使用零字节传输图像或其他数据;换句话说:它不是一个零终止的字符串read()
得到的。
另请注意- 1
之后的sizeof
- 为空字节腾出空间。
fputs 写入,直到第一次终止空字节,但不包括在输出中(如果您正在编写缓冲的 string <,这通常是您想要的/强>数据)。
char buf[8];
Char未初始化且包含垃圾。例如,它可能是:
buf[0] == 0x13
buf[1] == 0x0a
buf[2] == 0x00
buf[3] == 0x65
buf[4] == 0x78
buf[5] == 0xf3
buf[6] == 0x00
buf[7] == 0xaf
超出buf你有随机垃圾,例如
buf[7+1] == 0xde
buf[7+2] == 0xa0
buf[7+3] == 0x33
buf[7+3] == 0x00
bytes_read = read(soc,buf,8); soc提供:'ABCDEFG'
缓冲区现在是:
buf[0] == 0x41 (A)
buf[1] == 0x42 (B)
buf[2] == 0x43 (C)
buf[3] == 0x44 (D)
buf[4] == 0x45 (E)
buf[5] == 0x46 (F)
buf[6] == 0x47 (G)
buf[7] == 0xaf (H)
但buf[7]
以外的字节仍然充满了垃圾;并且你的fputs()
将会读取并将数据传递给文件,直到第一个零。
这就是为什么你改为说:
bytes_read = read(soc, buf, 7);
buf[bytes_read] = 0x00;
现在我们只读A-G。最后一个字节设置为0。
此处fputs(buf, fh)
要先写\0
,换句话说ABCDEFG
。
如果服务器现在在下次运行中提供,例如,只提供两个字节:
buf[0] == 0x48 (H)
buf[1] == 0x5A (Z)
然后bytes_read
将为2,声明为:
buf[bytes_read] = 0x00 ===> buf[2] = 0x00
给你
buf[0] == 0x48 (H)
buf[1] == 0x5A (Z)
buf[2] == 0x00 (0x00) <<--- nulled out
+---.
buf[3] == 0x44 (D) | \
buf[4] == 0x45 (E) | \
buf[5] == 0x46 (F) | }--->>> garbage from previous read.
buf[6] == 0x47 (G) | /
buf[7] == 0x00 (0x00) | /
+---/
此处fputs(buf, fh)
要先写\0
,换句话说HZ
。
答案 1 :(得分:0)
您是否尝试使用telnet访问网页?
请执行以下操作:
telnet [hostname] [port]
在telnet shell中输入:
GET / HTTP/1.1
Host: [hostname]
<return>
(介意主持人之后的额外回报!
请发布telnet的结果和代码中的结果
发现问题:
您使用fputs而不是fwrite。 fputs需要一个字符串,它通过查找NULL字符来检测。
但是,在你的情况下,不承诺这样的NULL字符,所以你必须明确。 作为奖励,您的程序现在终止并刷新输入到文件。 修复:
使用以下do while循环替换你的while和while循环:
do
{
int write;
bytes_read = read(socket_desc, server_reply, sizeof(server_reply));
write = fwrite(server_reply, 1, bytes_read, fp);
printf("Written %d bytes_read: %d\n", write, bytes_read);
memset(server_reply, 0, sizeof(server_reply));
fflush(fp);
} while (bytes_read > 0); // This termination is wrong! You should look at Content-Length from the server's reply to detect the actual length
现在有效....
答案 2 :(得分:0)
read()
不会终止字节。但fputs()
取决于空终止,因此如果要将其传递给fputs()
,则必须在char数组的末尾附加0x00。