1.Web服务器
Web服务器的工作原理并不复杂,一般可分成如下4个步骤:连接过程、请求过程、应答过程以及关闭连接。下面对这4个步骤作一简单的介绍。连接过程就是Web服务器和其浏览器之间所建立起来的一种连接。查看连接过程是否实现,用户可以找到和打开socket这个虚拟文件,这个文件的建立意味着连接过程这一步骤已经成功建立。请求过程就是Web的浏览器运用socket这个文件向其服务器而提出各种请求。应答过程就是运用HTTP协议把在请求过程中所提出来的请求传输到Web的服务器,进而实施任务处理,然后运用HTTP协议把任务处理的结果传输到Web的浏览器,同时在Web的浏览器上面展示上述所请求之界面。关闭连接就是当上一个步骤--应答过程完成以后,Web服务器和其浏览器之间断开连接之过程。Web服务器上述4个步骤环环相扣、紧密相联,逻辑性比较强,可以支持多个进程、多个线程以及多个进程与多个线程相混合的技术。
2.CGI程序
CGI程序通过标准输出(stdout)将输出信息传送给Web服务器。传送给Web服务器的信息可以用各种格式,通常是以纯文本或者HTML文本的形式,这样我们就可以在命令行状态调试CGI程序,并且得到它们的输出
总之,CGI程序的主要任务就是从Web服务器得到输入信息,进行处理,然后将输出结果再送回给Web服务器
3.协议
- 应用层使用HTTP协议。
- 传输层使用TCP协议
- HTML(标准通用标记语言下的一个应用)文档格式。
- 浏览器统一资源定位器(URL)。
- 为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS。为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密
4.我们在地址栏中输入一个网址,比如百度(www.baidu.com)后浏览器做了哪些事
在浏览器输入网址,Enter之后发生的事情:
(1)浏览器接收域名
(2) 发送域名给DNS,中文名字是域名系统服务器,一般位于ISP(互联网服务提供商,比如我们熟知的联通、移动、电信等) 中。浏览器会首先发给离自己最近的DNS,DNS收到浏览器发来的域名,在自己的数据库和缓存中查找这个域名所对应的IP,如果没有他会询问其他的DNS,递归下去直到弄清楚IP。最后把结果返回给浏览器。
(3)DNS返回域名所对应的IP地址
(4)浏览器向因特网中发出请求
(5)路由器依据IP地址,把包裹送达IP所对应的百度服务器
(6)百度服务器看到包裹中的请求,把百度的搜索页面发送回浏览器,回来的时候也是依据你主机的IP地址才能将包裹正确送达。
5.注册:分为两个:管理员注册和用户注册,管理员的标志为1,用户的标志为2。
表中的id设置为自增长类型的主键:primery key,auto_increment,从1开始增长,应该是32位吧。
6.main函数参数的意义:第一个agrc是参数个数,第二个argv[ ]是参数;项目中用到了fork+execv,fork一个子进程后,execv替换它,去执行已经编译好的CGI程序,execv的第一个参数是路径,第二个参数是一个二维数组,myargv[0]的意义是执行这个文件,myargv[1]是参数,myargv[2]是环境变量
7.execv替换执行一个程序时,此时被执行的程序的main函数的参数就可以接收到execv的参数,在这里,我将管理员和用户锁生成的不同页面放在execv第二个参数的最后一个位置,就可以相应的去处理它,比如管理员注册和用户注册这两个模块生成两个页面,在被执行的程序就根据生成的不同的页面向数据库插入数据时,就会标明标志位1或2,代表用户或管理员。
8.CGI程序怎么处理传进来的参数?它用字符串分割函数处理。
9.在插入数据时,字符串转日期类型,而我为了简便,在数据库中存储日期类型都以字符串类型存储。注册时,根据管理员和用户的注册界面不同,分为两个页面,实际上,可以根据注册信息的不同来区分是用户还是管理员,如果注册成功,生成同一个注册成功的页面,我这样就动态生成了两个页面,其实是一个页面。
10.dup2()这个函数是将标准输出重定向为写端,那么我的项目里,子进程输出的数据从管道传给父进程,父进程从0端读取,再将数据通过socket套接字(说白了,其实就是两个进程之间的通信,而socket就爱干这事)传给浏览器。
11.遇到的问题:调试CGI程序时,程序崩溃时,首先ulimit -c unlimit 设置生成的core文件大小为不限制大小,再执行程序,设置core文件大小时注意每一个窗口的core文件大小都需要重新设置,他们都不相同;之后
调试 gdb ./ser core.3452 bt
我用这个方法发现错误:signal 11,查找之后发现他是引用一个空指针的意思;具体错误如下:#0 0x00188da5 in __mempcpy_ia32 () from /lib/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.80.el6.i686 keyutils-libs-1.4-4.el6.i686 krb5-libs-1.9-33.el6.i686 libcom_err-1.41.12-12.el6.i686 libselinux-2.0.94-5.3.el6.i686 mysql-libs-5.1.61-4.el6.i686 nss-softokn-freebl-3.12.9-11.el6.i686 openssl-1.0.0-20.el6_2.5.i686 zlib-1.2.3-27.el6.i686
那么我由这个错误大概知道发生在内存拷贝时,并且与数据库有关。我再进行调试,错误发生在sprintf这里,它不能将myargv[0]读进去,所以发生段错误。
查看代码,思考后,发现我定义了一个char *quer_str;(导致这个问题的原因是我把另一个程序的代码不小心写到了这个程序里,以后一定要小心了)所以发生截断了,而在sprintf这个函数里,它的第一个参数要求我们不能这样定义,要写成数组形式。
12.你怎么知道数据库里还有没有此信息?先select查找,用 mysql_real_query来执行语句, mysql_store_result获取结果集, mysql_store_result并不能判断这个数据在数据库里是否存在,所以我用了mysql_num_rows,如果mysql_num_rows的返回值为0,说明数据库中没有该数据,mysql_num_rows的功能是得到结果集的行数。
mysql_store_result返回值为NULL的情况有:
(1)无法读取数据(在连接上出现错误);
(2)查询未返回数据(如insert,updata,delete);
(3)出现malloc故障(例如结果集过大)
通过调用mysql_field_count(),始终能检查语句是否应生成非空结果。如果mysql_field_count()返回0,结果为空,而且上一个查询是未返回值的语句(例如INSERT或DELETE)。如果mysql_field_count()返回非0值,语句应生成非空结果。关于这方面的示例,请参见mysql_field_count()函数介绍:
返回作用在连接上的最近查询的列数。该函数的正常使用是在mysql_store_result()返回NULL(因而没有结果集指针)时。在这种情况下,可调用mysql_field_count()来判定mysql_store_result()是否应生成非空结果。这样,客户端就能采取恰当的动作,而无需知道查询是否是SELECT(或类似SELECT的)语句。在这里给出的示例中,演示了完成它的方法。返回表示结果集中列数的无符号整数。
另一种可选的方法是,用mysql_errno(&mysql)替换mysql_field_count(&mysql)调用。在该情况下,无论语句是否是SELECT,你将直接从mysql_store_result()查找错误,而不是从mysql_field_count()的值进行推断。
通过调用mysql_error()或mysql_errno(),可测试是否出现了错误。
注意:
对于成功调用mysql_query()进行select语句的查询,即使查询的数据不存在(即rowcount=0),result也不为NULL。result为NULL的情况只是检测上一个查询是否是有返回值的语句。
13.对于用户名和密码不为空的判断,html5可以很简便的解决,以减少对服务器的请求,用后台语言也可以实现,如C。我做了测试,如果登录名重复,会有提示,反之注册成功。