C語言實現的Web服務器

/**************filename: Server.cpp****************
 該程序經過標準socket實現簡單Http服務器
 運行該服務器能夠經過瀏覽器訪問服務器目錄下的
 Html文件和jpg圖片 完成初步的Http服務器功能
***************************************************/
#include <winsock.h>
#include <sys/stat.h>
#include <iostream>
using namespace std;
#define SERVER_PORT 10000         //自定義的服務端口
#define HOSTLEN 256           //主機名長度
#define BACKLOG 10           //同時等待的鏈接個數
/**************************************
 該方法包裝了send()
 經過該方法發送數據 可以所有發出
 沒有遺漏
**************************************/
int sendall(int s, char *buf, int *len) {
 int total = 0;           // 已經發送字節數
 int bytesleft = *len;                                   //還剩餘多少字節
 int n;
 while(total < *len) {
  n = send(s, buf+total, bytesleft, 0);
  if (n == -1) { break; }
  total += n;
  bytesleft -= n;
 }
 *len = total;           // 返回實際發送出去的字節數
 return n==-1?-1:0;          // 成功發送返回0 失敗-1
}
/**************************************
 該方法處理錯誤請求
 並向客戶端發送錯誤信息
**************************************/
void wrong_req(int sock) {
 char* error_head = "HTTP/1.0 501 Not Implemented\r\n"; //輸出501錯誤
 int len = strlen(error_head);
 if (sendall(sock, error_head, &len) == -1) {   //向客戶發送
  printf("Sending failed!");
  return;
 }     
 char* error_type = "Content-type: text/plain\r\n"; 
 len = strlen(error_type);
 if (sendall(sock, error_type, &len) == -1) {
  printf("Sending failed!");
  return;
 }
 char* error_end = "\r\n";
 len = strlen(error_end);
 if (sendall(sock, error_end, &len) == -1) {
  printf("Sending failed!");
  return;
 }
 char* prompt_info = "The command is not yet completed\r\n";
 len = strlen(prompt_info);
 if (sendall(sock, prompt_info, &len) == -1) {
  printf("Sending failed!");
  return;
 }
}
/**********************************
 該方法判斷用戶請求的文件是否存在
 不存在返回true 存在返回false
***********************************/
bool not_exit(char* arguments) {
 struct stat dir_info;
 return (stat(arguments, &dir_info) == -1);
}
/*************************************
 所請求的文件不存在
*************************************/
void file_not_found(char* arguments, int sock) {
 char* error_head = "HTTP/1.0 404 Not Found\r\n";   //構造404錯誤head
 int len = strlen(error_head);
 if (sendall(sock, error_head, &len) == -1) {    //向客戶端發送
  printf("Sending error!");
  return;
 }  
 char* error_type = "Content-type: text/plain\r\n";
 len = strlen(error_type);
 if (sendall(sock, error_type, &len) == -1) {
  printf("Sending error!");
  return;
 }
 char* error_end = "\r\n";
 len = strlen(error_end);
 if (sendall(sock, error_end, &len) == -1) {
  printf("Sending error!");
  return;
 }
 char prompt_info[50] = "Not found:  ";
 strcat(prompt_info, arguments);
 len = strlen(prompt_info);
 if (sendall(sock, prompt_info, &len) == -1) {    //輸出未找到的文件
  printf("Sending error!");
  return;
 }   
}
/*************************************
 發送Http協議頭部信息
 其中包括響應類型和Content Type
*************************************/
void send_header(int send_to, char* content_type) {
 
 char* head = "HTTP/1.0 200 OK\r\n";     //正確的頭部信息
 int len = strlen(head);
 if (sendall(send_to, head, &len) == -1) {   //向鏈接的客戶端發送數據
  printf("Sending error");
  return;
 }
 if (content_type) {         //content_type不爲空
  char temp_1[30] = "Content-type: ";    //準備好要鏈接的字串
  strcat(temp_1, content_type);     //構造content_type
  strcat(temp_1, "\r\n");
  len = strlen(temp_1);
  if (sendall(send_to, temp_1, &len) == -1) {
   printf("Sending error!");
   return;
  }
 }
}
/***********************************
 取得用戶所請求的文件類型
 即文件後綴 (.html .jpg .gif)
************************************/
char* file_type(char* arg) {
 char * temp;          //臨時字符串指針
 if ((temp=strrchr(arg,'.')) != NULL) {    //取得後綴
  return temp+1;
 }
 return "";           //若是請求的文件名中沒有. 則返回空串
}
/*************************************
 該方法爲程序核心
 負責真正發送文件 如*.html *.jpg等
*************************************/
void send_file(char* arguments, int sock) {
 char* extension = file_type(arguments);    //得到文件後綴名
 char* content_type = "text/plain";     //初始化type='text/plain'
 FILE* read_from;         //本地文件指針從該文件中讀取.html .jpg等
 int readed = -1;         //每次讀得的字節數
 
 if (strcmp(extension, "html") == 0) {    //發送內容爲html
  content_type = "text/html";
 }
 if (strcmp(extension, "gif") == 0) {    //發送內容爲gif
  content_type = "image/gif";
 }
 if (strcmp(extension, "jpg") == 0) {    //發送內容爲jpg
  content_type = "image/jpg";
 }
 read_from = fopen(arguments, "r");     //打開用戶指定的文件準備讀取
 if(read_from != NULL) {        //指針不爲空
  char read_buf[128];        //讀文件時的字節緩存數組
  send_header(sock, content_type);    //發送協議頭
  send(sock, "\r\n", 2, 0);      //再加一個"\r\n" 不能缺乏 格式要求
  while(!feof(read_from)) {      //判斷文件是否已經結束
   fgets(read_buf, 128, read_from);   //讀取
   int len = strlen(read_buf);
   if (sendall(sock, read_buf, &len) == -1) { //發送數據
    printf("Sending error!");    //出現發送錯誤顯示到控制檯 繼續發送
    continue;
   }
  }
 }
}
/***********************************
 解析並處理用戶請求
***********************************/
void handle_req(char* request, int client_sock) {
 char command[BUFSIZ];        //保存解析到的命令字段 GET PUT
 char arguments[BUFSIZ];        //保存解析到的請求的文件
 /*
  * 在用戶請求前加上當前目錄符號
  */
 strcpy(arguments, "./");       //注意該符號在不一樣操做系統的區別
 /*
  * 解析請求
  */
 if (sscanf(request, "%s%s", command, arguments+2) != 2) {
  return;           //解析出錯在返回
 }
 
 printf("handle_cmd:    %s\n",command);    //向控制檯輸出此時的命令
 printf("handle_path:   %s\n",arguments);   //向控制檯輸出此時的請求路徑
 
 if (strcmp(command, "GET") != 0) {     //請求命令格式是否正確
  wrong_req(client_sock);
  return;
 }
 if (not_exit(arguments)) {       //請求的文件是否存在 
  file_not_found(arguments, client_sock);
  return;
 }
 send_file(arguments, client_sock);     //命令格式及請求路徑正確則發送數據
 
 return;
}
/*************************************
 該方法構造服務器端的SOCKET
 返回構造好的socket描述符
*************************************/
int make_server_socket() {
 struct sockaddr_in server_addr;       //服務器地址結構體
 int tempSockId;           //臨時存儲socket描述符
 tempSockId = socket(PF_INET, SOCK_STREAM, 0);
 
 if (tempSockId == -1) {         //若是返回值爲-1 則出錯
  return -1;
 }
 /*
  * 填充服務器鏈接信息
  */
 server_addr.sin_family = AF_INET;
 server_addr.sin_port = htons(SERVER_PORT);
 server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //本地地址
 memset(&(server_addr.sin_zero), '\0', 8);
 if (bind(tempSockId, (struct sockaddr *)&server_addr,
  sizeof(server_addr)) == -1) {       //綁定服務若是出錯 則返回-1
  printf("bind error!\n");
  return -1;
 }
 if (listen(tempSockId, BACKLOG) == -1 ) {     //開始監聽
  printf("listen error!\n");
  return -1;
 }
 return tempSockId;           //返回取得的SOCKET
}
/***********************
 主函數main()
 程序入口
***********************/
void main(int argc, char * argv[]) {
 /*
  * 調用WSAStartup() 便於訪問sockets library
  */
 WSADATA wsaData;
 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
  fprintf(stderr, "WSAStartup failed.\n");
  exit(1);
 }
 printf("My web server started...\n");
 int server_socket;        //服務器的socket
 int acc_socket;         //接收到的用戶鏈接的socket
 int sock_size = sizeof(struct sockaddr_in); 
 struct sockaddr_in user_socket;     //客戶鏈接信息
 server_socket = make_server_socket();   //建立服務器端的socket
 if (server_socket == -1) {      //建立socket出錯
  printf("Server exception!\n");
  exit(2);
 }
 /*
  * 主循環
  */
 while(true) {
  acc_socket = accept(server_socket, (struct sockaddr *)&user_socket, &sock_size); //接收鏈接
 
  //cout << inet_ntoa(user_socket.sin_addr) << endl;    //測試用:-)//
 
  /*
   * 讀取客戶請求
   */
  int numbytes;
  char buf[100];
  if ((numbytes=recv(acc_socket, buf, 99, 0)) == -1) {
   perror("recv");
   exit(1);
  }
 
  //printf("buf ... %s", buf);      //測試用
  /*
   * 處理用戶請求
   */
  handle_req(buf, acc_socket);
 }
}
/**************程序結束Server.cpp******************/html

相關文章
相關標籤/搜索