Socket网络编程--简单Web服务器(2)
上一小節(jié)通過閱讀開源的Web服務(wù)器--tinyhttpd。大概知道了一次交互的請求信息和應(yīng)答信息的具體過程。接下來我就自己簡單的實現(xiàn)一個Web服務(wù)器。
下面這個程序只是實現(xiàn)一個簡單的框架出來。這次先實現(xiàn)能夠Accept客戶端的請求。
簡單創(chuàng)建web服務(wù)器
webserver.h
1 #include <iostream> 2 #include <string> 3 #include <string.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <errno.h> 7 #include <sys/types.h> 8 #include <sys/socket.h> 9 #include <netinet/in.h> 10 #include <arpa/inet.h> 11 #include <unistd.h> 12 #include <pthread.h> 13 #include <thread>//使用c++11的多線程 14 15 using namespace std; 16 17 class WebServer 18 { 19 public: 20 WebServer(); 21 ~WebServer(); 22 int ServerInit(u_short port); 23 int ServerError(string str); 24 int ServerAccept(); 25 int ServerClose(); 26 int ServerRequest(int cli_fd); 27 int get_line(int cli_fd,char * buf,int size);//來自tinyhttpd 28 29 int Page_200(int cli_fd); 30 int Page_501(int cli_fd); 31 private: 32 int httpd; 33 }; 34 35 int WebServer::ServerRequest(int cli_fd) 36 { 37 char buf[1024]; 38 int size=1024; 39 int i=1; 40 memset(buf,0,sizeof(buf)); 41 while((i>0)&&strcmp("\n",buf)) 42 { 43 i=get_line(cli_fd,buf,sizeof(buf)); 44 cout<<buf; 45 } 46 if(fork()==0) 47 { 48 //處理階段 49 execl("/bin/ls","ls","/home/myuser/",NULL); 50 } 51 Page_200(cli_fd); 52 close(cli_fd); 53 return 0; 54 } 55 int WebServer::ServerAccept() 56 { 57 struct sockaddr_in cli_sin; 58 socklen_t cli_len=sizeof(cli_sin); 59 int cli_fd; 60 cli_fd=accept(httpd,(struct sockaddr *)&cli_sin,&cli_len);//阻塞等待連接 61 if(cli_fd==-1) 62 ServerError("Fail to accept"); 63 cout<<"連接進來的IP: "<<inet_ntoa(cli_sin.sin_addr)<<":"<<ntohs(cli_sin.sin_port)<<endl; 64 return cli_fd; 65 } 66 int WebServer::ServerInit(u_short port) 67 { 68 struct sockaddr_in sin; 69 int on; 70 httpd=socket(PF_INET,SOCK_STREAM,0); 71 if(httpd==-1) 72 ServerError("Fail to Socket"); 73 //init sockaddr_in 74 sin.sin_family=AF_INET; 75 sin.sin_port=htons(port); 76 sin.sin_addr.s_addr=htonl(INADDR_ANY); 77 bzero(&(sin.sin_zero),8); 78 setsockopt(httpd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); 79 if(::bind(httpd,(struct sockaddr *)&sin,sizeof(struct sockaddr))==-1) 80 ServerError("Fail to bind"); 81 //如果port指定為零那么就隨機打開一個端口 82 if(port==0) 83 { 84 socklen_t len=sizeof(sin); 85 if(getsockname(httpd,(struct sockaddr *)&sin,&len)==-1) 86 ServerError("Fail to getsockname"); 87 port=ntohs(sin.sin_port); 88 } 89 if(listen(httpd,100)<0) 90 ServerError("Fail to listen"); 91 return port; 92 } 93 /// 94 int WebServer::get_line(int cli_fd,char * buf,int size) 95 { 96 int i=0; 97 char c='\0'; 98 int n; 99 while((i<size-1)&&(c!='\n')) 100 { 101 n=recv(cli_fd,&c,1,0); 102 if(n>0) 103 { 104 if(c=='\r') 105 { 106 n=recv(cli_fd,&c,1,MSG_PEEK); 107 if((n>0)&&(c=='\n')) 108 recv(cli_fd,&c,1,0); 109 else 110 c='\n'; 111 } 112 buf[i]=c; 113 i++; 114 } 115 else 116 c='\n'; 117 } 118 buf[i]='\0'; 119 return i; 120 } 121 int WebServer::ServerError(string str) 122 { 123 perror(str.c_str()); 124 exit(-1); 125 } 126 int WebServer::ServerClose() 127 { 128 close(httpd); 129 return 0; 130 } 131 int WebServer::Page_200(int cli_fd) 132 { 133 char buf[1024]; 134 sprintf(buf, "HTTP/1.1 200 OK\r\n"); 135 send(cli_fd, buf, strlen(buf), 0); 136 sprintf(buf, "Server:wunaozai.cnblogs.com\r\n"); 137 send(cli_fd, buf, strlen(buf), 0); 138 sprintf(buf, "Content-Type: text/html\r\n"); 139 send(cli_fd, buf, strlen(buf), 0); 140 sprintf(buf, "\r\n"); 141 send(cli_fd, buf, strlen(buf), 0); 142 sprintf(buf, "<HTML><HEAD><TITLE>Hello World\r\n"); 143 send(cli_fd, buf, strlen(buf), 0); 144 sprintf(buf, "</TITLE></HEAD>\r\n"); 145 send(cli_fd, buf, strlen(buf), 0); 146 sprintf(buf, "<BODY><h1>Hello World</h1>\r\n"); 147 send(cli_fd, buf, strlen(buf), 0); 148 sprintf(buf, "</BODY></HTML>\r\n"); 149 send(cli_fd, buf, strlen(buf), 0); 150 } 151 int WebServer::Page_501(int cli_fd) 152 { 153 char buf[1024]; 154 sprintf(buf, "HTTP/1.1 501 Method Not Implemented\r\n"); 155 send(cli_fd, buf, strlen(buf), 0); 156 sprintf(buf, "Server:wunaozai.cnblogs.com"); 157 send(cli_fd, buf, strlen(buf), 0); 158 sprintf(buf, "Content-Type: text/html\r\n"); 159 send(cli_fd, buf, strlen(buf), 0); 160 sprintf(buf, "\r\n"); 161 send(cli_fd, buf, strlen(buf), 0); 162 sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n"); 163 send(cli_fd, buf, strlen(buf), 0); 164 sprintf(buf, "</TITLE></HEAD>\r\n"); 165 send(cli_fd, buf, strlen(buf), 0); 166 sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n"); 167 send(cli_fd, buf, strlen(buf), 0); 168 sprintf(buf, "</BODY></HTML>\r\n"); 169 send(cli_fd, buf, strlen(buf), 0); 170 } 171 WebServer::~WebServer() 172 { 173 } 174 WebServer::WebServer() 175 { 176 }webserver.cpp
1 #include "webserver.h" 2 3 int main(int argc,char **argv) 4 { 5 WebServer ws;//實例化web服務(wù)器 6 ws.ServerInit(8080);//打開8080端口 7 pid_t pid; 8 int cli_fd; 9 while(1) 10 { 11 cli_fd=ws.ServerAccept();//程序會在這個函數(shù)阻塞 12 ws.ServerRequest(cli_fd);//這個函數(shù)會創(chuàng)建一個進程對請求頭進行處理并發(fā)送應(yīng)答信息給客戶端 13 } 14 ws.ServerClose();//關(guān)閉服務(wù)器 15 16 return 0; 17 }makefile
1 main: 2 g++ webserver.cpp -std=c++0x -g -o webserver 3 run: 4 ./webserver下面這個是運行時的截圖
增加了幾個函數(shù)get_line(由于socket的讀取方式好像沒有一行一行的讀取)各種Page信息還有一個ServerRequest函數(shù)。
ServerRequest:這個函數(shù)里面有一個fork函數(shù)創(chuàng)建多進程。一開始我是把fork的創(chuàng)建放在主函數(shù)的,然后ServerRequest不用fork函數(shù)。但是最后會出現(xiàn)一個問題就是,每次在客戶端發(fā)出請求后服務(wù)器一直沒有給出應(yīng)答,客戶端瀏覽器一直處于加載狀態(tài),然后強制性終止程序,瀏覽器才會有反映。不知道原因,弄了很久。一直在想以前寫的那篇HTTP是沒有問題的。一查才知道原來我以前用的請求頭Connection:close 而瀏覽器現(xiàn)在這個Connection默認的值是keep-alive。是長連接。所以才會出現(xiàn)這個情況。?
get_line:由于socket沒有一整行的讀取數(shù)據(jù),所以這里使用tinyhttpd這個程序里的代碼。
Page_200 Page_501 Page_404 ... ...
到這里服務(wù)器可以簡單的返回一個200ok的頁面了。接下來要實現(xiàn)的是實現(xiàn)對第一行請求信息的處理,接下來的處理基本都是在ServerRequest這個函數(shù)里進行。
帶處理get/post方法的WEB服務(wù)器
1 int WebServer::ServerRequest(int cli_fd) 2 { 3 char buf[1024]; 4 int size=1024; 5 int i,j; 6 char method[255];//用于保存請求方式 7 char url[512]; 8 memset(buf,0,sizeof(buf)); 9 //獲取第一行請求信息 一般格式為: GET / HTTP/1.1 10 // POST / HTTP/1.1 11 size=get_line(cli_fd,buf,sizeof(buf)); 12 cout<<"\t\t"<<buf<<endl; 13 i=0,j=0; 14 //截取第一個單詞 15 while(!isspace(buf[j]) && (i<sizeof(method)-1)) 16 { 17 method[i]=buf[j]; 18 i++;j++; 19 } 20 method[i]='\0'; 21 //取第一個與第二個單詞之間的空格 22 while(isspace(buf[j]) && (j<sizeof(buf))) 23 j++; 24 //截取第二個單詞 25 i=0; 26 while(!isspace(buf[j]) && (i<sizeof(url)-1) && (j<sizeof(buf))) 27 { 28 url[i]=buf[j]; 29 i++;j++; 30 } 31 url[i]='\0'; 32 33 if(strcasecmp(method,"GET") && strcasecmp(method,"POST")) 34 { 35 Page_501(cli_fd); 36 return -1; 37 } 38 39 if(strcasecmp(method,"GET")==0) 40 { 41 cout<<"此次請求的方式是GET方法"<<endl; 42 } 43 else if(strcasecmp(method,"POST")==0) 44 { 45 cout<<"此次請求的方式是POST方法"<<endl; 46 } 47 cout<<"此次請求的地址為:"<<url<<endl; 48 49 while((size>0)&&strcmp("\n",buf)) 50 { 51 size=get_line(cli_fd,buf,sizeof(buf)); 52 } 53 54 if(fork()==0) 55 { 56 //處理階段 57 //execl("/bin/ls","ls","/home/myuser/",NULL); 58 Page_200(cli_fd); 59 } 60 close(cli_fd); 61 return 0; 62 }運行的結(jié)果
可以看出只要在瀏覽器地址欄寫上什么就可以在GET后截取到,只是中文就顯示成16進制了
還有這個成功獲取第一個頁面后會有一個獲取/favicon.ico這個請求,這個是自動的,我沒有在地址欄輸入的。如果有學(xué)過靜態(tài)頁面HTML編寫的就知道,這個是網(wǎng)頁的圖標,一般在主目錄的根目錄下。
在這里沒有看到圖標是由于這個favicon.ico不是通過簡單text/html的Content-Type顯示的所以這里就沒有,等以后實現(xiàn)image發(fā)送就可以看到了。好了這一小節(jié)就到這里了。
?
參考資料:?http://blog.csdn.net/hanchaoman/article/details/5685582
本文地址:?http://www.cnblogs.com/wunaozai/p/3936295.html
轉(zhuǎn)載于:https://www.cnblogs.com/wunaozai/p/3936295.html
總結(jié)
以上是生活随笔為你收集整理的Socket网络编程--简单Web服务器(2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Eclipse生成JavaDoc时指定字
- 下一篇: 1号楼+-0.000相当于绝对标高4.3