順著教程1,以及教程2,完成以上2個教程了,我們就可以進(jìn)行這個章節(jié)的操作了,完成一個支持靜態(tài)頁面的web服務(wù)器了,再次獻(xiàn)上運(yùn)行成功的截圖

那咱們開始做些準(zhǔn)備工作,并測試些代碼,看看瀏覽器發(fā)送給Server端的跟Client發(fā)送給Server端有什么不同
1.準(zhǔn)備aaa.html這個界面
<html>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>哈哈 鵬哥的第一個web服務(wù)器</title>
<body>
</body>

<span style="font-size:20px;font-color:yellow">鼓掌鼓掌</span>
</html>
2.編寫測試代碼,查看瀏覽器發(fā)送這個鏈接地址時Server端收到了哪些數(shù)據(jù)
在Server端編寫以下代碼
while (1) {
n = Read(connfd, buf, MAXLINE);
if (n == 0) {
printf("the other side has been closed.\n");
break;
}
printf("received from %s at PORT %d,message is %s\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port),buf);
//解析來自瀏覽器的buf
printf("buf=%s\n",buf);
}
主要查看
//解析來自瀏覽器的buf
printf("buf=%s\n",buf);
這個代碼到底打印了什么

buf_result.png
哦,可以看到這個是請求的header頭,在chrome中也可以看到一模一樣的,主要看第一行,GET /aaa.html HTTP/1.1,
嗯,可以看到瀏覽器傳輸給Server是什么信息
協(xié)議是HTTP/1.1,請求方式是GET,請求的文件是 當(dāng)前目錄的aaa.html文件
很簡單,所以我們可以很輕易的得出將aaa.html文件內(nèi)容傳輸給瀏覽器就好了,下面開始碼代碼了
解析來自瀏覽器的buf
//解析來自瀏覽器的buf
printf("buf=%s\n",buf);
sscanf(buf, "%s %s %s", method, uri, version);
printf("method:%s\n",method);
printf("uri:%s\n",uri);
printf("version:%s\n",version);
這里的方式有很多,在php中使用expode(" ",$str),這樣的方式就能夠分割第一行的字符串,得到method,uri,version。但是c中沒有這樣的方式,c語言中有兩種方式可以實現(xiàn),大家可以分別選擇去使用,這里我用了第一種方式
1.int sscanf( string str, string fmt, mixed var1, mixed var2 ... );
2.char * strtok(char *s, const char *delim);
找到uri指向的文件,并包裝返回結(jié)果回寫給瀏覽器
//這塊先支持get請求
if(strcasecmp(method, "GET") == 0 && strstr(version,"HTTP")) {
printf("method=%s,uri=%s\n",method,uri);
//這里是根據(jù)uri得出filename的位置
if(find_url(uri,filename)) {
printf("filename=%s\n",filename);
//封裝response
wrap_response(connfd,filename);
}
}
這里我將find_url()以及wrap_response()寫到wrap_socket.c文件里面
int find_url(char *uri,char *filename) {
char *ptr;
//說明走的是靜態(tài)網(wǎng)址
if(!strstr(uri, "cgi-bin")) {
strcpy(filename, ".");
strcat(filename, uri);
if (uri[strlen(uri)-1] == '/')
strcat(filename, "index.html");
return 1;
}else {
ptr = index(uri, '?');
strcpy(filename, ".");
strcat(filename, uri);
return 0;
}
}
/**
* @desc 封裝response 支持靜態(tài)頁面以及php頁面
*
*/
void wrap_response(int connfd,char *filename) {
struct stat sbuf;
int filefd,phpfd;
char *php_result;
char *srcp;
char response[MAXLINE],filetype[MAXLINE];
if(stat(filename,&sbuf) < 0) {
error_response(connfd);
exit(1);
}else {
//獲取文件類型
get_filetype(filename,filetype);
//打開文件并將其寫入內(nèi)存,并由瀏覽器展示
filefd = open(filename,O_RDONLY);
//拼接靜態(tài)文件的response頭
sprintf(response, "HTTP/1.0 200 OK\r\n");
sprintf(response, "%sServer: Pengge Web Server\r\n",response);
sprintf(response, "%sConnection: close\r\n",response);
sprintf(response, "%sContent-length: %lld\r\n",response,sbuf.st_size);
sprintf(response, "%sContent-type: %s\r\n\r\n",response,filetype);
Write(connfd, response, strlen(response));
printf("Response headers:\n");
printf("%s\n",response);
srcp = mmap(0, sbuf.st_size, PROT_READ, MAP_PRIVATE, filefd, 0);
Close(filefd);
//清空srcp空間
Write(connfd, srcp, sbuf.st_size);
munmap(srcp, sbuf.st_size);
}
}
這里代碼能看出來主要邏輯是這樣的:
- stat(filename,&sbuf) //判斷是否有這個文件
- get_filetype(filename,filetype);//獲取文件類型
- 封裝response,具體內(nèi)容可以參考chrome下的response頭
- mmap()讀取filename 的內(nèi)容寫給瀏覽器
srcp = mmap(0, sbuf.st_size, PROT_READ, MAP_PRIVATE, filefd, 0);
Close(filefd);
//清空srcp空間
Write(connfd, srcp, sbuf.st_size);
這個時候調(diào)調(diào)bug,輸入
<b>這個查看下是否有頁面出來了吧!</b>
做出來是不是非常有成就感,接下來就要進(jìn)行動態(tài)頁面的開發(fā)了,在瀏覽器中輸入
在第四章,我們將要實現(xiàn)支持動態(tài)頁面以及報錯界面的制作以及對該項目可以增加功能點的建議原文章鏈接