写在前面:本文注意解释http协议的格式,主要包括以下内容:HTTP概述、HTTP特点、URL、HTTP报文格式以及用sockt实现访问http服务器。
正文:
一、HTTP协议概述
1、HTTP协议是Hyper Text Transfer Protocol (超文本传输协议)的缩写,HTTP基于TCP/IP协议来传输数据,HTTP工作于CS架构之上,即浏览器作为HTTP客户端,通过URL向HTTP服务器即WEB服务器发送请求,WEB服务器收到请求之后,向HTTP客户端发送响应消息。
2、HTTP默认端口号是80,HTTPS默认端口号是443.
二、HTTP特点:
1、无连接:无连接的含义是限制每次连接只处理一个请求,即服务器处理客户端的请求,收到客户端的应答后,即断开连接,采用这种方式可以节省传输时间。
2、无状态:无状态是指协议对于事物处理没有记忆能力,如果后面的处理依赖前面信息,则必须重传,这样可能导致传输的数据量会增大。
3、客户端向服务器请求时,只需要传送请求方法和路径。常用的请求方法有GET、HREAD、POST等。
三、URL介绍:
1、URI(Uniform Resource Identifiers )统一资源标识符,HTTP协议使用URI来传输数据和建立连接,URL是一种特殊类型的URI。
2、URL(Uniform Resource Locator)统一资源定位符,互联网上用来标识某一处资源的地址。下面用一例子说明URL的格式:
http://www.aspxfans.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name
从上面的URL可以看出,一个完整的URL包括以下几部分:
1.协议部分:该URL的协议部分为“http:”,这代表网页使用的是HTTP协议。在Internet中可以使用多种协议,如HTTP,FTP等等本例中使用的是HTTP协议。在"HTTP"后面的“//”为分隔符。
2.域名部分:该URL的域名部分为“www.aspxfans.com”。一个URL中,也可以使用IP地址作为域名使用
3.端口部分:跟在域名后面的是端口,域名和端口之间使用“:”作为分隔符。端口不是一个URL必须的部分,如果省略端口部分,将采用默认端口
4.虚拟目录部分:从域名后的第一个“/”开始到最后一个“/”为止,是虚拟目录部分。虚拟目录也不是一个URL必须的部分。本例中的虚拟目录是“/news/”
5.文件名部分:从域名后的最后一个“/”开始到“?”为止,是文件名部分,如果没有“?”,则是从域名后的最后一个“/”开始到“#”为止,是文件部分,如果没有“?”和“#”,那么从域名后的最后一个“/”开始到结束,都是文件名部分。本例中的文件名是“index.asp”。文件名部分也不是一个URL必须的部分,如果省略该部分,则使用默认的文件名
6.锚部分:从“#”开始到最后,都是锚部分。本例中的锚部分是“name”。锚部分也不是一个URL必须的部分
7.参数部分:从“?”开始到“#”为止之间的部分为参数部分,又称搜索部分、查询部分。本例中的参数部分为“boardID=5&ID=24618&page=1”。参数可以允许有多个参数,参数与参数之间用“&”作为分隔符。
四、HTTP协议报文格式
1、请求消息Resuest
客户端发送HTTP请求到服务器的请求消息格式如下:
请求行(request line)、请求头部(header)、空行和请求数据四个部分组成。
2、相应消息Response
HTTP响应消息也有四个部分组成,状态行、消息包头、空行、响应报文。
所以,总结以上Requst和Response一般如下状态:
3、HTTP返回状态码:
4、HTTP请求方法:
五 、用socket实现访问百度服务器,同样代码尽可能注释
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define HTTP_PORT (80) //http的固定端口号:80
//struct hostent 类型结构体
// struct hostent {
// char *h_name; /* official name of host */
// char **h_aliases; /* alias list */
// int h_addrtype; /* host address type */
// int h_length; /* length of address */
// char **h_addr_list; /* list of addresses */
// }
int main(void)
{
int sockfd = -1;
int ret = -1;
struct sockaddr_in ser_ip;
char *p_hostName = "www.baidu.com"; //要访问的服务器域名地址
struct hostent * p_Host = NULL;
char serIp[20] = {0};
char recv_packet[1024] = {0};
int size_recv, total_size = 0;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
//设置要访问的服务器的地址结构
memset(&ser_ip,0,sizeof(ser_ip));
ser_ip.sin_family = AF_INET;
ser_ip.sin_port = htons(HTTP_PORT);
p_Host = gethostbyname(p_hostName);//gethostbyname()函数的作用:
//输入主机域名地址,返回包含主机名字和地址
//信息的hostent结构的指针
strcpy(serIp,inet_ntoa(*(struct in_addr *)p_Host->h_addr_list[0]));
printf("host name :%s\n",p_Host->h_name);
printf("host ip : %s\n",serIp);
ser_ip.sin_addr.s_addr = inet_addr(serIp);
//连接服务器
ret = connect( sockfd, (const struct sockaddr *)&ser_ip,sizeof(ser_ip));
if(ret < 0)
{
printf("connect error\n");
return -1;
}
printf("connected success\n");
//按照http协议组GET包
char *p_message= "GET / HTTP/1.1\r\nHost: www.baidu.com\r\n\r\n";
//调用send 向服务器发送http协议包
send( sockfd, p_message, strlen(p_message), 0);
//设置超时时间
struct timeval timeout = {3, 0};
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));//SO_RCVTIMEO 类型,设置超时
//接受服务器响应数据
while(1)
{
memset(recv_packet , 0 , 512); //clear the variable
//获取数据
if ((size_recv = recv(sockfd, recv_packet, 512, 0) ) == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
printf("recv timeout ...\n");
break;
} else if (errno == EINTR) {
printf("interrupt by signal...\n");
continue;
} else if (errno == ENOENT) {
printf("recv RST segement...\n");
break;
} else {
printf("unknown error: %d\n", errno);
exit(1);
}
} else if (size_recv == 0) {
printf("peer closed ...\n");
break;
} else {
total_size += size_recv;
printf("%s" , recv_packet);
}
}
close(sockfd);
return 0;
}
测试效果: