Linux系統函式read()/write()/pread()/pwrite()的區別
轉載地址:https://blog.csdn.net/u013525455/article/details/52661313
在Linux和UNIX中有很多的輸入輸出函式,有時真是讓想跟它攀點關係的菜鳥們束手無策。先來看看都有哪些函式,通過解析與總結,看看能不能讓大家能這些函式有個理性的認識,哦,原來是這麼回事,也就算我沒白花這份閒。
核心檔案I/O->標準庫I/O->高階I/O->IPC中
1. read()/write();
2. pread()/pwrite();
3. getc()/putc();
4. fgetc()/fputc();
5. getchar()/putchar();
6. ferror()/feof();
7. fgets()/fputs();
8. gets()/puts();
9. fread()/fwrite();
10. scanf()/fscanf()/sscanf()/vscanf()/vfscanf()/vsscanf()
11. printf()/fprintf()/sprintf()/snprintf()/vprintf()/vfprintf()/vsprintf()/vsanprintf()
12. readv()/writev()
13. read()/written()
14. msgrcv()/msgsnd()
15. revc()/recvfrom()/recvmsg()
16. send()/sendto()/sendmsg()
17. recv_fd()/send_fd()/send_err()
粗略總結了下,有如上邊所示的17個大類,咋一看,的確讓人頭有點小暈。但是大師們都說存在的就是合理的,下邊讓我們看看,是怎麼樣的深入淺出,讓這些函式有了存在的理由。要理解這些,先要知道系統在輸入輸出時所要經過的邏輯處理模組是怎樣。如下圖示
以上的使用者空間的應用程式利用系統呼叫完成檔案的讀寫過程,說明如下:
(1) 使用者空間與核心空間;這一組關係不用說明了。
(2) 讀與寫:都將cpu或是記憶體或是使用者程式看成主體,則讀,記憶體<-檔案;寫,記憶體->檔案;因為主體是使用者程式,所以在讀或是寫是,對讀,要確定從什麼讀,對寫,向什麼寫。
(3) 應用程式利用系統服務有三條路:通過shell命令等直接實現;利用庫函式實現;直接呼叫系統呼叫的函式,如read,write等命令。在這裡可以將庫函式與系統呼叫的關係看清楚了。系統呼叫是最基本的了,任何想要獲得系統服務的都要經過它,這是個關卡。
(4) 檔案I/O與標準I/O:前者是指在使用者空間中不需要其實程序明確提供一個緩衝(如圖中的bf2),其實就是程序在使用者空間直接呼叫read/write等函式,但是,在核心空間中都是要有緩衝的。這一般稱為檔案I/O。標準I/O:提供了一種對不用緩衝I/O的函式(這些函式即可以用於不用緩衝的I/O函式,也可以有於帶有緩衝的I/O函式)的帶緩衝的介面。這一般是庫函式在使用者空間建立的(這些緩衝由庫函式完成,不需使用者自己管理,是封裝在庫函式中的),如BUF2,可能是庫函式想將對從上層接收過來的資料做個預處理,如格式變換等。使用標準I/O函式可以無需擔心如何選取最佳的緩衝區大小(由庫函式為你完成),還有一個是簡化了對輸入行的處理。標準I/O函式庫提供了使我們能夠控制該庫使用的緩衝風格的函式。
(5) BUF1/BUF2/BUF3:BUF1,其實是使用者空間的一些字串,變數等,理解為資料即可。有時也定義為名稱BUF的形式,如char buf[MAXLINE];,但此時BUF只是名稱叫BUF而矣,區別於真正的緩衝區的概念。BUF2,這是庫函式為您老在使用者空間建立的,不用您親自管理,您只要一聲令下,如呼叫個庫函式中某個函式,自有人為你服務,這個BUF2,我們稱之為真正的緩衝區。BUF3,不論您是選擇檔案I/O的形式還是標準I/O的形式,不論是哪一種,在核心中的都要用到緩衝區BUF3(這是怎麼樣都免不了的),但是這個也不要使用者來親力親為,由核心代為管理。
(6) 流(stream):這是標準為I/O中用到的,流是檔案的邏輯代表,將檔案I/O的: 程序->fd->檔案,改變為:程序->fp(FILE物件)->流/緩衝->檔案。原來對檔案的操作,現在使用者只用處理:程序->流之間的操作,而流->檔案之間的操作將由庫函式為你完成。流的邏輯表示就是FILE物件,而流的實體就是流使用的緩衝區,這些緩衝區相對於應用程序來說就是檔案的代表。流=FILE + 緩衝。標準I/O庫提供緩衝的的是儘可能減少使用read 和write的次數。
好,暫此做以上四點說明吧,待有想法時再新增。下邊進入正題,看看上邊的這些函式,是什麼形式的,為什麼要有這些函式的存在,都為系統做些什麼,怎麼做的。
1. 檔案I/O相關(程序->fd->檔案)(檔案fd, buf):
(1) read()
形式:#include<unistd.h>
ssize_t read (int filedes, void *buf, size_t nbytes );
成功:返回讀到的位元組數;出錯:返回-1;檔案尾:返回0;
原因:基本系統呼叫功能;
實現:檔案(由filedes所指)-讀nbytes位元組->記憶體buf中。
補充:有多種情況可使實際讀到的位元組數少於要求讀的位元組數:
當從普通檔案讀時,在讀到要求位元組數之前已到達了檔案尾端。
當從終端裝置讀時,通常一次最多讀一行。
當從網路讀時,網路中緩衝機構可能造成返回值小於所要求讀的位元組數。
當從管道或FIFO讀時,如若管道包含的位元組少於所需的數量,那麼只返回實際用
的位元組數。
當從某些面向記錄的裝置讀時,一次最多返回一個記錄。
當某一訊號造成中斷,而已經讀了部分資料量時。
讀操作從檔案的當前偏移量處開始,在成功返回之前,該偏移量將增加實際讀到的位元組數。常用的unix系統shell都提供一種方法,它在標準輸入上開啟一個檔案,在標準輸出上追尋或重寫一個檔案,這使得程式不必自行開啟輸入和輸出檔案。
(2) write()
形式:#include<unistd.h>
ssize_t write (int filedes, const void *buf, size_t nbytes );
成功:返回已寫的位元組數;出錯:返回-1;
原因:基本系統呼叫功能;
實現:檔案(由filedes所指)<-寫nbytes位元組-記憶體buf中。
補充:write出錯的一個常見的原因是:磁碟已寫滿,或者超過了一個給定程序的檔案長度限制。對於普通檔案,寫操作從檔案的當前偏移量處開始。如果在開啟該檔案時,指定了O_APPEND選項,則在每次寫操作之前,將檔案偏移量設定在檔案的當前結尾處。在一次成功寫之後,該檔案偏移量增加實際寫的位元組數。
(3) pread()
形式:#include<unistd.h>
ssize_t pread (int filedes, void *buf, size_t nbytes, off_t offset );
成功:返回讀到的位元組數;出錯:返回-1;到檔案結尾:返回0
原因:由於lseek和read 呼叫之間,核心可能會臨時掛起程序,所以對同步問題造成了問題,呼叫pread相當於順序呼叫了lseek 和 read,這兩個操作相當於一個捆綁的原子操作。
實現:檔案(由filedes所指)-讀nbytes位元組->記憶體buf中。
補充:呼叫pread時,無法中斷其定位和讀操作,另外不更新檔案指標。
(4) pwrite()
形式:#include<unistd.h>
ssize_t pwrite (int filedes, const void *buf, size_t nbytes, off_t offset );
成功:返回已寫的位元組數;出錯:返回-1;
原因:由於lseek和write 呼叫之間,核心可能會臨時掛起程序,所以對同步問題造成了問題,呼叫pwrite相當於順序呼叫了lseek 和 write,這兩個操作相當於一個捆綁的原子操作。
實現:檔案(由filedes所指)<-寫nbytes位元組-記憶體buf中。
補充:呼叫pwrite時,無法中斷其定位和讀操作,另外不更新檔案指標。
2. 流(stream)或標準I/O( 程序->fp->流(FILE+緩衝)->檔案)(記憶體buf, 流fp):
每次輸入一個字元:
(1) getc();
格式:#include <stdio.h>
int getc(FILE *fp);
成功:返回下一個字元;出錯:返回EOF;檔案尾:EOF;
實現:記憶體 <-讀一個字元c- 流(由fp所指的流,是檔案的邏輯代表)
原因:在標準I/O中用,將流看成檔案的邏輯代表,將對程序->檔案的操作,現轉換為程序->流(也就是相當於檔案)的操作。
補充:函式在返回下一個字元時,會將其unsigned char型別轉換為int型別。為不帶符號的理由是,如果最高位是1也不會使返回值為負。要求整形返回值的理由是,這樣就可以返回所有可能的字元值再加上一個已出錯或已到達檔案尾端的指示值。即字元值變為正的int值,負的值就是出錯或是到達檔案尾端。(負值表特殊意義),同時不論是出錯還是到達檔案尾端,這三個函式都返回同樣的值即都是-1。由於每個流在FILE物件中維持了兩個標誌,即出錯標誌和檔案結束標誌,為了區分其不同,必須呼叫ferror或feof。
(2) fgetc();
格式:#include <stdio.h>
int fgetc(FILE *fp);
成功:返回下一個字元;出錯:返回EOF;檔案尾:EOF;
實現:同getc
原因:同getc
補充:同getc
(3) getchar();
格式:#include <stdio.h>
int getchar(void);
成功:返回下一個字元;出錯:返回EOF;檔案尾:EOF;
實現:記憶體 <-讀一個字元c- 流(由stdin所指的流,是標準輸入檔案的邏輯代表),所以getchar=getc(stdin);
原因:同getc
補充:同getc
每次輸入一行:
(4) fgets();
格式:#include <stdio.h>
char *fgets(char *restrict buf, Int n, FILE *restrict fp);
成功:返回buf;出錯:返回NULL; 檔案結尾:NULL;
實現:記憶體buf <-從fp所指的流中取一行字元- 流(由fp所指)
原因:在標準I/O中用,將流看成檔案的邏輯代表,將對程序->檔案的操作,現轉換為程序->流(也就是相當於檔案)的操作。
補充:必須指定使用者程序緩衝區的長度n,即buf的大小,此函式從流中一直讀到下一個換行符為止,但是不超過n-1個字元,讀入的字元被送入使用者緩衝區buf中。該緩衝區以null字元結尾。如若該行包括最後換行符的字數大於n-1,則其只返回一個不完整的行,但是緩衝區buf總是以null字元結尾,對此函式的呼叫會繼續讀該行。緩衝區buf中的內容為:(字元+換行符)+null。所以字元+換行符<=n-1,因為一定要留一個NULL字元來標識緩衝區的結束;
(5) gets();
格式:#include <stdio.h>
char *gets(char * buf);
成功:返回buf;出錯:返回NULL; 檔案結尾:NULL;
實現:記憶體buf <-從stdin所指的流中取1行字元-標準輸入流(由fp=stdin所指)
原因:同上;
補充:不推薦使用,問題是呼叫者在使用gets時,不能指定緩衝區buf(使用者程序)的長度,這樣可能造成緩衝區溢位。
每次輸出一個字元:
(6) putc();
格式:#include <stdio.h>
int putc(int c ,FILE *fp);
成功:返回c;出錯:返回EOF;
實現:記憶體中整形變數c-寫字元C->流(由fp所指)。至於流什麼時候將C寫入檔案中,這個由庫函式來實現,不用使用者操心;
原因:
補充:
(7) fputc();
格式:#include <stdio.h>
int fputc(int c ,FILE *fp);
成功:返回c;出錯:返回EOF;
實現:記憶體中整形變數c-寫字元C->流(由fp所指)。至於流什麼時候將C寫入檔案中,這個由庫函式來實現,不用使用者操心;
原因:
補充:
(8) putchar();
格式:#include <stdio.h>
int putchar(int c);
成功:返回c;出錯:返回EOF;
實現:記憶體中整形變數c-寫字元C->流(由fp=stdout所指)。至於流什麼時候將C寫入標準輸出檔案中,這個由庫函式來實現,不用使用者操心;
原因:
補充:putchar(c)=putc(c,stdout);
每次輸出一行:
(9) fputs();
格式:#include <stdio.h>
int fputs(const char *restrict str, FILE *restrict fp);
成功:返回非負值;出錯:返回EOF;
實現:記憶體中字元陣列str-寫字元陣列str->流(由fp所指)。
原因:
補充:將一個以null符終止的字串(相當於使用者空間buf,肯定有null,對應於fgets的buf中一定要有個null來標識緩衝區buf的結束。)寫到指定的流,尾端的終止符null不寫進流中。注意,這並不一定是每次輸出一行,因為它並不要求在null之前一定是換行符,buf中有就有,沒有就沒有,通常,在空字元之前是一個換行符,但並不要求總是如此。使用者空間buf:字元(+換行符)+null;流中的buf:字元+換行符。
(10) puts();
格式:#include <stdio.h>
int puts(const char * str);
成功:返回非負值;出錯:返回EOF;
實現:記憶體中字元陣列str-寫字元陣列str->標準輸出流(由fp=stdout所指)。
原因:
補充:將一個以null結尾的字串寫到標準輸出上,相當於程序->流->標準輸出檔案。終止符不寫出,但是puts然後又將一個換行符寫到標準輸出。應當少用,以免需要記住它在最後是否添加了一個換行符。而fgets和fputs在處理換行符,本著實事求是的態度,有就有,沒有就沒有,不會在使用者buf和流緩衝以及檔案中自己新增,只是在資料經過流緩衝時,增加或是過濾到null字元。當fgets時會在使用者buf中增加一個null以標識使用者buf的結束,而fputs時,以null為終止字元,但是尾端的null並不寫在流中。
二進位制I/O:
(11) fread()
格式:#include <stdio.h>
ssize_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
成功:讀到的物件數。
實現:記憶體始址ptr<-讀N個物件- 流(由fp所指)
原因:以上有一次一個字元或是一次一行的方式進行I/O操作,當我們讀或寫一個結構時,對於一次一個字元的方式,必須迴圈通過整個結構,每次迴圈處理一個位元組,一次讀或寫一個位元組,這會很煩。而對於一次一行的方式,當每次結構體中有null字元時,fputs就會停止,所以也不能用它實現讀結構,同時fgets中包含有null位元組或換行符,其也不能正常工作。所以要並實現結構體作為一個整體的讀或寫。
補充:使用二進位制的基本問題是:它只能用於讀在同一系統上已寫的資料。其原
因是:在結構中,同一成員偏移量可能因為編譯器和系統而異,另外,用來儲存多位元組整數和浮點值的二進位制格式在不同的機器體系結構之間也可能不同。
(12) fwrite()
格式:#include <stdio.h>
ssize_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
成功:寫的物件數。
實現:記憶體始址ptr-寫N個物件-> 流(由fp所指)
原因:
補充:
格式化輸入:檔案-流->格式轉換->記憶體變數中
(13) scanf();
格式:#include <stdio.h>
int scanf(const char *restrict format,…)
成功:指定的輸入項數;出錯:返回EOF;輸入出錯或在任意變換前已到達檔案結尾:EOF;
實現:標準輸入流->格式轉換->記憶體變數中。用於分析輸入字串,並將字元序列轉換成指定型別的變數。格式之後的各個引數包含了變數的地址,以用轉換結果初始化這些變數。
原因:要在流中做格式轉換,再將結果放到記憶體變數中
補充:
(14) fscanf();
格式:#include <stdio.h>
int fscanf(FILE *restrict fp, const char *restrict format,…)
成功:指定的輸入項數;出錯:返回EOF;輸入出錯或在任意變換前已到達檔案結尾:EOF;
實現:輸入流->格式轉換->記憶體變數中
原因:
補充:
(15) sscanf();
格式:#include <stdio.h>
int sscanf(const char *restrict buf, const char *restrict format,…)
成功:指定的輸入項數;出錯:返回EOF;輸入出錯或在任意變換前已到達檔案結尾:EOF;
實現:記憶體buf->格式轉換->記憶體變數中。
原因:
補充:對於scanf(), 從標準輸入流中輸入;fscanf,從流中輸入; sscanf,這個比較特殊,不是從流中輸入,而是記憶體的一個buf相當於string中輸入。
(16) vscanf();
格式:#include <stdio.h>
int vscanf(const char *restrict format, va_list arg);
成功:指定的輸入項數;出錯:返回EOF;輸入出錯或在任意變換前已到達檔案結尾:EOF;
實現:標準輸入流->格式轉換->記憶體變數中。用於分析輸入字串,並將字元序列轉換成指定型別的變數。格式之後的各個引數包含了變數的地址,以用轉換結果初始化這些變數。同於scanf,只是將原來的可變引數…換成了arg;
原因:要在流中做格式轉換,再將結果放到記憶體變數中
補充:
(17) vfscanf();
格式:#include <stdio.h>
int vfscanf(FILE *restrict fp, const char *restrict format, va_list arg)
成功:指定的輸入項數;出錯:返回EOF;輸入出錯或在任意變換前已到達檔案結尾:EOF;
實現:輸入流->格式轉換->記憶體變數中, 同於fscanf,只是將原來的可變引數…,換成了arg;
原因:
補充:
(18) vsscanf();
格式:#include <stdio.h>
int vsscanf(const char *restrict buf, const char *restrict format, va_list arg)
成功:指定的輸入項數;出錯:返回EOF;輸入出錯或在任意變換前已到達檔案結尾:EOF;
實現:記憶體buf->格式轉換->記憶體變數中。同於sscanf,只是將原來的可變引數…,換成了arg;
原因:
補充:對於scanf(), 從標準輸入流中輸入;fscanf,從流中輸入; sscanf,這個比較特殊,不是從流中輸入,而是記憶體的一個buf相當於string中輸入。
格式化輸出:檔案-流<-格式字串<-記憶體變數
(19) printf();
格式:#include <stdio.h>
int printf(const char *restrict format, …);
成功:返回輸出字元數;出錯:返回負值;
實現:標準輸出流<-格式字串<-記憶體變數
原因:要將記憶體變數的資料做格式變換,再將變換的結果放入流中
補充:
(20) fprintf();
格式:#include <stdio.h>
int fprintf(FILE *restrict fp,const char *restrict format, …);
成功:返回輸出字元數;出錯:返回負值;
實現:檔案-輸出流<-格式字串<-記憶體變數
原因:
補充:
(21) sprint();
格式:#include <stdio.h>
int sprintf(char *restrict buf, const char *restrict format, …);
成功:返回輸出字元數;出錯:返回負值;
實現:記憶體字串buf<-格式字串<-記憶體變數,就是將格式化的字串送入陣列buf而不是指定的流中。在陣列的尾端自動加一個null位元組,但該位元組不包括在返回值中。
原因:
補充:
(22) snprintf();
格式:#include <stdio.h>
int snprintf(char *restrict buf, size_t n , const char *restrict format, …);
成功:返回輸出字元數;出錯:返回負值;
實現:記憶體字串buf<-格式字串<-記憶體變數,就是將格式化的字串送入陣列buf而不是指定的流中。在陣列的尾端自動加一個null位元組,但該位元組不包括在返回值中。只能輸入n-1個字元,超過的任何字條都會被丟棄。
原因:
補充:
(23) vprintf();
格式:#include <stdarg.h>
#include <stdio.h>
int vprintf(const char *restrict format, va_list arg);
成功:返回輸出字元數;出錯:返回負值;
實現:標準輸出流<-格式字串<-記憶體變數,同於printf,只是將原來的可變引數…換成了arg;
原因:要將記憶體變數的資料做格式變換,再將變換的結果放入流中
補充:
(24) vfprintf();
格式:#include <stdarg.h>
#include <stdio.h>
int vfprintf(FILE *restrict fp,const char *restrict format, va_list arg);
成功:返回輸出字元數;出錯:返回負值;
實現:輸出流<-格式字串<-記憶體變數,同於fprintf,只是將原來的可變引數…換成了arg;
原因:要將記憶體變數的資料做格式變換,再將變換的結果放入流中
補充:
(25) vsprintf();
格式:#include <stdarg.h>
#include <stdio.h>
int vsprintf(char *restrict buf, const char *restrict format, va_list arg);
成功:返回輸出字元數;出錯:返回負值;
實現:記憶體陣列buf<-格式字串<-記憶體變數,同於sprintf,只是將原來的可變引數…換成了arg; 就是將格式化的字串送入陣列buf而不是指定的流中。在陣列的尾端自動加一個null位元組,但該位元組不包括在返回值中。
原因:要將記憶體變數的資料做格式變換,再將變換的結果放入流中
補充:
(26) vsnprintf();
格式:#include <stdio.h>
int vsnprintf(char *restrict buf, size_t n , const char *restrict format, va_list arg);
成功:返回輸出字元數;出錯:返回負值;
實現:記憶體字串buf<-格式字串<-記憶體變數, 同於snprintf,只是將原來的可變引數…換成了arg; 就是將格式化的字串送入陣列buf而不是指定的流中。在陣列的尾端自動加一個null位元組,但該位元組不包括在返回值中。只能輸入n-1個字元,超過的任何字條都會被丟棄。
原因:
補充:
3. 高階I/O:(檔案(fd), 記憶體buf )
(1) readv()
格式:#include <sys/uio.h>
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
成功:返回已讀的位元組數;出錯:返回-1;
實現:檔案(fd)->記憶體向量中
原因:在一次函式呼叫中讀、寫多個非連續緩衝區,但是這些緩衝區已經用iovec表示好了。減少了系統呼叫的次數。
補充:
(2) writev()
格式:#include <sys/uio.h>
ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
成功:返回已讀的位元組數;出錯:返回-1;
實現:檔案(fd)<-記憶體向量
原因:在一次函式呼叫中讀、寫多個非連續緩衝區,但是這些緩衝區已經用iovec表示好了。減少了系統呼叫的次數。
補充:
(3) readn()
格式:#include <sys/uio.h>
ssize_t readn(int filedes, void *bug, size_t nbytes);
成功:返回已讀的位元組數;出錯:返回-1;
實現:檔案(fd)->記憶體buf中
原因:管道、FIFO以及某些裝置,特別是終端、網路和STREAMS裝置有下列兩種性質:一是,一次read操作所返回的資料可能少於所要求的資料,即使還沒達到檔案尾端也可能是這樣的。這不是一個錯誤,應當繼續讀該裝置。二是,一次write操作所返回的值也可能少於所指定輸出的位元組數,這可能是由若干因素造成的。這些也不是錯誤,也應當繼續寫餘下的資料至該裝置。通常只對非阻塞描述符,或捕捉到一個訊號時,才發生這種write的中途返回。但是在讀寫磁碟時,很少遇到這樣的情況。所以這個函式其實是按需要多次呼叫read 和write直至讀、寫了N個位元組資料,即我們稱之為:直到集齊了再返回。
補充:
(4) written()
格式:#include <sys/uio.h>
ssize_t writen(int filedes, void *bug, size_t nbytes);
成功:返回已讀的位元組數;出錯:返回-1;
實現:檔案(fd)<-記憶體buf中
原因:管道、FIFO以及某些裝置,特別是終端、網路和STREAMS裝置有下列兩種性質:一是,一次read操作所返回的資料可能少於所要求的資料,即使還沒達到檔案尾端也可能是這樣的。這不是一個錯誤,應當繼續讀該裝置。二是,一次write操作所返回的值也可能少於所指定輸出的位元組數,這可能是由若干因素造成的。這些也不是錯誤,也應當繼續寫餘下的資料至該裝置。通常只對非阻塞描述符,或捕捉到一個訊號時,才發生這種write的中途返回。但是在讀寫磁碟時,很少遇到這樣的情況。所以這個函式其實是按需要多次呼叫read 和write直至讀、寫了N個位元組資料,即我們稱之為:直到集齊了再返回。
補充:
4. IPC中:
訊息佇列中:
(1) msgrcv()
格式:#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag);
成功:返回訊息的資料部分長度;出錯:-1;
實現:訊息佇列->記憶體訊息結構體(由ptr指向)
原因:
補充:nbytes說明資料緩衝區的長度。用來構造mymesg。若返回的訊息大於nbytes,而且在flag中設定了MSG_NOERROR,則該訊息被截短。如果沒有設定這一標誌,而訊息又太長,則出錯返回E2BIG(訊息仍留在佇列中。引數type我們可以指定想要哪一種訊息。可以指定flag值為IPC_NOWAIT,使操作不阻塞。這使得如果沒有所指定型別的訊息,則msgrcv返回-1,errno設定為ENOMSG。
(2) msgsnd()
格式:#include <sys/msg.h>
int msgsnd(int msqid, const void *ptr, size_t nbytes, long type, int flag);
成功:返回0;出錯:-1;
實現:訊息佇列<-記憶體訊息結構體(由ptr指向)
原因:
補充:每個訊息都由三部分組成,它們是:正長整型型別欄位、實際資料位元組(這兩個對就myseq結構體)、非負長度(nbytes)。訊息總是放在佇列尾端。ptr引數指向一個長整型數,它包含了正的整型訊息型別,在其後緊跟著訊息資料。可以定義如下結構:struct myseq{ long mtype; char mtex[512];} 於是ptr就是一個指向mymesg結構的指標。接收者可以用訊息型別以非先進先出的次序取訊息。
SOCKET中:
(1) revc()
格式:#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
成功:以位元組計數的訊息長度;出錯:-1;無可用訊息或對方已經按序結束:0;
實現:網路sockfd-取訊息msg->記憶體buf中。
原因:
補充:
(2) recvfrom()
格式:#include <sys/socket.h>
ssize_t recvfrom( int sockfd, void *restrict buf, size_t len, int flags, struct sockaddr *restrict addr, socklen_t *restrict addrlen);
成功:以位元組計數的訊息長度;出錯:-1;無可用訊息或對方已經按序結束:0;
實現:網路sockfd-取訊息msg->記憶體buf中。
原因:
補充:如果addr非空,它將包含資料傳送者的套接字端點地址,當呼叫recvfrom時,需要設定addrlen引數指向一個包含addr所指的套接字緩衝區位元組大小的整數。返回時,該整數設為該地址的實際位元組大小。因為可以獲得傳送者的地址,recvfrom通常用於無連線套接字。
(3) recvmsg()
格式:#include <sys/socket.h>
ssize_t recvmsg( int sockfd, struct msghdr *msg, int flags);
成功:以位元組計數的訊息長度;出錯:-1;無可用訊息或對方已經按序結束:0;
實現:網路sockfd-取訊息msg->記憶體buf中。
原因:
補充:結構msghdr被recvmsg用於指定接收資料的輸入緩衝區。
(4) send()
格式:#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
成功:返回傳送的位元組數;出錯:-1;
實現:網路sockfd<-取訊息msg-記憶體buf中。
原因:
補充:如果send成功,並不必然表示連線另一端的程序接收資料。所保證的僅是當send成功返回時,資料已經無錯誤的傳送到網路上。
(5) sendto()
格式:#include <sys/socket.h>
ssize_t sendto( int sockfd, const void *restrict buf, size_t nbytes, int flags, const struct sockaddr *dest addr, socklen_t * addrlen);
成功:返回傳送的位元組數;出錯:-1;
實現:網路sockfd<-取訊息msg-記憶體buf中。
原因:
補充:適用於無連線的套接字,不能使用send,除非呼叫connect時預先設定了目標地址,或者採用了sendto來提供另外一種報文傳送方式。
(6) sendmsg()
格式:#include <sys/socket.h>
ssize_t sendmsg( int sockfd, const struct msghdr *msg, int flags);
成功:返回傳送的位元組數;出錯:-1;
實現:網路sockfd<-取訊息msg-記憶體buf中。
原因:
補充:可以呼叫帶有msghdr結構的sendmsg來指定多重緩衝區傳輸資料,和writev很像。
傳送檔案描述符(略,自行實現)
(1) recv_fd()
(2) send_fd()
(3) send_err()
總算整差不多了,雖然花了點時間,但是希望能對大家有幫助,最後謝謝閱讀!