这个版本与前一个使用多进程的版本相比,有了较大的改动。主要把处理HTTP响应的代码都改成多线程的了。
当然基本框架还是一样的:即一个进程处
理一次HTTP会话,但单进程内用多线程来响应那些具有多线程请求的浏览器的请求。即使对那些只使用单线程来发送HTTP请求的浏览器来说,也可以显著提
高服务器和客服端的响应效率,当然代价是服务器需要消耗更多的内存,这是一种用空间换时间的做法,应视具体情况来决定使用哪种模型。
下面简要说明一下整个程序的执行流程:
先看看目录结构
程序入口 main --webserver.c
1.首先调用config,读取配置文件 webconfig.conf,配置文件中有一些对服务器初始化的配置选项,具体选项一般都能“见文知意”,在此不再说明。
2.注册信号处理函数,该信号处理函数捕获 SIGCHLD 信号,也就是当子进程退出时向父进程发出的信号,
在信号处理函数中,将僵死子进程的资源回收。
3.调用 init_socket。根据配置信息初始化服务器。
4.进入死循环,等待客户端浏览器的连接请求(accept)
5.一旦有连接请求到达,立刻生成一个子进程来处理该请求(我称为一个HTTP session),自此函数流程应该执行到http_session了。
6.
在http_session中利用select设置一超时时间,主要目的是在客服端长时间没有数据过来时,服务器端能主动断开也客户端的连接,也就是终止
这个子进程,结束这次http
session。若在超时范围内套接口有数据可读,那子进程就读出该数据,继而将数据转交给一个新线程来处理,子进程不管读到的数据是什么,他只负责接收
数据并将其转交给线程,所以细节都由线程来处理。
7.在6中通过调用new_thread函数,现在执行流到了多线程内部了,呵呵,这里可不是什么好地方,我得时刻保护好所以共享变量,如果你对Posix多线程不甚了解,我还是建议你先去看看Posix多线程编程的资料再回过头来读这部分多线程的代码。
下面我对thread.c中的几个函数做一个简单的介绍:
new_thread 负责生成一个线程,并将从子进程中传递过来的参数保存到全局变量中(注意要保护临界区代码,为了简单起见我通过互斥量来保护,当然还有其他更好的方法)
thread_init 这是为了创建线程特定数据结构(TSD)而编写的一个函数,他的目的就是保证该TSD在所以线程中只被创建一次,且一定创建一次。
destructor 线程退出时调用的函数,主要负责释放(或者说回收)线程动态申请的内存,以便程序继续使用
deal_req 真正负责处理HTTP请求的函数,首先调用pthread_once创建一个TSD,接着调用
pthread_getspecific 该函数根据key去查找TSD中与这个key关联的value,若value为NULL,则说明还没有给该key关联一个value,于是我们就动态的生成一个value(即一个TSD数据结构包括其他必要的内存)
然后调用pthread_setspecific 把该value与key关联起来。完成这先必要的初始工作以后,便可以着手分析和处理HTTP请求了。
8.调用get_uri 获得请求的uri地址,也就是对应服务器硬盘上的文件路径
9.调用get_mime_type 获得请求文件的mime类型,这主要用于填充Content-Type字段
10.调用 get_uri_status 获得该uri请求的状态,这主要用来填充响应头中的响应码
11.调用 get_file_disk 将uri对应的文件从服务器硬盘上读至内存,并把该片内存的首地址赋给TSD中相应指针
12.调用 set_header 将HTTP响应头填充好,并把文件拼接到响应头后
13.
调用 send 将上一步设置好的HTTP响应头和响应实体(即拼接在后面文件)发送给客服端(其实调用send并不是真的发送,
send仅仅只是把buf指示的内存拷贝到内核缓冲区,具体什么时候发送,那还得看缓冲区的情况和对方的情况,而这便是TCP协议要处理的细节了)。
下面贴出源码,所以源码,配置文件,还有测试页面,我以打包到附件,请下载查阅,如果有什么疑问,欢迎给我发邮件。
点击下载附件 下一页
阅读(1932) | 评论(0) | 转发(0) |