how to 自己写个web服务器其一:关于HTTP协议那点秘密--分享给有兴趣的朋友
primula2013/01/01软件综合 IP:四川
      前段时间写了个嵌入式linux的web服务器(帖子在这),最近再改版,想像JSP那种java和HTML混写的形式,整一个c语言和HTML混写。这顺便把我当时从头开始的思路整理出来,给有兴趣的朋友参考参考。
      本文假设您有C语言基础,了解一些linux下的编程,知道有个叫socket的东西...如果您不知道这些,并且也想自己写一个web服务器,那您可以先学学这些,或者找我吧。。。
      先说说一些简单的原理吧。浏览器访问web服务器的时候,默认会连接服务器的80端口,并向其发送HTTP报文,用来告诉服务器要请求哪个资源。这个HTTP报文其实是明文,ascii码字符串,也就是说我们能读出这个数据流的话,转成字符串也能直接看出些东西,那当浏览器要向服务器请求页面的话,他所发送的http请求是什么样的呢?
以下一段代码可以假装成一个服务器,监听在80端口上,并把收到的数据在控制台上打印。
  
头文件省略…
9 #define BUFFER_SIZE 10240
10 #define PORT 80
11
12 int main()          
13 {
14     struct sockaddr_in server_socket, client_socket;
15     int sockfd, client_fd,sin_size;
16    
17     char buff[BUFFER_SIZE];
18    
19     sockfd = socket(AF_INET, SOCK_STREAM, 0);          //创建一个socket
20    
21     server_XXXXXXXXXn_family = AF_INET;            //结构体初始化
22     server_XXXXXXXXXn_port = htons(PORT);
23     server_XXXXXXXXXn_addr.s_addr = INADDR_ANY;
24     bzero(&(server_XXXXXXXXXn_zero), 8);
25     //将socket与80端口绑定,监听80端口
26     bind(sockfd, (struct sockaddr *)&server_socket, sizeof(struct sockaddr)) ;
27     //设置监听
28     listen(sockfd, 5) ;
29    
30     sin_size = sizeof(client_socket); //下面等待客户端连接,如没有连接该函数会一直等到有连接进来才返回。
31     client_fd = accept(sockfd, (struct sockaddr *)&client_socket, &sin_size);
32     buff[BUFFER_SIZE] = 0;
33    
34     recv(client_fd, buff, BUFFER_SIZE - 1,0);
35     perror("recv");
36     printf("%s\n",buff);
37     close(client_fd);
38    
39     sleep(10);
40     return 0;
41 }

    编译并运行这个程序,然后使用一个浏览器访问运行这个程序电脑的IP,比如说http://192.168.128.111/XXXXXXXXXXml。192.168.128.111是运行这个程序计算机IP,/XXXXXXXXXXml是表示请求在web根目录下的一个叫XXXXXXXXXXml的页面,然后再来看看服务器收到了什么?
1.jpg

第三行的recv:success是由程序第35行输出的perror输出success表示上一步执行没有错误,第四行看到了一个GET /XXXXXXXXXXml HTTP/1.1,这就是一个最重要的开始
    Get 表示客户端是以GET方式提交请求,方式还有其它的,比如POST,这个后面再讲。
    一个空格以后的/XXXXXXXXXXml 表示客户端请求在web根目录下的一个叫XXXXXXXXXXml的页面。
HTTP/1.1 是表示浏览器的版本信息。
弄明白这三条就够我们开始了,下面一大串后面再讲,有兴趣的也可以自己先查查资料。

既然客户端都请求了,现在我们试着给客户端返回点什么。。

在程序前面声明两个字符串:
char httphead[]= {"HTTP/1.1 200 OK\r\n\r\n"};
char httpdata[]= {" <head> <title>primula</title><head>

wahahaha

"};

然后再close前把这个字符串发到客户端
63     send(client_fd, httphead, strlen(httphead) , 0);
64     send(client_fd, httpdata, strlen(httpdata) , 0);
65     close(client_fd);

然后再次编译执行,再用浏览器访问

2.png

出现了,IE的标题变成primula ,正文也有了。学过HTML的仁兄会明白,其实添加上去的第二个数组就是一段HTML文本,<title>标签指明了浏览器标题名,正文的wahahaha在

标签的修饰下变大了。
如果我们换个url呢,比如http://192.168.128.111/XXXXXXXXml,就能发现,不管你输入的是什么网址(当然这个网址是在我们这个服务器下的,比如我的192.168.128.111),它返回的都是这个页面。
这是为什么呢?因为服务器压根就没有看你提交的申请,不管你申请的是什么页面,都返回个这个东西给你,浏览器只是很诚实的提交你的URL,然后很诚实的将服务器返回的信息显示回来。就像你老爸叫你去买烟,就算你给老板说了你要买云烟或者轿子,但老板每次给你的都是红塔山。因为你只是个跑腿的,也确实从老板那收到烟了,但你却不知道这个烟没对,依然屁颠屁颠跑回去了…同样,浏览器也只是个跑腿的,他负责将你的链接交给服务器,负责显示从服务器那收到的信息,只有你才能判断出服务器每次给你的都是红塔山。。
这个特性可以用在404错误页面上,当你访问一个不存在的页面时,服务器可以将一个固定的错误页面返回给你,这个也后面再说。
刚才说到了第二个数组的内容,现在说第一个字符串的内容
HTTP/1.1   200   OK\r\n\r\n (为了明显点,多加了几个空格)
HTTP/1.1:表示服务器使用的HTTP版本。
200是状态码,表示成功,常见得还有404啊500啊什么的,
OK是描述状态的字符
最后跟了一串\r\n\r\n,这个是干什么的呢?
\r\n是一个回车换行,第一个\r\n 是HTTP/1.1 200 OK的,第二个\r\n是一个空行,HTTP协议规定报文头的结束是以一个空行结束的,也就是浏览器在读报文头读到一个空行的时候,就知道报文头结束了,接下来是正文了,当然在这正文就是第二个字符串。
服务器返回的一个典型的HTTP报文就是这样的
HTTP/1.1 200 OK\r\n
Server: daisy\r\n
……………………\r\n
\r\n       --------------从这个空行开始,下面的内容都是正文了
<html>
<head>
……………………..

现在大体上就能够知道服务器和浏览器是怎么通过HTTP传信息的了吧。
再想一下一个web服务器的实现,如果我们能从客户端的请求中提取出他请求的页面内容,比如从GET /XXXXXXXXXXml HTTP/1.1 中提取出/XXXXXXXXXXml 这个信息,是不是我们就能够在某个目录中去找XXXXXXXXXXml 这个文件并传送给客户端,如果下次又被客户端以http://192.168.128.111/XXXXXXXXml的url请求了一个XXXXXXXXml的文件,那不是也可以在某个目录下去找XXXXXXXXml了么,那也就是只要我们把网页放在某个目录下,让一个监听在80端口上的程序能够解析出发送来的请求,然后将请求的页面传过去。。。这不就是一个简单的web服务器了吗?

未完。。有空再继续。。。(可能吧[s:220])

+10  科创币    eifheowvd    2013/01/01 高质量发帖
+100  科创币    phpskycn    2013/01/01 低质量发帖
+25  科创币    cqsrmxxzyx    2013/01/01 鼓励,&lt;&lt;HTTP权威指南&gt;&gt;是本很好的书
+10  科创币    猎鹰    2013/01/01 标题看成HTPB的路过。。。。。。
+25  科创币    山寨雷神公司    2013/01/02 高质量发帖
+200  科创币    ehco    2013/02/17 深入浅出,茅塞顿开,解我网络盲多年疑惑。
来自:计算机科学 / 软件综合
9
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
eifheowvd
12年0个月前 IP:未同步
483901
非常通俗易懂,期待续集,想写个支持断点续传的下载服务器
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
phpskycn
12年0个月前 IP:未同步
483912
回 楼主(primula) 的帖子
求HTTP报文定义……
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
primula作者
12年0个月前 IP:未同步
483929
回 1楼(xp200798) 的帖子
唔~~我的这个服务器也有这个功能,下回解析[s:274]
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
primula作者
12年0个月前 IP:未同步
483936
回 2楼(phpskycn) 的帖子

attachment icon http1.3.pdf 880.64KB PDF 203次下载 预览
HTTP协议的文档,我觉网上随便找个文档都比这个好看,这个东西越看越不明白。。。
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
cqsrmxxzyx
12年0个月前 IP:未同步
483942
<<HTTP权威指南>>是本很好的参考书籍
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
primula作者
12年0个月前 IP:未同步
483966
回 5楼(cqsrmxxzyx) 的帖子
噢~~什么时候找来看看[s:274]
+1
科创币
猎鹰
2013-01-01
每次看你的头像都感觉你是妹子。
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
zx-16533
12年0个月前 IP:未同步
484004
干脆移植个php或jsp或asp上去?
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
lyly8
11年11个月前 IP:未同步
499349
下不了附件呢
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
wzy41718682
11年10个月前 IP:未同步
505128
这个不错。。先留名
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
primula
学者 机友 笔友
文章
20
回复
184
学术分
1
2012/03/10注册,5个月17天前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}