Linux 网络编程 全解(十一)--------HTTP协议以及用socket实现访问HTTP服务器

写在前面:本文注意解释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 <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netdb.h>
#include <string.h>
#include<stdlib.h>
#include<errno.h>

#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;
}

测试效果:

 

 

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KiranWang

一起努力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值