用C一步步開發(fā)web服務(wù)器(1)

對于php程序員拄轻,對于web服務(wù)器來說再熟悉不過了俺孙,apache志鞍,nginx。娜汁。但是內(nèi)心一直想開發(fā)出一個屬于自己的web服務(wù)器筏养,所以借此機會斧抱,用c開發(fā)出了一款web服務(wù)器。作為1.0版本渐溶,他實現(xiàn)了以下功能

  • 完成基礎(chǔ)的tcp連接辉浦,支持基礎(chǔ)的client與其連接
  • 使用fork()來支持并發(fā)訪問服務(wù)器
  • 簡單的http訪問,支持靜態(tài)頁面訪問
  • 支持php動態(tài)頁面訪問
  • 需要一定的報錯機制茎辐,如404頁面的建立

好了宪郊,先奉上幾張最后完成的圖片來說說我們需要實現(xiàn)哪些功能

靜態(tài)頁面

Paste_Image.png

動態(tài)界面(php)

exec_php.png

404界面

Paste_Image.png

看了上面的截圖展示,是不是要下定決心自己寫出屬于自己的web服務(wù)器拖陆。所以弛槐,開始吧!

首先依啰,先看看 TCP協(xié)議通訊流程(這張圖希望多看幾遍乎串,記下每個流程,每個方法)

image

TCP通訊流程文字描述是這樣的:

Server端:

1.完成socket(),bind(),listen()這些初始化工作后孔飒,調(diào)用accept()方法阻塞等待(其實就是進入一個死循環(huán)),等待CLient的connect()方法連接

Client端:
2.先調(diào)用socket(),然后調(diào)用connect()想要與Server端進行連接灌闺,這個時候就會進行<b>傳說中的TCP三次握手</b>,也就是在Client 發(fā)起connect()坏瞄,并且Server進入accept()阻塞等待時發(fā)生三次握手

三次握手可以如下圖表示:


image

這里3次握手的詳細過程桂对,大家請自行查閱有關(guān)資料,這里不多做介紹了鸠匀。

Client端:
3.當(dāng)建立與Server端的連接后蕉斜,Client端就可以進行write()方法了,將數(shù)據(jù)傳輸給Server,于此同時宅此,Server端可以通過read()方法讀取數(shù)據(jù)机错,獲得CLient端傳遞的數(shù)據(jù),當(dāng)然Server端也可以通過write()方法將數(shù)據(jù)回寫給Client端,這樣兩端就進行相互的數(shù)據(jù)交互父腕,當(dāng)CLient端覺得交互完成了弱匪,調(diào)用close()方法通知Server端與其斷開連接時,則會進行傳說中的<b>TCP 四次揮手</b>

四次揮手可以如下圖表示:


image

這里四次握手的詳細過程璧亮,大家請自行查閱有關(guān)資料萧诫,這里不多做介紹了。

好了到這里枝嘶,簡單的TCP通訊交互介紹完了帘饶,希望大家能夠真正去了解以上內(nèi)容,然后對接下來的編碼有很大幫助!


千里之行群扶,始于足下


  • 開始第一步及刻,實現(xiàn)client以及server的交互(我不希望全是長篇的代碼,這樣看的頭疼竞阐,我會一點一點剖析代碼缴饭,一步一步介紹每個功能點)

Server端

  1. 根據(jù)socket相關(guān)編程,首先在main函數(shù)中調(diào)起socket(),bind(),listen()這幾個方法
int main(int argc, char * argv[]) {
struct sockaddr_in servaddr,cliaddr;
    socklen_t cliaddr_len;
    int listenfd,connfd;
    char buf[MAXLINE],first_line[MAXLINE],left_line[MAXLINE],method[MAXLINE], uri[MAXLINE], version[MAXLINE];
    char str[INET_ADDRSTRLEN];
    char filename[MAXLINE];
    long n;
    int i,pid;
    
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    //初始化myaddr參數(shù)
    bzero(&servaddr, sizeof(servaddr)); //結(jié)構(gòu)體清零
    //對servaddr 結(jié)構(gòu)體進行賦值
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);
    
    bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    listen(listenfd, BACKLOGSIZE);
}

當(dāng)然以上的程序需要加上頭文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//read方法需要的頭文件
#include <unistd.h>
//socket方法需要的頭文件
#include <sys/socket.h>
#include <sys/types.h>
//htonl 方法需要的頭文件
#include <netinet/in.h>
//inet_ntop方法需要的頭文件
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>

當(dāng)然中間有些結(jié)構(gòu)體不太了解骆莹,比如servaddr這個茴扁。不了解也不影響閱讀,先讓程序跑起來對吧汪疮。這些等以后深入了自然能夠清楚明白

接下來就要Server端就要進行accept()方法進行阻塞等待Client連接了
我們使用

while(1) {
    accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
    ...
}

這樣的死循環(huán)進行阻塞等待

Client端

對比Server端,Client端會顯得很簡單毁习,同樣的進行socket(),然后進行connect()智嚷,如果成功的話就可以進行write()發(fā)送消息以及read()方法接收消息了,代碼應(yīng)該像這樣:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//read方法需要的頭文件
#include <unistd.h>
//socket方法需要的頭文件
#include <sys/socket.h>
#include <sys/types.h>
//htonl 方法需要的頭文件
#include <netinet/in.h>
//inet_ntop方法需要的頭文件
#include <arpa/inet.h>

#define MAXLINE 100
#define CLI_PORT 8000
//webserver 主程序

int main(int argc, const char * argv[]) {
    struct sockaddr_in servaddr;
    char buf[MAXLINE];
    
    int clientfd;
    long n;
    //client socket連接
    clientfd = socket(AF_INET, SOCK_STREAM, 0);
    char *str = "hello world";
    
    //sockaddr_in結(jié)構(gòu)體初始化
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
    servaddr.sin_port = htons(CLI_PORT);
    
    //connect()方法
    connect(clientfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    
    //write()方法是client 向 server 寫數(shù)據(jù)
    write(clientfd, buf, strlen(buf));
    printf("write to server : %s\n",buf);
    
    //read()方法是從server接收數(shù)據(jù)
    n = read(clientfd, buf, strlen(buf));
    if(n == 0) {
        printf("the other side has been close\n");
    }else {
        printf("Response from server: %s\n",buf);
        write(STDOUT_FILENO, buf, n);
        printf("\n");
    }
    close(clientfd);
}

很簡答,Client像個線式程序一樣寫下來纺且,這時候可以去完成Server端剩下的代碼了

//死循環(huán)中進行accept()
    while (1) {
        cliaddr_len = sizeof(cliaddr);
        
        //accept()函數(shù)返回一個connfd描述符
        connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
        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);
        for (i = 0; i < n; i++)
            buf[i] = toupper(buf[i]);
        write(connfd, buf, n);
        Close(connfd);
        exit(0);

    }

這里客戶端還可以進行將CLient傳來的數(shù)據(jù)大寫轉(zhuǎn)化盏道,會傳給Client,這時候第一步代碼寫完了,趕緊運行下試試吧

Server端啟動

![Uploading client_response_509412.png . . .]

Client端響應(yīng)

client_response.png

第一階段完成载碌,撒花猜嘱,接下來將在第二篇博客中繼續(xù)完善這個web服務(wù)器

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嫁艇,隨后出現(xiàn)的幾起案子朗伶,更是在濱河造成了極大的恐慌,老刑警劉巖步咪,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件论皆,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機点晴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門感凤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人粒督,你說我怎么就攤上這事陪竿。” “怎么了屠橄?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵族跛,是天一觀的道長。 經(jīng)常有香客問我仇矾,道長庸蔼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任贮匕,我火速辦了婚禮姐仅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刻盐。我一直安慰自己掏膏,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布敦锌。 她就那樣靜靜地躺著馒疹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪乙墙。 梳的紋絲不亂的頭發(fā)上颖变,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音听想,去河邊找鬼腥刹。 笑死,一個胖子當(dāng)著我的面吹牛汉买,可吹牛的內(nèi)容都是我干的衔峰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛙粘,長吁一口氣:“原來是場噩夢啊……” “哼垫卤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起出牧,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤穴肘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后崔列,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梢褐,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡旺遮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了盈咳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耿眉。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鱼响,靈堂內(nèi)的尸體忽然破棺而出鸣剪,到底是詐尸還是另有隱情,我是刑警寧澤丈积,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布筐骇,位于F島的核電站,受9級特大地震影響江滨,放射性物質(zhì)發(fā)生泄漏铛纬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一唬滑、第九天 我趴在偏房一處隱蔽的房頂上張望告唆。 院中可真熱鬧,春花似錦晶密、人聲如沸擒悬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽懂牧。三九已至,卻和暖如春尊勿,著一層夾襖步出監(jiān)牢的瞬間僧凤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工元扔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拼弃,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓摇展,卻偏偏與公主長得像,于是被迫代替她去往敵國和親溺忧。 傳聞我的和親對象是個殘疾皇子咏连,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容