浏览器与服务器通信过程
浏览器与Web服务器再应用层通信使用的是HTTP协议,而HTTP协议在传输层使用的是TCP协议。那么浏览器需要和web服务器进行三次握手建立连接之后才能发送HTTP请求报文,服务器收到请求保温,向浏览器回复HTTP应答报文。在发起连接之前需要得到服务器的IP以及端口。用户在浏览器中输入网址,浏览器会通过DNS服务查询获取到服务器的IP地址。对于端口来讲,使用HTTP协议的程序一般默认使用80端口。
浏览器服务器建立连接之后,如果两次以上的请求复用同一个TCP连接,则称之为长连接。如果浏览器发送一次请求报文,服务器回复一次应答就断开连接,下次交互重新进行三次握手建立连接,这种称之为短连接。现如今大多数为长连接,可以减少网络中的同步报文,也使得服务器的响应速度变快。
HTTP的请求报头
HTTP请求报头结构
GET/index.html HTTP/1.0\r\n 请求方法 请求页面 协议版本
User-Agent:Wget/1.12\r\n 客户端应用程序
Host:192.168.141.128\r\n 目标主机
Connection:close\r\n 连接方式(close/keep-alive)
HTTP的请求方法
请求方法 | 含义 |
---|---|
GET | 申请获取资源,而不对服务器产生任何影响 |
HEAD | 和GET方法类似,不过仅要求服务器返回头部信息,而不需要传输任何实际内容 |
POST | 客户都安向服务器提交数据的方法.这种方法会影响服务器:服务器可能根据收到的数据动态创建新的资源,也可能更新原有的资源 |
PUT | 上传某个资源 |
DELETE | 删除某个资源 |
TRACE | 要求目标服务器返回原始HTTP请求的内容.他可用来查看中间服务器对HTTP请求的影响 |
OPTIONS | 查看服务器对某个特定URL都支持那些请求方法.也可以把URL设置为*,从而获取服务器支持的所有请求方法 |
CONNECT | 用于某些代理服务器,他们能把请求的连接转化为一个安全隧道 |
PATCH | 对某个资源做部分修改 |
HTTP应答报头
HTTP应答报头结构
应答状态
状态码和状态信息 | 含义 |
---|---|
100 Continue | 服务器收到了客户端的请求行和头部信息,告诉客户端继续发送数据部分,客户端通常要先发送Expect:100-continue 头部字段告诉服务器自己还有数据要发送 |
200 OK | 请求成功 |
400 Bad Request | 通用客户请求错误 |
403 Forbidden | 访问被服务器禁止,通常是由于客户端没有权限访问该资源 |
404 Not Found | 资源没找到 |
web服务器的c语言实现
Web 服务器对应的文件是 myHttp.c,代码示例如下, 其中使用到的页面文件(.html)需要
用户自己提供,并且和程序在同一个位置:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <fcntl.h>
#define PATH "/home/xin/kd1"
#define DFL_FILENAME "/index.html"//默认路径
struct mess
{
int fd;
};
int socket_init();
char* get_filename(char http_req[])//获取该主机的所申请文件的路径
{
if ( http_req == NULL )
{
return NULL;
}
char * ptr = NULL;
char* s = strtok_r(http_req," ",&ptr);
if ( s == NULL )
{
return NULL;
}
printf("客户端请求方法:%s\n",s);
s = strtok_r(NULL," ",&ptr);
return s;
}
//struct mess节点的地址传入
void* fun(void * arg)
{
struct mess * p = (struct mess*)arg;
int c = p->fd;
free(p);
char http_req[512] = {0};
int num = recv(c,http_req,512,0);
if ( num <= 0 )
{
close(c);
pthread_exit(NULL);
}
printf("浏览器发送的请求报文:\n%s\n",http_req);
char* filename = get_filename(http_req);
if ( filename == NULL )
{
//回复404
close(c);
pthread_exit(NULL);
}
char path[256] = {PATH};
if ( strcmp(filename,"/") == 0 )
{
strcat(path,DFL_FILENAME);
}
else
{
strcat(path,filename);
}
printf("open:%s\n",path);
//打开文件
int fd = open(path,O_RDONLY);
if ( fd == -1 )
{
//错误回复 404
printf("打开失败\n");
close(c);
pthread_exit(NULL);
}
int filesize = lseek(fd,0,SEEK_END);//计算文件大小
lseek(fd,0,SEEK_SET);
//组装报文
char http_res[512] = {0};
strcpy(http_res,"HTTP/1.0 200 OK\r\n");
strcat(http_res,"Server: myhttp\r\n");
sprintf(http_res+strlen(http_res),"Content-Length: %d\r\n",filesize);
strcat(http_res,"\r\n");
send(c,http_res,strlen(http_res),0);
printf("发送应答头部:%s\n",http_res);
char data[1024] = {0};
int m = 0;
while( (m = read(fd,data,1024)) > 0 )
{
send(c,data,m,0);
}
close(fd);
close(c);
}
int main()
{
int sockfd = socket_init();
if ( sockfd == -1 )
{
printf("socket init err\n");
exit(0);
}
while( 1 )
{
struct sockaddr_in caddr;
int len = sizeof(caddr);
int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
if ( c < 0 )
{
continue;
}
struct mess * p = (struct mess*)malloc(sizeof(struct mess));
pthread_t id;
p->fd = c;
pthread_create(&id,NULL,fun,(void*)p);
}
}
int socket_init()
{
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,30);
if ( res == -1 )
{
return -1;
}
return sockfd;
}
index.html:
服务器连接结果:
此处正常解析文件需要时html文档,如果是其他文档运行则结果会因为无法解析出现文件直接下载或直接打印在网页上(不同浏览器可能出现状况不一样)