利用socket自己实现基于HTTP协议的Web服务器

来源:互联网 发布:盗号软件万能钥匙 编辑:程序博客网 时间:2024/05/16 12:53

在开发网站的过程中,首先我们需要配置一个web服务器,一般会使用Apache这个开源的服务器软件,扩展性高,支持性也很好。实际上如果是windows系统的话那么也可以使用windows操作系统提供的IIS(Internet Information Server)。这两个服务器软件使用度各占60%和30%。但是现在,我们经过分析HTTP协议,我们会自己实现一个Web服务器,当然了,只是一个很小的Web服务器了。

那么用什么来实现Web服务器呢?实际上实现一个Web服务器主要就是实现HTTP协议,而这个HTTP协议处于计算机网络结构体系中的应用层,那么我们就可以借助应用程序接口Socket来实现HTTP协议。在这之前我们需要通过抓包来分析一下HTTP协议的机制,抓包在这里就不说了,会用另一篇博客来详细分析利用抓包来分析HTTP协议的详细过程,网上也有很多的资料,大家可以参考一下。

那么下面哦我们就来看看这个web服务器是如何实现的。

helpFunction.h文件(函数声明)

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <winsock2.h>#pragma comment ( lib , "ws2_32" )#define MAXLINE 10000// 声明函数// 获取文件类型void GetFileType( char * filename , char * fileType ) ;// 处理静态请求void HandleStatic( SOCKET clientSocket , char * filename ) ;// 处理动态请求的函数void HandleDynamicFunc( char * filename , char * args , int & result ) ;// 处理动态请求void HandleDynamic( SOCKET clientSocket , char *filename , char * args ) ;//解析URIbool ParseURI( SOCKET clientSocket , char * uri , char * filename , char *args ) ;// 解析请求(Parse Request ),如果为静态返回true,如果为动态,返回falsevoid ParseRequest( SOCKET clientSocket , char * request ) ;// 处理请求行:读取请客户端的请求,给出响应void HandleRequestLine( SOCKET clientSocket , char * request ) ;

helpFunction.cpp(主要功能函数定义)

#include "helpFunction.h"// 获取文件类型void GetFileType( char * filename , char * fileType ){memset( fileType , 0 , 20 ) ;if( strstr( filename , ".html") || 0 == strcmp( filename , "/" ) ){strcpy( fileType , "text/html" ) ;}else if( strstr( filename , ".gif" ) ){strcpy( fileType , "image/gif" ) ;}else if( strstr( filename , ".jpg" ) ){strcpy( fileType , "imgae/jpeg" ) ;}else{    strcpy( fileType , "text/plain" ) ;}}// 处理静态请求void HandleStatic( SOCKET clientSocket , char * filename ){// 直接返回服务器主页if( 0 == strcmp( filename , "/" ) ){strcpy( filename , "/index.html" ) ;}FILE *fp = fopen( filename , "r" ) ;if( fp == 0 ){// 如果找不到响应的数据就返回404状态码,并将其发送给客户端char response[] = "HTTP/1.1 404 NOT FOUND\r\n\r\n";send( clientSocket, response, strlen( response ), 0 );}else{// 在文件目录中找响应的资源int file_size ;char *content;char response[1024];fseek( fp, 0, SEEK_END );file_size = ftell( fp );fseek( fp, 0, SEEK_SET );content = (char*)malloc( file_size + 1 );fread( content, file_size, 1, fp );content[file_size] = 0;// 检测请求文件的类型char fileType[20] ;GetFileType( filename , fileType ) ;// 发送请求headers到客户端sprintf( response, "HTTP/1.1 200 OK\r\n") ;sprintf( response , "%sContent-Type: %s\r\n" , response , fileType ) ;sprintf( response , "%sContent-Length: %d\r\n", response , file_size ) ;sprintf( response , "%sServer: Bobo Server\r\n\r\n" , response ) ;send( clientSocket , response, strlen( response ), 0 ) ;// 发送请求body到客户端sprintf( response , "%s" , content ) ;send( clientSocket , response , strlen( response ) , NULL ) ;free( content ) ;}}// 处理动态请求的函数void HandleDynamicFunc( char * filename , char * args , int & result ) {// 处理动态请求参数char * a = strtok( args , "&" ) ;char * b = strtok( NULL , "&" ) ;int x = atoi( a ) ;int y = atoi( b ) ;if( strstr( filename , "Add" ) ){result = x + y ;}else if( strstr( filename , "Sub" ) ){result = x - y ;}}// 处理动态请求void HandleDynamic( SOCKET clientSocket , char *filename , char * args ) {int result = 0 ;char response[8000] ;  // 响应内容// 执行动态请求HandleDynamicFunc( filename , args , result ) ;// 发送响应头到客户端sprintf( response, "HTTP/1.1 200 OK\r\n") ;sprintf( response , "%sServer: Bobo Server\r\n\r\n" , response ) ;send( clientSocket , response , strlen( response ) , NULL ) ;  //发送响应Body到客户端sprintf(response , "<head><head><title>Welcome to Bobo Server!</title></head>" ) ;sprintf( response , "%s<body>The result is %d</body>" , response , result ) ;send( clientSocket , response , strlen( response ) , NULL ) ; }//解析URIbool ParseURI( SOCKET clientSocket , char * uri , char * filename , char *args ){sprintf( filename , ".%s" , uri ) ;if( !strstr( uri , "dynamic"  ) )  // 静态请求{strcpy( args , "" ) ;strcpy( filename , "." ) ;strcat( filename , uri ) ;if( uri[strlen( uri ) - 1 ] == '/' ){strcat( filename , "index.html" ) ;}return true ;}else  // 动态请求{char * ptr = strstr( uri , "?" ) ;if( ptr ){strcpy( args , ptr + 1 ) ;}else{strcpy( args , "" ) ;}ptr = strstr( uri , "?" ) ;*ptr = '\0' ;strcpy( filename , uri ) ;strcat( uri , "?" ) ;strcat( uri , args ) ;return false ;}}// 解析请求(Parse Request ),如果为静态返回true,如果为动态,返回falsevoid ParseRequest( SOCKET clientSocket , char * request ){// 判断URI请求的是否为静态请求bool is_static ;//  获取请求的命令类型char *method = strtok( request, " " ) ;//获取请求的urichar *uri = strtok( 0 , " " ) ;// 解析URIchar args[20] = { 0 } ;    // URI中的动态请求参数char filename[100] = { 0 } ;  // URI中静态请求中的文件名is_static = ParseURI( clientSocket , uri , filename , args ) ;if( is_static )  // 请求为静态{HandleStatic( clientSocket , filename ) ;}else  // 请求为动态{HandleDynamic( clientSocket , filename , args ) ;}}// 处理请求行:读取请客户端的请求,给出响应void HandleRequestLine( SOCKET clientSocket , char * request ){// 解析URIParseRequest( clientSocket , request ) ;}

WebServer.cpp(主函数)

//////////////////////////////////////////////////////      创建一个很小的WebServer,基于HTTPS协议/////////////////////////////////////////////////////////////////////////#include "helpFunction.h"// 主函数int main(){// 初始化windows socketWSADATA wd ;SOCKET s ;if( WSAStartup( MAKEWORD( 2, 2 ), &wd ) < 0 ){fprintf( stderr, "winsock startup failed\n" ) ;exit( -1 );}// 创建服务器套接字s = socket( AF_INET, SOCK_STREAM, 0 ) ;// 绑定并监听SOCKADDR_IN addr;memset( &addr, 0, sizeof( addr ) );addr.sin_family = AF_INET;addr.sin_port = htons( 827 );addr.sin_addr.s_addr = inet_addr( "127.0.0.1" );int ret = bind( s, (struct sockaddr*)&addr, sizeof( addr ) );if( ret < 0 ){fprintf( stderr, "bind failed\n" );closesocket( s );exit( -1 );}// 进行监听ret = listen( s, 1024 ) ;if( ret < 0 ){fprintf( stderr, "listen failed\n" );closesocket( s );exit( -1 );}char request[8000] = { 0 } ;  // 请求while( true ){// 连接到客户端的套接字SOCKET clientSocket = accept( s, 0, 0 );// 接受客户端的信息recv( clientSocket, request, sizeof( request ), 0 );// 将客户端的信息输出到终端printf( request ) ;// 处理请求,主要功能函数HandleRequestLine( clientSocket , request ) ;}return 0 ;}

应为代码中注释比较清楚,在这里就只说一下重点部分。

实现这个Web服务器的主要就是用到了socket来发送ASCII格式的文本命令,客户端(也包含)浏览器和这个Web服务器之间分别解释命令给出响应即可。

我目前实现的这个Web服务器是仿造《深入理解计算机操作系统》中的一段Linux程序写的Windows程序,实现了一个Web服务器主要的框架,虽然没有考虑到安全和容错问题,但是大部分服务器都是这样一个基于套接字实现的框架。

在这个程序

在这个程序中主要实现了页面访问和加减法操作。熟悉了URI中的基本格式,在这个程序中,进行加法的操作是这样的请求URI127.0.0.1:827/dynamic/Add?3&4,服务器收到这个请求会在后台进行3+4的操作,最后把结果返回给客户端。

顾名思义,URI127.0.0.1:827/dynamic/Sub?3&4进行减法操作。

访问127.0.0.1:827会直接返回服务器首页,即文件index.html。



在上图中,输入的URI中包含了dynamic这个关键字,实际上,这个dynamic本该是一个文件夹的名字,它代表的含义是处理动态请求的区域,一般含有这个dynamic的URI都会进入这个区域找出在这个路径下的处理函数,例如这里的处理函数就是Add,进行加法的一个程序。在我们实现的这个Web服务器中为了简便并没有实现这样的路径查找,只是简单模拟了其逻辑,在后面的优化中会慢慢实现。


要注意的是,浏览器作为客户端访问的时候,服务器发给客户端的响应必须是html格式的文本,而不能是普通的文字信息,必须加上html标签。


实际上,只要多动一下手就可以看到自己想看的结果,那是令人兴奋的!





0 0
原创粉丝点击