您的位置:首页 > 理论基础 > 计算机网络

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

2014-01-14 11:26 881 查看
在开发网站的过程中,首先我们需要配置一个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 ) ;

//解析URI
bool ParseURI( SOCKET clientSocket , char * uri , char * filename , char *args ) ;

// 解析请求(Parse Request ),如果为静态返回true,如果为动态,返回false
void 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 ) ;
}

//解析URI
bool 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,如果为动态,返回false
void ParseRequest( SOCKET clientSocket , char * request )
{
// 判断URI请求的是否为静态请求
bool is_static ;

// 获取请求的命令类型
char *method = strtok( request, " " ) ;
//获取请求的uri
char *uri = strtok( 0 , " " ) ;

// 解析URI
char 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 )
{
// 解析URI
ParseRequest( clientSocket , request ) ;
}

WebServer.cpp(主函数)
//////////////////////////////////////////////////
//
// 创建一个很小的WebServer,基于HTTPS协议
//
/////////////////////////////////////////////////////
//////////////////

#include "helpFunction.h"

// 主函数
int main()
{
// 初始化windows socket
WSADATA 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中的基本格式,在这个程序中,进行加法的操作是这样的请求URI:127.0.0.1:827/dynamic/Add?3&4,服务器收到这个请求会在后台进行3+4的操作,最后把结果返回给客户端。
顾名思义,URI:127.0.0.1:827/dynamic/Sub?3&4进行减法操作。
访问127.0.0.1:827会直接返回服务器首页,即文件index.html。



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

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

实际上,只要多动一下手就可以看到自己想看的结果,那是令人兴奋的!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐