一个简单的http server

来源:互联网 发布:禅道数据库 编辑:程序博客网 时间:2024/05/16 08:09

        只有一个源文件的http server。首先打开一个socket:serverSocket1,在PORT定义的端口上侦听,侦听到之后accept出来一个新的socket,放入request[].socket 里。然后当这些socket收到http request之后,把 url提取出来,放在 request[].url里。在SERVER_ROOT 所指明的路径寻找url所描述的文件,加上http 响应头,发送到远端的user agent。测试结果:部署在linux和windows下,使用 firefox 和 ie测试可以访问。

      在开始的时候,端口号没有使用 htons 转变为网络字节次序,花费了很多时间调试这个问题,以后一定要记住这个小曲折。

/* 主要测试select的用法,同时包含socket/bind/listen/accept/connect/send/recv/close/closesocket等的使用开始打开一个服务器socket。通过select侦查 1 键盘输入 2 客户的连接请求  3 客户连接之后的数据传送请求。1 侦查键盘的输入,有的话就输入到bre变量中,测试其值,是-1的话退出循环进而退出程序。2 serverSocket1 收到客户的连接请求后,accept出一个新的socket,放入subSockets[MAX_CLIENTS]中。3 每一个有效的subSockets[] 都在select里侦查,有数据需要收的时候就读进来,printf出去  对应的client 在selectclient.c里*/#define PORT 8010 //liunx 里 96这样的小数 bind会失败#ifdef WIN32#define SERVER_ROOT "c:\\cygwin\\home\\httpd"#define SERVER_ROOT "D:\\live555\\selectaccept"#else#define  SERVER_ROOT "/home/user1/live555/selectaccept"#endif#include <stdio.h>#include <string.h>#ifdef WIN32#include <winsock2.h>#include <ws2tcpip.h>#define socklen_t int#else#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/errno.h>#define closesocket close#endif #define LEN 20240 #define MAX_CLIENTS  32struct Request{#defineURL_LENGTH 1024  int socket ;char url[URL_LENGTH] ;//int  responseSent;} ;int main(){   char buf[LEN] ;   int serverSocket1  ;   struct Request request[MAX_CLIENTS] = {0} ;   volatile int sockets = 0 , is;   int selected ;   int readed;      struct sockaddr   remoteAddr ;   int size_remoteAddr = sizeof( remoteAddr )  ;   struct sockaddr_in sockAddr_in = {0};   int nfds = 0 ;   fd_set readfds ;   fd_set writefds ;   int re ;   int bre;   int rc ;   struct timeval ttv= {0, 10000};#ifdef WIN32    WSADATA wsd ;serverSocket1 = WSAStartup( WINSOCK_VERSION , &wsd ) ;#endif      printf("start\n" ) ;   serverSocket1 = socket( PF_INET , SOCK_STREAM ,  0 ) ;      if( serverSocket1 < 0 )      printf("open server socket 1 error\n" ) ;      printf("open server socket  %d \n" , serverSocket1 ) ;   sockAddr_in.sin_family = AF_INET ;   sockAddr_in.sin_addr.s_addr =  INADDR_ANY ;   sockAddr_in.sin_port = htons(PORT) ; //必须是网络字节顺序   re = bind( serverSocket1 , (struct sockaddr* )&sockAddr_in , sizeof( sockAddr_in ) ) ;   printf(" bind ok %d\n" , re ) ;   re = listen( serverSocket1 , 4 ) ;   printf("listen ok %d\n" , re ) ;   while ( 1 ) {//   printf("1 time:%d\n" , GetTickCount() ) ;        FD_ZERO(  & readfds  ) ; FD_ZERO( &writefds ) ; nfds = serverSocket1;     FD_SET( serverSocket1 , & readfds ) ;#ifndef WIN32     FD_SET( 0 , & readfds ) ; //stdin,linux ; 只在uinx/linux下起作用,windows下会造成select错误WSAENOTSOCK   #endif for( is = 0 ; is < sockets ; is ++ ) {    FD_SET( request[is].socket , &readfds ) ;FD_SET( request[is].socket , &writefds ) ;    if( nfds <  request[is].socket )    nfds = request[is].socket ; }     nfds ++ ;     selected = select ( nfds , &readfds , &writefds ,0 ,&ttv ) ;#ifdef WIN32 if( selected < 0 ) re = WSAGetLastError() ; //WSAENOTSOCK#endif        if( selected <= 0 )       continue ;     if( FD_ISSET( 0 , &readfds )) { /* 从stdin 标准输入-1的话整个程序退出 ,只在uinx/linux下起作用*/        scanf("%d", &bre) ;if( bre == -1 ) {printf("you enter -1 is to exit\n" ) ; break ;} }     for( is = 0 ; is < sockets ; is ++ ) {        if( FD_ISSET( request[is].socket  ,  &readfds )  ) {            readed = recv( request[is].socket  , buf , LEN ,0  ) ;printf("readed=%d\n" , readed ) ;if( readed <= 0  ) { //对方已经关闭该socket,见注释1 ,在linux ,windows和cygwin下返回值一直是0 , errno 也是0               closesocket( request[is].socket ) ;   request[is].socket = -1 ;   request[is].url[0] = 0 ;         request[is] = request[sockets-1] ;   sockets -- ;   int ern = errno ;   char* str   ;#ifdef WIN32   ern = WSAGetLastError() ;   printf("winscoket error no : %d sockets:%d\n" , ern  , sockets ) ;#else   printf("linux scoket error no : %d %s sockets:%d\n" , ern , strerror(ern) , sockets ) ;    #endif}else {        char a[10] , b[10] ;        //printf("read from subSockets[%d] : %s\n" , is , buf ) ;  sscanf(buf , "%s%s%s", a, request[is].url, b ) ;  printf("Request line:%s %s %s\n" , a , request[is].url , b ) ;}}if(   FD_ISSET( request[is].socket  ,  &writefds )  ) {      //printf("enter writeable , url:%s \n" , request[is].url ) ; if(  strlen( request[is].url ) >= 1  ) {   FILE* fp ;   int nnn,flen , sendlen ;char name[256]={0} ;nnn = sprintf( name , "%s" , SERVER_ROOT ) ;strcat( name , request[is].url ) ;   if( strcmp( request[is].url , "/" ) == 0 )   strcat( name , "index.html" ) ;     //printf("name:%s \n" , name ) ;   fp = fopen( name , "rb" ) ;   char ctype[16] ;   if( strlen(name) > 3 ) {   if( strcmp( name+strlen(name)-3 , "mp3" ) == 0 )    strcpy( ctype , "audio/mp3" ) ;   else if( strcmp( name+strlen(name)-3 , "png" ) == 0 )    strcpy( ctype , "image/png" ) ;   else   strcpy( ctype , "text/html" ) ;   } else    strcpy( ctype , "text/html" ) ;      if( fp ) {           printf("name:%s \n" , name ) ;   fseek( fp ,  0 , SEEK_END ) ;   flen = ftell( fp ) ;   fseek( fp , 0 , SEEK_SET  ) ;   nnn = sprintf( buf , "HTTP/1.1 200 OK\r\n" ) ;   nnn += sprintf( buf+nnn , "Server: GoAhead\r\n" ) ;   nnn += sprintf( buf + nnn , "Content-Length: %d\r\n" , flen ) ;   nnn += sprintf( buf + nnn , "Content-Type: %s\r\n" , ctype );   nnn += sprintf( buf + nnn , "\r\n" );   nnn += fread( buf+nnn , 1 , flen , fp ) ;   fclose( fp ) ;   sendlen = send( request[is].socket , buf , nnn , 0 ) ;   printf("send %d sent %d :send %s\n", nnn , sendlen,name ) ;   if( nnn == sendlen )   request[is].url[0] = 0 ;   }else {            static int dd = 0 ;             if( dd %10 == 0 )               printf("name:%s", name ) ;            dd ++ ;            }           }}      }     if( FD_ISSET( serverSocket1 ,  &readfds )  &&  ( sockets < MAX_CLIENTS)  ) {/*可以在readfds查到accept信号  因为客户方有连接需求时,会发送报文,所以它就变成可读状态 */        request[sockets].socket = accept( serverSocket1 , &remoteAddr ,(socklen_t*) &size_remoteAddr ) ;        sockets ++ ; printf("connected romote address :  %d\n" , ((struct sockaddr_in*)&remoteAddr)->sin_port ) ;      }   }   if( serverSocket1 >= 0 )   closesocket( serverSocket1 ) ;   for( is = 0 ; is < sockets ; is ++ ) {        if(  request[is].socket  >= 0  ) {            closesocket( request[is].socket );}   }   return 0 ;}/* 注1:来自网上,但测试结果和他描述不一样利用select()检测对方Socket关闭的问题: 仍然是本地Socket有东东可读,因为对方Socket关闭时,会发一个关闭连接 通知报文,会马上被select()检测到的。关于TCP的连接(三次握手)和关 闭(二次握手)机制,敬请参考有关TCP/IP的书籍。 不知是什么原因,UNIX好象没有提供通知进程关于Socket或Pipe对方关闭的 信号,也可能是cpu所知有限。总之,当对方关闭,一执行recv()或read(), 马上回返回-1,此时全局变量errno的值是115,相应的sys_errlist[errno] 为"Connect refused"(请参考/usr/include/sys/errno.h)。所以,在上 篇的for(;;)...select()程序块中,当有东西可读时,一定要检查recv()或 read()的返回值,返回-1时要作出关断本地Socket的处理,否则select()会 一直认为有东西读,其结果曾几令cpu伤心欲断针脚。*/
下载区 :  https://sourceforge.net/projects/avccodecdemo/files/selectaccept.zip/download
原创粉丝点击