第六章|高級I/O函數(shù)

pipe

創(chuàng)建一個管道祝峻,實現(xiàn)進程間通信魔吐。

dup、dup2

復制文件描述符
實例:cgi服務器原理

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>


int main(int argc, char *argv[]) {
    if (argc <= 2) {
        printf("usage: %s id_address prot_number \n", basename(argv[0]));
        return 1;
    }


    const char *ip = argv[1];
    //字符串轉(zhuǎn)換成整型數(shù)的一個函數(shù)
    int port = atoi(argv[2]);

    //創(chuàng)建一個專用 IPV4 socket 地址
    struct sockaddr_in address;
    //bzero功能:置字節(jié)字符串a(chǎn)ddress的前sizeof( address )個字節(jié)為零且包括‘\0’
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;//地址組
    //inet_pton:將用字符串表示ip地址轉(zhuǎn)換成網(wǎng)絡字節(jié)序整數(shù)表示的ip地址莱找。 成功返回1 失敗返回0
    inet_pton(AF_INET, ip, &address.sin_addr);
    //小端轉(zhuǎn)大端 酬姆、主機字節(jié)序轉(zhuǎn)換網(wǎng)絡字節(jié)序
    address.sin_port = htons(port);


    //創(chuàng)建一個socket PF_INET:ipv4   SOCK_STREAM:流服務
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    //判斷是否失敗  失敗返回-1并設置errno  成功返回socket文件描述符
    assert(sock >= 0);

    //命名socket   將address所指的地址分配給未命名的 sock 變量(文件描述符)
    int ret = bind(sock, (struct sockaddr *) &address, sizeof(address));
    // bind 成功返回0 失敗返回-1 并設置errno
    assert(ret !=-1);

    //監(jiān)聽socket sock參數(shù):指定被監(jiān)聽的socket   backlog參數(shù):提示內(nèi)核監(jiān)聽隊列的最大長度
    ret = listen(sock,5);
    //listen 成功返回0  失敗返回-1 并設置errno
    assert(ret !=-1);


    //創(chuàng)建一個專用 IPV4 socket 地址
    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof(client);
    int connfd = accept(sock,(struct sockaddr*)&client ,&client_addrlength);
    if (connfd < 0)
    {
        printf("error is :%d\n",errno);
    }
    else
    {
        //關閉標準輸出文件描述符
        close(STDOUT_FILENO);
        //復制socket文件描述符connfd
        dup(connfd);
        printf("adcd\n");
        close(connfd);
    }

    //關閉socket
    close(sock);
    return 0;
}

B主機執(zhí)行該程序


image.png

A主機執(zhí)行該程序


image.png
readv() 、wirtev()函數(shù)用法

readv和writev函數(shù)和前面提過的readmsg和writemsg函數(shù)類似奥溺,也是用來對數(shù)據(jù)的集中寫和分散讀辞色,相當于前面兩個函數(shù)的簡化版。舉一個例子來說明浮定,在Web服務器解析完HTTP請求后如果客戶端請求的文件存在并且有權(quán)限時相满,就需要返回一個HTTP首部狀態(tài)碼和狀態(tài)信息,然后再返回該文件桦卒,但是我們考慮效率問題立美,如果每次我們都需要將兩個不相關的存儲空間合并到一起再發(fā)送勢必會很影響效率,所以我們可以事先將HTTP不同的頭部存儲好方灾,找到文件后使用sendv函數(shù)直接發(fā)送即可建蹄。我們建立一個index.html文件模擬一下,服務器代碼如下:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#define BUFFER_SIZE 1024
static const char *status_line[2] = {"200 OK", "500 Internal server error"};

int main(int argc, char *argv[]) {
    if (argc <= 3) {
        printf("usage: %s ip_address port_number filename\n", basename(argv[0]));
        return 1;
    }
    const char *ip = argv[1];
    int port = atoi(argv[2]);
    const char *file_name = argv[3];

    //創(chuàng)建一個專用 IPV4 socket 地址
    struct sockaddr_in address;
    //bzero功能:設置字符串a(chǎn)ddress的前sizeof(address)個字節(jié)為0且包括'\0'
    bzero(&address, sizeof(address));
    //地址族
    address.sin_family = AF_INET;
    //將用字符串表示ip地址轉(zhuǎn)換成網(wǎng)絡字節(jié)序整數(shù)表示的ip地址裕偿,成功返回1 失敗返回0
    inet_pton(AF_INET, ip, &address.sin_addr);
    //小端轉(zhuǎn)大端洞慎、主機字節(jié)序轉(zhuǎn)換網(wǎng)絡字節(jié)序
    address.sin_port = htons(port);

    //創(chuàng)建一個socket   PF_INET:ipv4  SOCK_STREAM:流服務
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    //判斷是否失敗  失敗返回-1 并設置error  成功返回socket文件描述符
    assert(sock >= 0);

    //命名socket  并將address所指向的地址分配給未命名的sock變量(文件描述符)
    int ret = bind(sock, (struct sockaddr *) &address, sizeof(address));
    //判斷是否失敗  失敗返回-1 并設置error
    assert(ret != -1);

    //監(jiān)聽socket   sock: 指定被監(jiān)聽的socket    backlog:提示內(nèi)核監(jiān)聽隊列的最大長度
    ret = listen(sock, 5);
    //成功返回0 失敗返回-1 并設置error
    assert(ret != -1);

    //創(chuàng)建一個專用ipv4 socket地址
    struct sockaddr_in client;
    //獲取client類型長度
    socklen_t client_addrlength = sizeof(client);
    //接收連接  成功返回一個新的 socket 失敗返回-1 并設置error
    //sock : 監(jiān)聽套接字,即服務器端創(chuàng)建的用于listen的socket描述符嘿棘。
    //client :  struct sockaddr*這是一個結(jié)果參數(shù)劲腿,它用來接受一個返回值,這返回值指定客戶端的地址
    //client_addrlength : 描述 client 的長度  socklen_t*
    int connfd = accept(sock, (struct sockaddr *) &client, &client_addrlength);
    if (connfd < 0) {
        printf("errno is: %d\n", errno);
    } else {
        //用于保存http應答的狀態(tài)行鸟妙、頭部字段和一個空行的緩存區(qū)
        char header_buf[BUFFER_SIZE];
        //將已開辟內(nèi)存空間 header_buf 的首 BUFFER_SIZE 個字節(jié)的值設為值 '\0'焦人。
        memset(header_buf, '\0', BUFFER_SIZE);
        //用于存放目標文件內(nèi)容的應用程序緩存
        char *file_buf;
        //用于獲許目標文件的屬性挥吵,比如是否為目錄,文件大小等
        struct stat file_stat;

        //記錄目標文件是否是有效文件
        bool valid = true;
        //緩存區(qū)header_buf 目前已經(jīng)使用了多少個字節(jié)的空間
        int len = 0;
        //stat(): 獲取一些文件相關的信息  filename : 文件路徑名  file_stat 垃瞧;保存文件信息的結(jié)構(gòu)體  成功返回0蔫劣。失敗返回-1 并設置error
        if (stat(file_name, &file_stat) < 0)//目標文件不存在
        {
            valid = false;
        } else {
            if (S_ISDIR(file_stat.st_mode))//目標文件是一個目錄
            {
                valid = false;
            } else if (file_stat.st_mode & S_IROTH)//當前用戶有讀取目標文件的權(quán)限
            {
                //動態(tài)分配緩存區(qū) file_buf,并指定其大小為目標文件的大小file_stat.st_size + 1 ,然后講目標文件讀入緩存區(qū) file_buf
                int fd = open(file_name, O_RDONLY);
                file_buf = new char [ file_stat.st_size + 1 ];
//                char file_buf[file_stat.st_size + 1];
                memset(file_buf, '\0', file_stat.st_size + 1);
                if (read(fd, file_buf, file_stat.st_size) < 0) {
                    valid = false;
                }
            } else {
                valid = false;
            }
        }

        if (valid) {
            //下面這部分內(nèi)容講http應答的狀態(tài)行 坪郭、"Content-Length" 頭部字段和一個空行依次加入header_buf種

            //snprintf() :最多從源串中拷貝size-1個字符到目標串中个从,然后再在后面加一個0。所以如果目標串的大小為size的話歪沃,將不會溢出嗦锐。
            ret = snprintf(header_buf, BUFFER_SIZE - 1, "%s %s\r\n", "HTTP/1.1", status_line[0]);
            len += ret;
            ret = snprintf(header_buf + len, BUFFER_SIZE - 1 - len,
                           "Content-Length: %d \r\n", (int)file_stat.st_size);
            len += ret;
            ret = snprintf(header_buf + len, BUFFER_SIZE - 1 - len, "%s", "\r\n");

            //利用writev將header_buff 和 file_buf 的內(nèi)容一并寫出
            struct iovec iv[2];
            iv[0].iov_base = header_buf;
            iv[0].iov_len = strlen(header_buf);
            iv[1].iov_base = file_buf;
            iv[1].iov_len = file_stat.st_size;
            ret = writev(connfd, iv, 2);
        }
            //如果目標文件無效 則通知客戶端 服務器發(fā)生了 內(nèi)部錯誤
        else {
            ret = snprintf(header_buf, BUFFER_SIZE - 1, "%s %s\r\n", "HTTP/1.1", status_line[1]);
            len += ret;
            ret = snprintf(header_buf + len, BUFFER_SIZE - 1 - len, "%s", "\r\n");
            send(connfd, header_buf, strlen(header_buf), 0);
        }
        close(connfd);
        delete [] file_buf;
    }

    close(sock);
    return 0;
}

readv、wirtev函數(shù)調(diào)用實例
sendfile

函數(shù)可以在兩個文件描述符之間直接傳遞數(shù)據(jù)(完全在內(nèi)核中操作)沪曙,從而避免了內(nèi)核緩沖區(qū)和用戶緩沖區(qū)之間的數(shù)據(jù)拷貝奕污,效率高,被稱為零拷貝液走。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/sendfile.h>

#define BUFFER_SIZE 1024
static const char *status_line[2] = {"200 OK", "500 Internal server error"};

int main(int argc, char *argv[]) {
    if (argc <= 3) {
        printf("usage: %s ip_address port_number filename\n", basename(argv[0]));
        return 1;
    }
    const char *ip = argv[1];
    int port = atoi(argv[2]);
    const char *file_name = argv[3];

    int filefd = open(file_name, O_RDONLY);
    assert(filefd > 0);

    struct stat stat_buf;
    fstat(filefd,&stat_buf);

    //創(chuàng)建一個專用 IPV4 socket 地址
    struct sockaddr_in address;
    //bzero功能:設置字符串a(chǎn)ddress的前sizeof(address)個字節(jié)為0且包括'\0'
    bzero(&address, sizeof(address));
    //地址族
    address.sin_family = AF_INET;
    //將用字符串表示ip地址轉(zhuǎn)換成網(wǎng)絡字節(jié)序整數(shù)表示的ip地址碳默,成功返回1 失敗返回0
    inet_pton(AF_INET, ip, &address.sin_addr);
    //小端轉(zhuǎn)大端、主機字節(jié)序轉(zhuǎn)換網(wǎng)絡字節(jié)序
    address.sin_port = htons(port);

    //創(chuàng)建一個socket   PF_INET:ipv4  SOCK_STREAM:流服務
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    //判斷是否失敗  失敗返回-1 并設置error  成功返回socket文件描述符
    assert(sock >= 0);

    //命名socket  并將address所指向的地址分配給未命名的sock變量(文件描述符)
    int ret = bind(sock, (struct sockaddr *) &address, sizeof(address));
    //判斷是否失敗  失敗返回-1 并設置error
    assert(ret != -1);

    //監(jiān)聽socket   sock: 指定被監(jiān)聽的socket    backlog:提示內(nèi)核監(jiān)聽隊列的最大長度
    ret = listen(sock, 5);
    //成功返回0 失敗返回-1 并設置error
    assert(ret != -1);

    //創(chuàng)建一個專用ipv4 socket地址
    struct sockaddr_in client;
    //獲取client類型長度
    socklen_t client_addrlength = sizeof(client);
    //接收連接  成功返回一個新的 socket 失敗返回-1 并設置error
    //sock : 監(jiān)聽套接字缘眶,即服務器端創(chuàng)建的用于listen的socket描述符嘱根。
    //client :  struct sockaddr*這是一個結(jié)果參數(shù),它用來接受一個返回值巷懈,這返回值指定客戶端的地址
    //client_addrlength : 描述 client 的長度  socklen_t*
    int connfd = accept(sock, (struct sockaddr *) &client, &client_addrlength);
    if (connfd < 0) {
        printf("errno is: %d\n", errno);
    } else {
        sendfile(connfd,filefd,NULL,stat_buf.st_size);
        close(connfd);
    }

    close(sock);
    return 0;
}
sendfile函數(shù)調(diào)用實例
splice

函數(shù)用于在兩個文件描述符之間移動數(shù)據(jù)该抒,也是零拷貝。
在使用該函數(shù)時顶燕、fd_in 和 fd_out 必須至少有一個是管道文件描述符凑保。splice函數(shù)調(diào)用成功返回移動字節(jié)的數(shù)量,可能返回0涌攻,表示沒有數(shù)據(jù)需要移動欧引,比如從管道中讀取數(shù)據(jù)(fd_in是管道文件描述符)而該管道沒有被寫入任何數(shù)據(jù)時。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

int main(int argc, char *argv[]) {
    if (argc <= 2) {
        printf("usage: %s ip_address port_number \n", basename(argv[0]));
        return 1;
    }
    const char *ip = argv[1];
    int port = atoi(argv[2]);


    //創(chuàng)建一個專用 IPV4 socket 地址
    struct sockaddr_in address;
    //bzero功能:設置字符串a(chǎn)ddress的前sizeof(address)個字節(jié)為0且包括'\0'
    bzero(&address, sizeof(address));
    //地址族
    address.sin_family = AF_INET;
    //將用字符串表示ip地址轉(zhuǎn)換成網(wǎng)絡字節(jié)序整數(shù)表示的ip地址恳谎,成功返回1 失敗返回0
    inet_pton(AF_INET, ip, &address.sin_addr);
    //小端轉(zhuǎn)大端芝此、主機字節(jié)序轉(zhuǎn)換網(wǎng)絡字節(jié)序
    address.sin_port = htons(port);

    //創(chuàng)建一個socket   PF_INET:ipv4  SOCK_STREAM:流服務
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    //判斷是否失敗  失敗返回-1 并設置error  成功返回socket文件描述符
    assert(sock >= 0);

    //命名socket  并將address所指向的地址分配給未命名的sock變量(文件描述符)
    int ret = bind(sock, (struct sockaddr *) &address, sizeof(address));
    //判斷是否失敗  失敗返回-1 并設置error
    assert(ret != -1);

    //監(jiān)聽socket   sock: 指定被監(jiān)聽的socket    backlog:提示內(nèi)核監(jiān)聽隊列的最大長度
    ret = listen(sock, 5);
    //成功返回0 失敗返回-1 并設置error
    assert(ret != -1);

    //創(chuàng)建一個專用ipv4 socket地址
    struct sockaddr_in client;
    //獲取client類型長度
    socklen_t client_addrlength = sizeof(client);
    //接收連接  成功返回一個新的 socket 失敗返回-1 并設置error
    //sock : 監(jiān)聽套接字,即服務器端創(chuàng)建的用于listen的socket描述符惠爽。
    //client :  struct sockaddr*這是一個結(jié)果參數(shù)癌蓖,它用來接受一個返回值,這返回值指定客戶端的地址
    //client_addrlength : 描述 client 的長度  socklen_t*
    int connfd = accept(sock, (struct sockaddr *) &client, &client_addrlength);
    if (connfd < 0) {
        printf("errno is: %d\n", errno);
    } else {
        int pipefd[2];
        assert(ret != -1);
        ret = pipe(pipefd);
        ret = splice(connfd, NULL, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
        assert(ret != -1);
        ret = splice(pipefd[0], NULL, connfd, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
        assert(ret != -1);
        close(connfd);
    }

    close(sock);
    close(ret);
    return 0;
}
splice函數(shù)調(diào)用實例
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末婚肆,一起剝皮案震驚了整個濱河市租副,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌较性,老刑警劉巖用僧,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件结胀,死亡現(xiàn)場離奇詭異,居然都是意外死亡责循,警方通過查閱死者的電腦和手機糟港,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來院仿,“玉大人秸抚,你說我怎么就攤上這事〈醯妫” “怎么了剥汤?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長排惨。 經(jīng)常有香客問我吭敢,道長,這世上最難降的妖魔是什么暮芭? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任鹿驼,我火速辦了婚禮,結(jié)果婚禮上辕宏,老公的妹妹穿的比我還像新娘畜晰。我一直安慰自己,他們只是感情好匾效,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布舷蟀。 她就那樣靜靜地躺著,像睡著了一般面哼。 火紅的嫁衣襯著肌膚如雪野宜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天魔策,我揣著相機與錄音匈子,去河邊找鬼。 笑死闯袒,一個胖子當著我的面吹牛虎敦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播政敢,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼其徙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了喷户?” 一聲冷哼從身側(cè)響起唾那,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎褪尝,沒想到半個月后闹获,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體期犬,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年避诽,在試婚紗的時候發(fā)現(xiàn)自己被綠了龟虎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡沙庐,死狀恐怖鲤妥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情轨功,我是刑警寧澤旭斥,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布容达,位于F島的核電站古涧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏花盐。R本人自食惡果不足惜羡滑,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望算芯。 院中可真熱鬧柒昏,春花似錦、人聲如沸熙揍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽届囚。三九已至有梆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間意系,已是汗流浹背泥耀。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蛔添,地道東北人痰催。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像迎瞧,于是被迫代替她去往敵國和親夸溶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

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

  • 操作系統(tǒng)概論 操作系統(tǒng)的概念 操作系統(tǒng)是指控制和管理計算機的軟硬件資源凶硅,并合理的組織調(diào)度計算機的工作和資源的分配缝裁,...
    野狗子嗷嗷嗷閱讀 11,931評論 3 34
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)咏尝,斷路器压语,智...
    卡卡羅2017閱讀 134,657評論 18 139
  • 談論WEB編程的時候常說天天在寫CGI啸罢,那么CGI是什么呢?可能很多時候并不會去深究這些基礎概念胎食,再比如除了CGI...
    __七把刀__閱讀 2,197評論 2 11
  • 美美的
    放松身心閱讀 235評論 0 0
  • 書中韻味悠然享厕怜,劍底風情自在歌衩匣。 攻書不忘安邦志,撫劍常懷報國心粥航。 性與書融情亦雅琅捏,心同劍合意猶真。 一榻琴書隨我...
    于光里閱讀 285評論 0 2