HTTP协议通信

HTTP协议介绍

HTTP(超文本传输协议)属于应用层协议,但是它在传输层使用的TCP协议,也就是说HTTP协议也是对TCP协议的一种应用,HTTP协议不光能传输本文信息,也能对视频、声音、图片都可以进行传输

浏览器与服务器通信过程

左边是我们的web服务器,右边是我们的浏览器,我们通过浏览器来访问一个网址,web服务器与浏览器直接传输层通信是TCP协议,也就是说需要有三次握手建立连接以及四次挥手断开连接
在这里插入图片描述
我们进行TCP连接需要被连接主机的IP以及端口号,而我们现在只有一个网址该如何进行连接呢?这需要我们首先通过DNS服务器对网址进行解析,得到ip与端口进行connect()进行连接
在这里插入图片描述
等TCP连接建立完成,浏览器就会向服务器发送HTTP请求报文,web服务器收到HTTP的请求报文,就会回复HTTP应答报文,就像客户端向服务器发送一个“hello”,服务器向客户端回复一个“ok”
在这里插入图片描述

HTTP的长连接与短连接

  • 如果我们在HTTP应答报文发送过后执行close()断开连接,这就叫HTTP的短连接,短连接需要不断的进行TCP连接,每次HTTP应答都使用了不同的TCP连接
  • 如果我们在HTTP应答报文发送过后不断开连接,并且浏览器继续发送HTTP的请求,服务器也再向浏览器发送HTTP应答,再两次及以上的HTTP请求用了同一个TCP连接,我们就叫做它长连接,长连接更有利于我们对网络资源的使用

HTTP请求方法和应答状态码

下面是一个不完整的示例:
在这里插入图片描述

  • GET方法,需要获取的资源 以及协议版本
  • 客户端,浏览器的名字
  • 访问的主机名
  • 长连接还是短连接
    如果我们是一个长连接的话就是keep-alive,短连接是close

请求方法

在这里插入图片描述
例如:

  • GET方法就是向web服务端以只读的方式,将我们需要的内容呈现出来,并不会对服务器产生什么影响
  • HEAD方法只需要返回应答的头部信息,而不需要数据部门的信息
  • POST方法是客户端向服务器提交数据,就像我们在网页中输入信息点击提交向服务器发送数据

应答状态码

HTTP应答的部分内容如下:

  • HTTP协议版本,状态码(200 ok)
  • 服务器名字
  • 数据部分长度
  • 类型等
  • 后面才是数据内容
    在这里插入图片描述
  • 100 代表收到请求行与头部,告诉客户端继续发送数据
  • 200 请求成功
  • 3xx 资源重定向
  • 4xx 客户端错误(404资源找不到、403无权限访问)
  • 5xx 服务端错误

自己实现简单的HTTP服务器

我们先写这样一段代码来看看,当浏览器访问服务器发送的报文,以及服务器向浏览器回复“hello”会发送什么

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>


int create_socket();
int main()
{
	int sockfd = create_socket();
	assert(sockfd != -1);

	while(1)
	{
		struct sockaddr_in caddr;
		int len = sizeof(caddr);
		
		int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
		if(c<0)
		{
			continue;
		}

		char buff[1024] = {0}; //用于接受浏览器发来的报文
		int n = recv(c,buff,1023,0);
		printf("n=%d,read:\n%s",n,buff);

		send(c,"hello",5,0);
		close(c);
	}
		
}
int create_socket()
{
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1)
	{
		return -1;
	}

	struct sockaddr_in saddr;
	memset(&saddr,0,sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(80);//http协议专用端口
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
	if(res == -1)
	{
		return -1;
	}

	res = listen(sockfd,5);
	if(res == -1)
	{
		return -1;
	}

	return sockfd;
}

编译运行代码,会显示错误,这是因为我们使用的80号端口,在Linux上小于1024的端口属于系统保留端口,只有管理员权限才能使用

在这里插入图片描述
切换至管理员执行成功,代码阻塞等待浏览器的连接
在这里插入图片描述
我们打开浏览器输入“127.0.0.1”本机测试ip,可以看到来自服务器回复的“hello”,我们没有向浏览器回复正确格式的应答报文,所以浏览器将它直接打印出来
在这里插入图片描述
回到终端可以看到来自浏览器的请求报文
在这里插入图片描述
我们接着组装一个正确的回复报文,增加下面的代码

	char sendbuff[512] = {0};//回复报文
	strcpy(sendbuff,"HTTP/1.1 200 ok\r\n");//http版本 应答状态码
	strcat(sendbuff,"Server: myhttp\r\n");//服务器名称
	strcat(sendbuff,"Content-Length: 5\r\n");//数据长度
	strcat(sendbuff,"\r\n");//分割头部与数据部分
	strcat(sendbuff,"hello");
	
	printf("send:\n%s\n",sendbuff);
	send(c,sendbuff,strlen(sendbuff),0);

在这里插入图片描述
运行并且通过浏览器进行访问
在这里插入图片描述
这里的hello不同于上面的hello,我们可以通过查看页面源代码进行查看
在这里插入图片描述
在这里插入图片描述
我们现在修改代码,解析客户端请求方法以及需要访问的哪个文件

	char *s = strtok(buff," ");
	if(s == NULL)
	{
		close(c);
		continue;
	}
	printf("请求方法:%s\n",s);
	s = strtok(NULL," ");
	if(s == NULL)
	{
		close(c);
		continue;
	}
	printf("请求的资源:%s\n",s);

在这里插入图片描述
运行可以看到,以及成功分析了请求方法以及请求的资源
在这里插入图片描述
现在我们写一个简易的index.html文件,提供浏览器访问

<html>
	<head>
		<meta charset = utf-8>
		<title>这是测试页面</title>
	</head>
	<body>
		<h3>欢迎访问
			<br>
			<hr>
	</body>
</html>

对代码也进行修改,将该文件发送至浏览器

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<fcntl.h>

int create_socket();
int main()
{
	int sockfd = create_socket();
	assert(sockfd != -1);

	while(1)
	{
		struct sockaddr_in caddr;
		int len = sizeof(caddr);
		
		int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
		if(c<0)
		{
			continue;
		}

		char buff[1024] = {0}; //用于接受浏览器发来的报文
		int n = recv(c,buff,1023,0);
		printf("n=%d,read:\n%s",n,buff);

		char *s = strtok(buff," ");
		if(s == NULL)
		{
			close(c);
			continue;
		}
		printf("请求方法:%s\n",s);
		s = strtok(NULL," ");
		if(s == NULL)
		{
			close(c);
			continue;
		}
		printf("请求的资源:%s\n",s);
		if(strcmp(s,"/")==0)
		{
			s = "/index.html";
		}

		char path[128] = {"/home/zyq/Linux/c208/http"};
		strcat(path,s);

		int fd = open(path,O_RDONLY);
		if(fd == -1)
		{
			send(c,"404",3,0);
			close(c);
			continue;
		}


		int size = lseek(fd,0,SEEK_END);
		lseek(fd,0,SEEK_SET);

		char sendbuff[512] = {0};//回复报文
		strcpy(sendbuff,"HTTP/1.1 200 ok\r\n");//http版本 应答状态码
		strcat(sendbuff,"Server: myhttp\r\n");//服务器名称
		sprintf(sendbuff+strlen(sendbuff),"Content-Length: %d\r\n",size);//数据长度
		strcat(sendbuff,"\r\n");//分割头部与数据部分

		printf("send:\n%s\n",sendbuff);
		send(c,sendbuff,strlen(sendbuff),0);
		
		char data[512] = {0};
		int num = 0;
		while((num = read(fd,data,512)) >0 )
		{
			send(c,data,num,0);
		}
		close(fd);
		close(c);
	}
		
}
int create_socket()
{
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1)
	{
		return -1;
	}

	struct sockaddr_in saddr;
	memset(&saddr,0,sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(80);
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
	if(res == -1)
	{
		return -1;
	}

	res = listen(sockfd,5);
	if(res == -1)
	{
		return -1;
	}

	return sockfd;
}

我们运行代码,并且在浏览器查看
在这里插入图片描述
在这里插入图片描述
我们可以通过修改index.html文件来改变浏览器访问服务器得到的内容

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值