项目:HTTP服务器框架分析(一)

项目:HTTP服务器框架分析(二)
项目:HTTP服务器框架代码分析(三)

0、项目描述

项目名称:基于HTTP服务器的~~~~项目
基于HTTP服务器框架的世界时间查询器,采用多线程处理多客户端连接请求,具体的还要分析请求是静态请求还是动态请求,静态请求直接返回对应URL目录中的HTML资源,动态请求使用了CGI技术处理具体业务,接收到城市名称后,就从后台MySQL数据库中取该城市与北京时间时差,计算后将结果返回给客户端。利用duhup执行shell脚本实现网站访问。利用webbench测试网站的并发量。

1、端口号重用机制是怎么实现的?

只有主动关闭的一方才会进入TIME_WAIT状态,那这种情况也就是高并发连接都是服务端主动关闭。那么端口不够用就是文件描述符不够用了,因为文件描述符只有在从TIME_WAIT状态转换到CLOSE状态后才会真正被系统收回。TIME_WAIT状态会持续2MSL的时间才会转换到CLOSE状态,一般是1-4分钟。如果在这段时间内文件描述符都被用完了,而关闭的连接处于TIME_WAIT状态导致文件描述符并没有被真正释放,就会出现这种情况。

TIME_WAIT 状态到底会占用什么?
被占用的是一个五元组:(协议,本地IP,本地端口,远程IP,远程端口)。对于 Web 服务器,协议是 TCP,本地 IP 通常也只有一个,本地端口默认的 80 或者 443。只剩下远程 IP 和远程端口可以变了。如果远程 IP 是相同的话,就只有远程端口可以变了。这个只有几万个,所以当同一客户端向服务器建立了大量连接之后,会耗尽可用的五元组导致问题。

等待 TIME_WAIT 结束可能是令人恼火的一件事,特别是如果您正在开发一个套接字服务器,就需要停止服务器来做一些改动,然后重启。幸运的是,有方法可以避开 TIME_WAIT 状态。可以给套接字应用 SO_REUSEADDR 套接字选项,以便端口可以马上重用。

在绑定地址之前,以 SO_REUSEADDR 选项调用 setsockopt。为了允许地址重用,设置整型参数(on)为 1 (不然,可以设为 0 来禁止地址重用)。

on = 1; 
ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ); 
2、accept多线程建立过程?

accept接收客户端的connect请求。这个过程的实质是对backlog队列的一个操作,在accept前,内核接受到connect请求首先把socket放入未完成队列,然后accept的时候,需要把socket放入已完成队列当中去,然后accept成功以后从已完成队列当中取出。这就是accept的整个过程。

accept成功以后,我们使用pthread_create来进行创建线程,把socket托付给线程来进行操作。在线程处理的过程中,有一个问题就是线程等待,我们为了解决这个问题,我们使用线程分离,这样使得线程可以作为孤儿进程的形式托管给1号进程,当执行完毕以后,由1号进程来进行资源的回收。

线程分离
pthread_detach(pthread_t thread)
而pthread_join(线程ID,二级指针,指向一个线程的返回值)
不关心线程返回值,join是一种负担。

3、怎么写一个shell脚本、duhup?

在这里插入图片描述

4、状态码有哪些?

(1)100:继续 客户端应当继续发送请求。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。

(2)200: 请求成功。

(3)201:请求完成,结果是创建了新资源。

(4)301:请求到的资源都会分配一个永久的URL。这样就可以在将来通过该URL来访问此资源 处理方式:重定向到分配的URL。

(5)302:请求到的资源在一个不同的URL处临时保存。处理方式:重定向到临时的URL。

(6)400:非法请求 处理方式:丢弃。

(7)401:未授权 处理方式:丢弃。

(8)404:没有找到 处理方式:丢弃。

(9)500:服务器内部错误。

(10)501:服务器无法识别 服务器不支持当前请求所需要的某个功能。

(11)503:服务出错 由于临时的服务器维护或者过载,服务器当前无法处理请求。

5、非CGI模式流程?

首先明白此时我们可以得到资源路径,这个资源路径其实就是根目录下的路径,默认我们去寻找根目录下的主页。
所以我们需要给资源加上index.html
然后我们把整个index.html的信息发送给scoket。

我们在这里采用的方式是sendfile的操作,sendfile主要是实现零拷贝发送文件,实现一个高效的数据传输,并且对其进行验证。这样socket接受到主页信息,就可以显示出来网页了,当然这个过程我们也是按照HTTPPOST响应发送过去的。

6、CGI模式流程?

CGI优点:解耦合
CGI缺点:每次出发CGI都要创建子进程,高并发导致服务器性能降低。

  • 父进程:

1、创建一对匿名管道;
2、创建子进程;
3、父进程等待子进程返回;

  • 子进程:
    1、设置环境变量;(传递请求方法、content_length、query_string)
    2、重定向;(将标准输入输出重定向到管道)
    3、子进程程序替换;(替换对应的CGI程序、CGI完成页面生成动作,CGI程序通过标准输入读取到需要的信息,把生成的页面通过标准输出返回)
7、GET模式的CGI?

get方法的时候,这个时候cgi所需要的参数是放在url当中的,所以这个时候我们就去在HTTP GET请求行的第二个内容资源路径当中进行字符串的处理,我们找‘?’,当找到以后,我们让一个指针指向这里,叫做query_string,我们把这个作为一个环境变量传递给子进程就可以了。对于GET的cgi模式,最重要的也就是method(方法)和query_string(包含参数)

8、POST模式的CGI?

当我们使用POST方法的cgi模式的时候,这个时候就会有另外的问题,我们的参数在正文当中,所以我们需要在正文当中寻找,另外需要知道正文中的字节数。这个时候POST的消息报头就起作用了,它在其中组织了name:value形式的content_length:xx这样的内容,然后获取到这个长度以后,我们就可以知道向socket读取多少长度的内容了,然后读取完以后我们就可以获得到参数,同样是按照“?”和“&”的形式进行组织的,我们取出这个内容,然后进行数据运算操作。

9、父进程的后序操作?

父进程进行处理的时候首先需要重定向管道,这样才好进行后续的操作,然后我们进行查看方法,如果是POST方法,我们需要把HTTP的请求正文全部获取到放入和cgi程序打交道的管道当中。这样才能让cgi获取到正文信息,其他情况下,我们都需要从cgi返回到管道的结果当中进行获取返回的信息,把这个信息发送给socket。最后,当然别忘了使用waitpid等待子进程。

怎么等待子进程?
如果用wait(NULL)是不行的,wait是无差别等待,见到任意子进程就回收,造成资源回收错乱,等待任意子进程。waitpid(child_pid, NULL, 0)函数指定了子进程PID。

10、CGI逻辑中父子进程读写的细节问题?

逻辑流程伪代码:

int HandlerCGI(int new_sock, Request *req)
{
	//创建匿名管道
	int fd1[2];
	int fd2[2];
	
	int ret = pipe(fd1);
	//成功
	ret = pipe(fd2);
	//失败要挨个关闭文件描述符
	//为了提高描述性、重命名
	int father_read = fd1[0];
	int father_write = fd2[0];
	int child_write = fd1[1];
	int child_read = fd2[1];
	
	ret = fork();
    if(ret>0)
    {
    	//父进程
	    close(child_write);
	    close(child_read);
	    err_code = HanderCGIFather(new_sock, father_read, ret, req);
    }

    else if(ret == 0)
    {
	    //子进程
	    close(child_write);
	    close(child_read);
	    err_code = HandlerCGIChild(child_read, chile_write, req);
    }

    else
    {
	    //err_code = 404;
    }
}

在处理父进程的CGI时,关闭子进程读写端,为了父进程从子进程中读取数据的时候,能够读到EOF。对于管道来说,所有写端关闭,继续读才有EOF。

11、CGI的编写方式?

cgi的编写方式我们可以叫做cgi网关协议,我们所有的cgi程序都可以套用这一套来进行操作,我们采用的传递参数方式是环境变量,其实还可以用管道进行传递,传递进管道,cgi程序从管道当中读取出来。

然后我们进行字符串处理。
因为参数的组织形式是”?data1=100&data2=200”这种形式的,所以我们要找的关键符号就是“=”和“&”,这样我们就可以取到参数,然后把参数进行运算,得到结果输出到标准输出就好了。

12、项目拓展性问题?

1、为了实现高并发,可以使用高级I/O,使用poll、epoll形式等待就绪文件描述符。
2、业务逻辑的拓展:利用python爬取一些网站数据,然后对数据进行处理。比如每天爬取天气预报,用户注册之后,每天定时将天气信息通过邮箱发送给用户。
3、CGI是一种古老动态页面生成方式,只要有一个客户端触发,就要创建子进程,高并发环境下,创建子进程开销比较大,建议使用其他的组织方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

askunix_hjh

谢谢请我吃糖~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值