Web服务器实现高崇铭2012001010016简单Web服务器详细设计说明书英才实验1班高崇铭20120010100161、引言:本程序为windous下用C++语言实现的简单的一个Web服务器,其功能很简单,开启时候能够监听到外部客户端发来的HTTP请求,解析请求文件,在本机进行搜索,最终以HTTP报文形式返回该文件。由于这只是一个简单的服务器架构,并没有考虑很好的鲁棒性,即返回报文中只有200OK的字段,并未考虑其他可能性。但在该程序的基础上,这些功能都很容易扩展。整个程序的思路框架如下图:Socket()初始化Bind()Listen()Accept()Recv()http_parse_request_cmd()http_send_response()Send()Closesocket()触发条件:有客户端请求到达监听套接字理论上,这一循环为死循环创建套接字1号,分配内存空间为新创建等的套接字绑定IP,port等本机信息,即贴上“门牌”开始在套接字1号上监听从客户端传来的HTTP报文从到达的请求队列中取队头的请求,为该请求新建一个绑定好ip,port等信息的套接字2号将套接字2号中缓存区里等的信息拷贝到buf中对buf里的信息(http报文)进行分析,获取其请求文件路径在本机找到寻找到相应文件先将HTTP相应报文打包发送,再将文件分段发到套接字2号的缓冲区Closesocket()关闭套接字2号关闭套接字1号退出Web服务器实现高崇铭20120010100162、组织结构:http_parse_request_cmd()http_send_response()Main()Socket()Bind()Listen()Accept()Closesocket()Send()Recv()注意:箭头所指方向为调用关系3、程序:3.1:主函数(1)函数:main(intargc,char**argv)(2)函数功能:包含socket(),bind(),listen(),accept(),recv(),closesocket()这一系列的套接字函数,负责建立套接字,绑定套接字与本机信息,分配端口号,监听并接收来自服务器的请求报文,将HTTP报文交给解析函数http_send_response(),最后关闭套接字。(3)参数说明:intargc:参数个数char**argv:端口号(4)返回值:空特殊说明:所用socket函数如下:(1)函数:socket(intaf,inttype,intprotocol);(2)函数功能:创建一个套接字,出现在地址族中,但并未赋名。即只是创建了一个套接字的描述,此时没有IP和端口号与其关联。Web服务器实现高崇铭2012001010016(3)参数说明:af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPAInternet地址格式。type:指定socket类型。其中所用SOCK_STREAM提供有序的、可靠的、双向的和基于连接的字节流,使用带外数据传送机制,为Internet地址族使用TCPprotocol:指定协议,0为缺省。(4)返回值:若创建套接字失败,则返回INVALID_SOCKET。(1)函数:bind(SOCKETs,conststructsockaddrFAR*name,intnamelen);(2)函数功能:将一本地地址与一套接口捆绑。本函数适用于未连接的数据报或流类套接口,在connect()或listen()调用前使用。当用socket()创建套接口后,它便存在于一个名字空间(地址族)中,但并未赋名。bind()函数通过给一个未命名套接口分配一个本地名字来为套接口建立本地捆绑(主机地址/端口号)。(3)参数说明:s:标识一未捆绑套接口的描述字。name:赋予套接口的地址。namelen:name名字的长度。(4)返回值:绑定成功返回0,失败返回-1或SOCKET_ERROR(1)函数:listen(SOCKETs,intbacklog);(2)函数功能:Listen()将上一步bind()函数绑定过IP与端口号的套接字申请进入的连接建立一个后备日志,即为监听连接请求(3)参数说明:s:标识一未捆绑套接口的描述字。name:赋予套接口的地址。namelen:name名字的长度。(4)返回值:绑定成功返回0,失败返回-1或SOCKET_ERROR(1)函数:accept(SOCKETs,structsockaddr*addr,int*addrlen);(2)函数功能:在到达listen()监听函数的请求连接队列中抽取第一个连接,创建一个相同SOCKET类型的套接字并返回句柄,即赋予新的端口号(3)参数说明:s:套接口描述字,该套接口在listen()后监听连接。Web服务器实现高崇铭2012001010016addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。addr参数的实际格式由套接口创建时所产生的地址族确定。addrlen:(可选)指针,输入参数,配合addr一起使用,指向存有addr地址长度的整型数。(4)返回值:成功返回一个新的套接字描述符,失败返回-1或INVALID_SOCKET。(1)函数:recv(SOCKETs,charFAR*buf,intlen,intflags);(2)函数功能:如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,直到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的);(3)参数说明:s:一个标识已连接套接口的描述字。buf:用于接收数据的缓冲区。len:缓冲区长度。flags:指定调用方式。(4)返回值:正常时返回其实际copy的字节数。在copy时出错,那么它返回SOCKET_ERROR。在等待协议接收数据时网络中断,返回0。(1)函数:closesocket(SOCKETs);(2)函数功能:本函数关闭一个套接口。更确切地说,它释放套接口描述字s,以后对s的访问均以WSAENOTSOCK错误返回。若本次为对套接口的最后一次访问,则相应的名字信息及数据队列都将被释放。(3)参数说明:s:一个用于标识已连接套接口的描述字。(4)返回值:无错误发生返回0。否则返回SOCKET_ERROR。3.2发送相应报文函数:(1)函数:http_send_response(SOCKETsoc,char*buf,intbuf_len)(2)函数功能:调用http_parse_request_cmd()函数,得到文件名、文件路径、类型(后缀)。用fopen()打开该文件,获得文件长度信息。发送HTTP相应报文头部。分段发送文件内容Web服务器实现高崇铭2012001010016到套接字缓冲区。(3)参数说明:Soc:连接套接字buf,:缓冲区数组buf_len:缓冲区数组的大小(4)返回值:成功返回1,失败返回0特殊说明:(1)函数:send(SOCKETs,constcharFAR*buf,intlen,intflags);(2)函数功能:用于向一个已经连接的socket发送数据。对于数据报类套接口,必需注意发送数据长度不应超过通讯子网的IP包最大长度。成功地完成send()调用并不意味着数据传送到达。如果传送系统的缓冲区空间不够保存需传送的数据,除非套接口处于非阻塞I/O方式,否则send()将阻塞。(3)参数说明:s:一个用于标识已连接套接口的描述字。buf:包含待发送数据的缓冲区。len:缓冲区中数据的长度。flags:调用执行方式。(4)返回值:正常时返回所发送数据的总数(请注意这个数字可能小于len中所规定的大小)。错误则返回SOCKET_ERROR。3.3解析报文函数:(1)函数:http_parse_request_cmd(char*buf,intbuflen,char*file_name,char*file_path,char*suffix)(2)函数功能:通过buf里的http请求报文,得到所需要文件路径、文件名,后缀,调用http_get_type_by_suffix()函数得知该后缀所属类型。返回值为空(3)参数说明:buf,:缓冲区数组buflen:缓冲区数组的大小file_name:文件名指针file_path:文件路径指针suffix:文件后缀指针(4)返回值:无Web服务器实现高崇铭2012001010016注意事项:程序生成的exe要与请求的本地文件放在同一个目录下,之后在浏览器里输入例如:源码:#includestdafx.h#includestdio.h#includewinsock2.h#pragmacomment(lib,ws2_32.lib)/*WinSock使用的库函数*//*定义常量*/#defineHTTP_DEF_PORT80/*连接的缺省端口*/#defineHTTP_BUF_SIZE1024/*缓冲区的大小*/#defineHTTP_FILENAME_LEN256/*文件名长度*//*定义文件类型对应的Content-Type*/structdoc_type{char*suffix;/*文件后缀*/char*type;/*Content-Type*/};structdoc_typefile_type[]={{html,text/html},{gif,image/gif},{jpeg,image/jpeg},{txt,text/html},{NULL,NULL}};char*http_res_hdr_tmpl=HTTP/1.1200OK\r\nServer:Chongming_Gao'sServer\r\nAccept-Ranges:bytes\r\nContent-Length:%d\r\nConnection:close\r\nContent-Type:%s\r\nWeb服务器实现高崇铭2012001010016\r\n;//注意以空行结束。char*http_404=HTTP/1.1404NotFound\r\n\r\n;/****************************************************************************函数功能:根据文件后缀查找对应的Content-Type.**参数说明:[IN]suffix,文件名后缀;**返回值:成功返回文件对应的Content-Type,失败返回NULL.***************************************************************************/char*http_get_type_by_suffix(constchar*suffix){structdoc_type*type;for(type=file_type;type-suffix;type++){if(strcmp(type-suffix,suffix)==0)returntype-type;}returnNULL;}/****************************************************************************函数功能:解析请求行,得到文件名及其后缀.请求行格式:*[GET]**参数说明:[IN]buf,字符串指针数组;*[IN]buflen,buf的长度;*[OUT]file_nam