http主要用在浏览器和web服务器通信,是基于TCP协议的应用层无连接的传输协议。
HTTP与HTTPS
http是明文数据传输,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息。协议使用端口号:80。
https更加安全:数据加密、校验(防止被篡改),建立https网站需要申请ca证书(证明合法性,防止中间人攻击)。协议使用端口号:443
加密方法:对称+非对称加密+证书+数字签名(对比防止证书被修改)
对称加密(对加密和解密使用相同密钥的加密算法):RC4、DES、3DES、AES、ChaCha20 等
非对称加密(公钥加密,私钥解密): DH、DSA、RSA、ECC 等。
访问http的网站:
会被提示不安全。
https的网站:
有一个安全的锁。
HTTP请求
请求头部:
浏览器给服务器发送
每一行结束都有一个\r\n;
头部与数据部分隔一行\r\n,空行表明头部结束了。
请求方法:
GET客户端向服务端请求资源,POST客户端向客户端提交数据(如:自己换头像)
其中:GET、HEAD、TRACE、OPTIONS是安全的。
应答头部:
服务端给浏览器发
HTTP状态码:
1XX信息,告诉服务器自己还有数据发送;
2XX成功,200 OK;
3XX重定向;
4XX客户端错误;
5XX服务器错误;
写一个服务端让浏览器使用ip连接
这里当服务端接收到浏览器的请求后向其发送:“打印ok?“,但是这是不符合它的应答格式的,此时浏览器无法解析,将其应答直接显示出来。
并且接收信息发送后就关闭了,这种为短链接。
头文件:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
创建套接字:
int socket_init()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
assert(sockfd!=-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));
assert(res!=-1);
res = listen(sockfd,5);
assert(res!=-1);
return sockfd;
}
主函数:
服务器收到浏览器请求后返回一个"打印ok?"
int main()
{
int sockfd=socket_init();
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);
if(n<=0)
{
close(c);
continue;
}
printf("%s\n",buff);
send(c,"打印ok?",7,0);
close(c);
}
}
申请1024大小的数组必须用管理员权限运行(不然不会分配这么多空间):
服务端可以收到发送请求的浏览器、系统的型号等等
这些数据都存在接收(recv)的buff中。
GET请求资源:
浏览器指定文件路径,GET后面的与浏览器上输入的东西一样,即把请求送给服务器。
让服务器回复指定数据:
由于请求报文可以得到浏览器申请的数据文件地址,我们可以获取它,然后回一个应答报文。
头文件:
因为要打开文件,所以要加上fcntl.h
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<fcntl.h>
//当前程序的位置
#define PATH "/home/stu/day21"
创建套接字:
int socket_init()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
assert(sockfd!=-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));
assert(res!=-1);
res = listen(sockfd,5);
assert(res!=-1);
return sockfd;
}
获取文件名:
//获取文件名(Get后面的东西)
char * get_filename(char buff[])//报头存在buff中
{
char*s = strtok(buff," ");//分割出请求方法
if(s==NULL)return NULL;
printf("请求方法:%s\n",s);
s = strtok(NULL," ");//分出文件名
return s;
}
主函数:
recv,拼接应答报文
默认的主页为index html
int main()
{
int sockfd=socket_init();
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);
if(n<=0)
{
close(c);
continue;
}
//打印请求
printf("%s\n",buff);
//获取浏览器指定的文件名
char* filename = get_filename(buff);
if(filename==NULL)
{
close(c);//解析不了,直接关闭
continue;
}
//拼接http应答报文
//PATH是自己定义的宏表示当前文件位置,下面要将文件名加到当前路径后
char path[236] = {PATH};
if(strcmp(filename,"/")==0)//filename只有一个/表示没有指定文件
{
strcat(path,"/index.html");// "/home/stu/day21/index.html"
}
else
{
strcat(path,filename);
}
//获取文件大小
int fd = open(path,O_RDONLY);
if(fd==-1)//文件没打开
{
close(c);
continue;
}
int size = lseek(fd,0,SRRK_END); //大小等于文件头到文件末尾的位置
lseek(fd,0,SEEK_SET);//再把偏移量移到文件头
char head[128] = {"HTTP/1.1 200 0K\r\n"};
strcat(head,"Server: myhttp\r\n");
//将内容格式化输出到一个buff中
sprintf(head+strlen(head),"Content-Length: %d\r\n",size);
strcat(head,"\r\n");//空行
//输出头部
printf("\n%s",head);
//给客户端发送头部
send(c,head,strlen(head),0);
//给客户端发送数据
char data[1024]={0};
int num = 0;
while((num=read(fd,data,1024))>0)//读数据
{
send(c,data,num,0);//发送
}
close(fd);
close(c);
}
}
html文件
把这个文件放在浏览器申请的文件夹中,服务器可以把它发给浏览器。
<html>
<head>
<meta charset="utf8">
<title>c2202的主页</title>
</head>
<body>
欢迎
</body>
</html>