首次将测试 程序贴出如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void showHostent(struct hostent *h)
{
printf ("h_name: %s\n", h->h_name);
printf ("h_aliases: %s\n", h->h_aliases[0]);
printf ("h_addrtype: %d\n", h->h_addrtype);
printf ("h_length: %d\n", h->h_length);
struct in_addr *inaddr;
inaddr = (struct in_addr *) h->h_addr;
printf ("IP Address: %x\n", inaddr->s_addr);
}
int
htconnect (char *domain, int port)
{
int white_sock;
struct hostent *site;
struct sockaddr_in me;
site = gethostbyname (domain);
if (site == NULL)
return -2;
white_sock = socket (AF_INET, SOCK_STREAM, 0);
if (white_sock < 0)
return -1;
memset (&me, 0, sizeof (struct sockaddr_in));
memcpy (&me.sin_addr, site->h_addr_list[0], site->h_length);
me.sin_family = AF_INET;
me.sin_port = htons (port);
showHostent(site);
return (connect
(white_sock, (struct sockaddr *) &me,
sizeof (struct sockaddr)) < 0) ? -1 : white_sock;
}
int
htsend (int sock, char *fmt, ...)
{
char BUF[1024];
va_list argptr;
va_start (argptr, fmt);
vsprintf (BUF, fmt, argptr);
va_end (argptr);
return send (sock, BUF, strlen (BUF), 0);
}
void
writeio (int sockfd)
{
const char *filename = "tmp.html";
char buff[3];
int fd = open (filename, O_RDWR | O_CREAT);
if (!fd)
{
printf ("error!\n");
return;
}
chmod (filename, S_IROTH | S_IWOTH);
while (read (sockfd, buff, 1) > 0)
{
// printf("%c", buff[0]);
write (fd, buff, 1);
}
close (fd);
}
void
main (int argc, char **argv)
{
int black_sock;
char bugs_bunny[3];
if (argc < 2)
return;
black_sock = htconnect (argv[1], 80);
if (black_sock < 0)
return;
htsend (black_sock, "GET / HTTP/1.0%c", '\n');
htsend (black_sock, "Host: %s%c", argv[1], 10);
htsend (black_sock, "%c", 10);
writeio (black_sock);
close (black_sock);
}
先对程序中的站位符%c之后的10说明下,在c语言中,所有的字符都是占用一个字节,其实可以用十进制的整形量表示,控制字符也是如此,例如'\n'的 换行符,在c语言下,用十进制表示 ,便是10,这点容易对刻读性是种破坏,不过有人就是爱这么用在语法上也没错误 。
接着研究上面的测试程序,当从控制台获取域名url后,便利用gethostbyname得到域名的相关信息,其中最重要的便是h_addr, 也就是h_addr_list[0], 这个值是个char *地址变量,可以强制转换为(struct in_addr *)的指针变量, 因为struct in_addr结构中只存在一个可以认为是整形的s_addr的变量,那么按照结构体的构成规则,该结构体的首地址和第一个成员的地址是相同的,经过转换后,也就构成了唯一的成员s_addr的地址,这是一个整形量。
最初用来描诉网路地址的结构体是sturct sockaddr, 但后来为了方便,用了结构体struct sockaddr_in, 两种结构体的定义如下:
struct sockaddr结构类型是用来保存socket信息的:
struct sockaddr {
unsigned short sa_family; /* 地址族, AF_xxx */——地址的格式
char sa_data[14]; /* 14 字节的协议地址 */——地址值(IP和端口号)
};
struct sockaddr_in {
short int sin_family; /* 地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */
};
因为历史 的遗迹,为了与以往的程序 兼容,在bind函数,accept函数,connect函数中,所有的绑定地址用的都是(struct sockaddr *)参数,在使用时候需要进行强制转换。
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
为了方便,一般使用的是struct sockaddr_in结构体, 其中需要注意的是sin_addr成员,这是一个为struct in_addr的变量,看到联系了吗?!struct in_addr结构体啊!利用gethostbyname 获取的struct hostent结构体中的h_addr_list[0]成员,正是 struct in_addr结构体的地址,也就是指针,可以通过强制类型转换将得到的h_addr_list[0]复制给 &sin_addr, 这样,域名的ip地址便能够被赋值。端口号可以直接指定,http协议默认的端口号为80, sin_family地址族一般被 赋值为AF_INET, 也就是ipv4协议,其它值通过man socket可以查到如下:
Name Purpose Man page
AF_UNIX, AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
AF_IPX IPX - Novell protocols
AF_NETLINK Kernel user interface device netlink(7)
AF_X25 ITU-T X.25 / ISO-8208 protocol x25(7)
AF_AX25 Amateur radio AX.25 protocol
AF_ATMPVC Access to raw ATM PVCs
AF_APPLETALK Appletalk ddp(7)
AF_PACKET Low level packet interface packet(7)
值得注意的是,在向服务器发送
htsend(black_sock,"GET / HTTP/1.0%c",10);
htsend(black_sock,"Host: %s%c",argv[1],10);
htsend(black_sock,"%c",10);
之后,服务器返回的字节流,在此我将执行./tes.bin 之后,服务器返回的字节流贴出来分析一下
<pre name="code" class="html">HTTP/1.1 200 OK
Date: Sun, 29 Jun 2014 10:12:35 GMT
Content-Type: text/html; charset=utf-8
Connection: Close
Vary: Accept-Encoding
Set-Cookie: BAIDUID=49BE07972221EFE6E02CF72A5BFC577D:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: H_PS_PSSID=6994_1428_5223_6995_7343_6505_7232_6017_7202_6931_7306_7134_7387_6983; path=/; domain=.
P3P: CP=" OTI DSP COR IVA OUR IND COM "
Cache-Control: private
Cxy_all: baidu+c182868b5e39f1c2a71dfbfd4412713b
Expires: Sun, 29 Jun 2014 10:11:59 GMT
X-Powered-By: HPHP
Server: BWS/1.1
BDPAGETYPE: 1
BDQID: 0xea966d2c00143a68
BDUSERID: 0
<!DOCTYPE html><!--STATUS OK--><html><head><meta http-equiv="content-type" content="text/html;charset=utf-8"><link rel="dns-prefetch" href="//"/><link rel="dns-prefetch" href="//t1."/><link rel="dns-prefetch" href="//t2."/><link rel="dns-prefetch" href="//t3."/><link rel="dns-prefetch" href="//t10."/><link rel="dns-prefetch" href="//t11."/><link rel="dns-prefetch" href="//t12."/><title>百度一下,你就知道</title><style >html,body{height:100%}html{overflow-y:a
<!-- 省略些html代码-->
0t>try{document.cookie="WWW_ST=;expires=Sat, 01 Jan 2000 00:00:00 GMT";$(document.forms[0]).on("submit",function(){var _t=new Date().getTime();document.cookie = "WWW_ST=" + _t +";expires=" + new Date(_t + 10000).toGMTString()})}catch(e){}</script></body></html>
ipt></body></html>
在数据流的最开始的部分,是http协议的头信息,由服务器生成,包含了服务器的一些信息,目前我对http协议还不是特别精通,建议查阅相关文档弄明白各个字段的含义:
HTTP/1.1 200 OK
Date: Sun, 29 Jun 2014 10:12:35 GMT
Content-Type: text/html; charset=utf-8
Connection: Close
Vary: Accept-Encoding
Set-Cookie: BAIDUID=49BE07972221EFE6E02CF72A5BFC577D:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: H_PS_PSSID=6994_1428_5223_6995_7343_6505_7232_6017_7202_6931_7306_7134_7387_6983; path=/; domain=.
P3P: CP=" OTI DSP COR IVA OUR IND COM "
Cache-Control: private
Cxy_all: baidu+c182868b5e39f1c2a71dfbfd4412713b
Expires: Sun, 29 Jun 2014 10:11:59 GMT
X-Powered-By: HPHP
Server: BWS/1.1
BDPAGETYPE: 1
BDQID: 0xea966d2c00143a68
BDUSERID: 0
有一点很值得注意,我以往写过的第一版服务器获得的日志记录里,浏览器给服务发送的信息如下:
GET /login.html HTTP/1.1
Host: 127.0.0.1:3355
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36
DNT: 1
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en,zh-CN;q=0.8,zh;q=0.6
对比下最开始的测试程序发送给服务器的字节流
htsend (black_sock, "GET / HTTP/1.0%c", '\n');
htsend (black_sock, "Host: %s%c", argv[1], 10);
htsend (black_sock, "%c", 10);
将"%c", 10理解为'\n'换行符,明白了吧,http协议就是如此,对比下两个的第一行! GET字符之后! '/'i字符之后,便是请求的url.