注册 登录
编程论坛 Linux教室

一个简单的 HTTP 服务器

madfrogme 发布于 2012-10-12 21:41, 1795 次点击
程序代码:
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "hacking.h"
#include "hacking-network.h"

#define PORT 50000   // 用户连接端口
#define WEBROOT "./webroot" //浏览器的默认目录

void handle_connection(int, struct sockaddr_in *); //处理請求的函数
int get_file_size(int); // 获得打开文件的大小

int main(void) {
   int sockfd, new_sockfd, yes=1;
   struct sockaddr_in host_addr, client_addr;   //
   socklen_t sin_size;

   printf("接受来自端口 %d 的請求\n", PORT);

   if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
      fatal("无法生成套接字");

   if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
      fatal("設定套接字为SO_REUSEADDR ");

   host_addr.sin_family = AF_INET;      
   host_addr.sin_port = htons(PORT);   
   host_addr.sin_addr.s_addr = INADDR_ANY; // 自动設定自身IP
   memset(&(host_addr.sin_zero), '\0', 8); // 结构体的最后8字节清0
   if (bind(sockfd, (struct sockaddr *)&host_addr, sizeof(struct sockaddr)) == -1)
      fatal("Bind失败");

   if (listen(sockfd, 20) == -1)
      fatal("Listen失败");

   while(1) {   
      sin_size = sizeof(struct sockaddr_in);
      new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);
      if(new_sockfd == -1)
         fatal("新连接失败");

      handle_connection(new_sockfd, &client_addr);
   }
   return 0;
}

void handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr) {
   unsigned char *ptr, request[500], resource[500];
   int fd, length;
   
   length = recv_line(sockfd, request);
   
   printf("来自%s:%d 的請求  \"%s\"\n", inet_ntoa(client_addr_ptr->sin_addr), ntohs(client_addr_ptr->sin_port), request);

   ptr = strstr(request, " HTTP/"); // 注意第一个字符是空格
   if(ptr == NULL) { // 找不到HTTP就说明不是一个合法的請求
      printf(" 非HTTP!\n");
   } else {
      *ptr = 0; //找到了HTTP," HTTP/“ 就结束使命了,在第一个空格处加'\0'
      ptr = NULL; // 接下来找"GET "或是" HEAD ",若没有找到,ptr设为NULL就有用了
      if(strncmp(request, "GET ", 4) == 0)  // GET請求
         ptr = request+4; // 請求会是类似于 GET /image.jpg HTTP/1.1"这种形式,就可以理解为什么加4了
      if(strncmp(request, "HEAD ", 5) == 0) // 同理
         ptr = request+5; // 同理

      if(ptr == NULL) { // 找不到"GET " 或是"HEAD "的任何一个
         printf("\t未知請求!\n");
      } else { // 找到"GET "或"HEAD "中任何一个,就开始处理后续的 比如"GET /image.jpg" 的"/image.jpg"部分
         if (ptr[strlen(ptr) - 1] == '/')  // 若只有一个"/",则默认返回index.html
            strcat(ptr, "index.html");     
         strcpy(resource, WEBROOT);     // 开始构建一个完整的路径
         strcat(resource, ptr);         
         fd = open(resource, O_RDONLY, 0); //  试着打开这个文件
         printf("\t打开\'%s\' \t", resource);
         if(fd == -1) { // 没有找到文件
            printf(" 404 Not Found\n");
            send_string(sockfd, "HTTP/1.0 404 NOT FOUND\r\n");
            send_string(sockfd, "Server: Tiny webserver\r\n\r\n");
            send_string(sockfd, "<html><head><title>404 Not Found</title></head>");
            send_string(sockfd, "<body><h1>URL not found</h1></body></html>\r\n");
         } else {      // 找到文件则发送,
            printf(" 200 OK\n");
            send_string(sockfd, "HTTP/1.0 200 OK\r\n");
            send_string(sockfd, "Server: Tiny webserver\r\n\r\n");
            if(ptr == request + 4) { // 上面ptr 已经被設定好,现在看ptr如果是GET 命令的话
               if( (length = get_file_size(fd)) == -1)
                  fatal("取得文件大小失败。");
               if( (ptr = (unsigned char *) malloc(length)) == NULL)
                  fatal("内存的分配失败。");
               read(fd, ptr, length); // 将文件读入内存
               send(sockfd, ptr, length, 0);  // 发送
               free(ptr); // 释放存储文件的内存
            }
            close(fd); // 关闭文件
         }
      }
   }
   shutdown(sockfd, SHUT_RDWR);
}

int get_file_size(int fd) {
   struct stat stat_struct;

   if(fstat(fd, &stat_struct) == -1)
      return -1;
   return (int) stat_struct.st_size;
}


[ 本帖最后由 madfrogme 于 2012-10-12 23:00 编辑 ]
7 回复
#2
madfrogme2012-10-12 21:42
这是hacking.h 文件

程序代码:

void fatal(char *message) {
   char error_message[100];

   strcpy(error_message, "[!!] 致命错误:");
   strncat(error_message, message, 83);
   perror(error_message);
   exit(-1);
}


void *ec_malloc(unsigned int size) {
   void *ptr;
   ptr = malloc(size);
   if(ptr == NULL)
      fatal("ec_malloc()内存分配失败");
   return ptr;
}



void dump(const unsigned char *data_buffer, const unsigned int length) {
   unsigned char byte;
   unsigned int i, j;
   for(i=0; i < length; i++) {
      byte = data_buffer[i];
      printf("%02x ", data_buffer[i]);  
      if(((i%16)==15) || (i==length-1)) {
         for(j=0; j < 15-(i%16); j++)
            printf("   ");
         printf("| ");
         for(j=(i-(i%16)); j <= i; j++) {  
            byte = data_buffer[j];
            if((byte > 31) && (byte < 127))
               printf("%c", byte);
            else
               printf(".");
         }
         printf("\n");
      }
   }
}
#3
madfrogme2012-10-12 21:44
这是hacking-network.h文件
程序代码:
int send_string(int sockfd, unsigned char *buffer) {
   int sent_bytes, bytes_to_send;
   bytes_to_send = strlen(buffer);
   while(bytes_to_send > 0) {
      sent_bytes = send(sockfd, buffer, bytes_to_send, 0);
      if(sent_bytes == -1)
         return 0; // 失敗時には0を返す。
      bytes_to_send -= sent_bytes;
      buffer += sent_bytes;
   }
   return 1;
}

int recv_line(int sockfd, unsigned char *dest_buffer) {
#define EOL "\r\n"
#define EOL_SIZE 2
   unsigned char *ptr;
   int eol_matched = 0;

   ptr = dest_buffer;
   while(recv(sockfd, ptr, 1, 0) == 1) {
      if(*ptr == EOL[eol_matched]) {
         eol_matched++;
         if(eol_matched == EOL_SIZE) {
            *(ptr+1-EOL_SIZE) = '\0';
            return strlen(dest_buffer);
         }
      } else {
         eol_matched = 0;
      }
      ptr++;
   }
   return 0;
}
#4
madfrogme2012-10-12 21:48
$ gcc -o tinyweb tinyweb.c

$ sudo chown root ./tinyweb

$ sudo chmod u+s ./tinyweb




打开浏览器

http://127.0.0.1:50000

http://127.0.0.1:50000/image.jpg

就可以浏览了,只管往webroot文件夹里放文件,来实验显示就可以
#5
有容就大2012-10-14 15:05
哇 厉害 写服务器了!
#6
madfrogme2012-10-14 18:04
回复 5楼 有容就大
等过几天可能还有一些好玩的东西看看能不能搞一下,贴出来
#7
wzj01182012-12-27 12:43
学习了,谢谢
#8
xinglinzhang2013-02-26 20:04
厉害,看得我眼馋
1