我们上一篇简单实现了一个反应堆,反应堆的程序只是简单的收发数据,并没有什么业务逻辑,所以今天就在反应堆的基础上,添加一个服务器端的htpp协议实现。
4.1 http介绍
在第一篇客户端http请求的时候就已经介绍了(其实还没写,以后再写),所以这里就简单描述一下。
http协议(hyper text Transfer Protocol,超文本传输协议),是因特网中应用最为广泛的一种网络传输协议。Http是一个基于TCP/IP通信协议来传递数据。
HTTP协议工作于客户端-服务器端架构上,浏览器作为HTTP客户端通过URL向HTTP服务器端即web服务器发送所有请求。
web服务器有:Apache服务器,IIS服务器
HTTP默认端口号为80,也可以改为8080或其他端口。
HTTP三点注意事项:
- HTTP是无连接:无连接的含义是限制每次连接只处理一个请求。
- HTTP是媒体独立的:这意味着,只要客户端和服务器知道如何处理数据内容,任何类型的数据都可以通过HTTP发送。
- Http是无状态:无状态是指协议对于事务处理没有记忆能力。
4.2 服务器响应的消息
http响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
具体返回的一个请求:
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: “34aa387-d-1568eb00”
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain
注意:
服务器响应消息,最后都要加\r\n\r\n。要不然一直转圈圈
4.3 状态码
状态码分为5大类,上面的思维导图中,已经分类清楚了,比较常用的是4xx,是客户端出现错误了,5xx是服务器出现了错误,2xx也比较熟悉,200就是接收成功了。
4.4 content-type
这是从菜鸟教程中截取出来,服务器返回的数据一定要带上content-type,这个字段的意思就是给客户端说明需要以什么方式的格式去解析服务器返回的数据,之前实现的时候,就是忘记了写了这个字段,所以一直报错。
4.5 实现
接下来我们就简易的实现了一个http sever版,在上一节课的reactor之上实现的,reactor属于网络接入层,http属于应用层,reactor把数据准备好之后,往http层发送就可以了。
4.5.1 修改reactor的程序
把之前一个buff的修改成2个buff,并且注册回调函数,在接收数据的回调函数调用这个回到,把整个数据传输到http应用层。
//对 socket疯转
struct nty_event
{
int fd;
int events;
int status;
void *arg;
int (*callback)(int fd, int events, void *arg); //回调函数
long last_active;
char sbuffer[1024];
int slen;
char rbuf[1024];
int rlen;
};
struct nty_reactor
{
int epfd;
struct nty_event *event;
HANDLER *handler;
};
回调函数:
void *http_handler(void *arg)
{
struct nty_event *ev = (struct nty_event *)arg;
//解析接收到的数据
char *rbuffer = ev->rbuffer;
//准备好需要返回的数据
char *sbuffer = ev->sbuffer;
}
4.5.2 客户端数据
简单的接收发送框架已经搭建好,接下来就要尝试接收客户端发来的数据了,我们通过浏览器往我们服务器发送数据,先看我们浏览器抓起的数据:
通过浏览器的f12我们也能看到客户端发送的请求,另外我在在服务器端把接收的数据打印出来了:
这样一对比,就是我们刚才发送的数据
4.5.3 解析客户端数据
接下来我们主要解析客户端数据,我们知道http的请求,都是以一行做为单位的,所以我们需要每行都需要读取,
for(; index < rlen; ){
memset(line, 0, 1024);
index = read_line(rbuffer, rlen, line, index);
//printf("line %d %s\n", index, line);
if(index <= 0) {
ev->slength = send_erron(sbuffer, 404);
return ;
} else if(index < rlen){
char * ret = strtok(line, " ");
if(strcmp(ret, "GET") == 0) {
//需要解析账号密码
char * soucer = strtok(NULL, " ");
printf("soucer %s\n", soucer);
ev->slength = send_erron(sbuffer, 200);
}
}
}
4.5.4 解析url
http://192.168.0.104:8889/source?name=chen&pwd=123456
解析上面写的带有特殊字符的url,目前没想到比较好的方法,只能想到用切割字符串的方式切割,所以暂时先不用吧,以后分析分析解析http服务器的源码的时候,再回来添加。