Linux IO函數的使用和區別


    Linux系統中的IO函數主要有read、write、recv、send、recvmsg、sendmsg、readv、writev,本篇主要介紹他們的使用以及區別。

read函數:

#include <unistd.h>
ssize_t read(int fd,void *buf,size_t count);

read函數從文件描述符fd對應的文件中,讀取count字節,放在buf緩沖區。如果count為0,read返回為0,不進行其他操作;如果count的值大於SSIZE_MAX,結果不能預料。在讀取成功的時候,文件對應的讀取位置指針,,向后移動位置,大小為成功讀取的字節數。

如果read執行成功,訪回讀取的字節數;當返回為-1時候,讀取函數有錯誤發生。如果已經達到文件的末尾,返回0;其中,ssize數據類型是不同於int、long類型,它是符號數,具體實現時,可能是int,也可能是long。

write函數:

#include <unistd.h>
ssize_t write(int fd,const void *buf,size_t count);

參數含義與read類似。

 

recv函數:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int s,void *buf,size_t len,int flags);

recv函數用於接受數據,該函數從套接字s中接收數據放到緩沖區buf中,buf長度為len,操作的方式由flags指定,第一個參數s是套接字文件描述符,它是由系統調用socket函數返回。第二個參數buf是一個指針,指向接收網絡的緩沖區。第三個參數len表示緩沖區的大小,以字節為單位。第四個參數flags用於設置接收數據的方式:

recv()函數flasgs的值及含義
MSG_DONTWAIT 非阻塞,立即返回,不等待
MSG_ERRQUEUE 錯誤消息從套接字錯誤隊列中接收
MSG_OOB 接收外部數據
MSG_PEEK   查看數據,不進行數據緩沖區的清空
MSG_TRUNC 返回所有數據,即使指定的緩沖區過小
MSG_WAITALL 等待所有消息

MSG_DONTWAIT:這個標志將單個IO操作設為非阻塞,而不需要在套接字上打開非阻塞標志,執行IO,然后關閉非阻塞標志。

MSG_ERRQUEUE:該錯誤的傳輸依賴於所使用的協議。

MSG_OOB:這個標志可以接收帶外的數據,而不是接收一般數據。

MSG_PEEK:這標志查看可讀的數據,在recv函數執行后,內核不會將這些數據丟棄。

MSG_TRUNC:在接收數據后,如果用戶的緩沖區大小不足以完全復制緩沖區里的數據,則將數據截掉,僅靠復制用戶緩沖區大小的數據,其他的數據將丟棄。

MSG_WAITALL:告訴內核在沒有讀到請求的字節數之前不要讀操作返回。如果系統支持這個標志,可以去掉readn()函數而用下面的替代:

#define readn(fd,ptr,n) recv(fd,ptr,n,MSG_WAITALL).

即使設置了MSG_WAITALL,如果發生以下情況:a撲捉一個信號;b連接終止;c在套接字上發生錯誤,這個函數返回的字節數仍然比請求的少。

當指定MSG_WAITALL標志時,函數會復制與用戶指定的長度相等的數據。如果內核中的數據不能滿足要求,會一直等待到數據足夠的時候才返回。

     函數recv()的返回值是成功接收到的字節數,當返回值為-1時錯誤發生。

recv函數errno的值及含義
EAGAIN 套接字定義為非阻塞,而操作采用了阻塞方式;或者定義了超時時間到了,而沒有接收到數據
EBADF 參數s不是合法的描述符
ECONNREFUSED 遠程主機不允許此操作
EFAULT 接收緩沖區的指針,在此進程之外
EINTR 接收到中斷信號
EINVAL 傳遞了不合法的參數
ENOTCONN 套接字s表示流式套接字,此套接字沒有連接
ENOTSOCK 參數不是套接字描述符

recv()通常用於TCP,UDP使用recvfrom函數接收數據,當然在數據報套接字綁定了地址和端口后,也可以使用recv函數接收數據。

recv()從內核的接收緩沖區中復制數據到用戶指定的緩沖區,當內核中的數據比指定的緩沖區小時,一般情況下(沒有采用MSG_WAITALL標志)會復制緩沖區中的所有數據到用戶緩沖區,並返回數據的長度。當內核中的數據比指定的緩沖區多時,會將用戶指定的長度的接收緩沖區中的數據復制到用戶指定地址,其余的數據需要下次調用接收函數時再復制,內核在復制用戶指定的數據后,會銷毀已經復制完畢的數據,並進行調整。

 

send函數:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int s,const void*buf,size_t len,int flags);

send函數將緩沖區buf中的大小為len的數據,通過套接字文件描述符s按照flasg指定的方式發生出去(其中的含義與recv()中一致),它的返回值是成功發生的字節數。

由於用戶緩沖區buf中的數據通過send發送,不一定能夠全部發送出去,所以要檢查send()返回值。當send()的返回值小於len時,表面緩沖區中仍有部分數據沒有發送成功,這時需要重新發送剩余部分的數據。通常的剩余數據發送方式是對原來的buf中的數據位置進行偏移,偏移的大小為已經發送成功的字節數。

當send()返回-1時,就錯誤了。

函數send()只能用於套接字處於連接狀態的描述符,之前必須用connect()函數或者其他函數進行連接。對於send和write之間的差別是表示發送方式flag,當flag為0時,send()和write()完全一樣的,且send(s,buf,len,flags)和sendto(s,buf,len,flags,NULL,0)是等價的。

readv函數:

#include <sys/uio.h>
ssize_t readv(int s,const struct iovec*vector,int count);

read()可以接收多個緩沖區的數據。readv函數從套接字描述符s中讀取count塊的數據放在緩沖區向量vector中。返回值為成功接收到的數據的字節數,當-1時,錯誤發生。

其中的參數vector為一個向量的指針,結構struct iover在文件<sys/uio.h>定義:

1 struct iovec
2 {
3     void*iov_base;                             // 向量的緩沖區地址
4     size_t iov_len;                             // 大小,字節為單位
5 }

在調用readv的時候必須指定iovec的iov_len長度,將值放在iov_len中。參數vector指向一塊結構vector的內存,大小count定。如下圖,其中陰影部分是需要設置的。

 

writev函數:

#include <sys/uio.h>
ssize_t writev(int s,const struct iovec*vector,int count);
//// 是不是覺得和readv一樣

 

總結:

下表總結了各個函數的區別、特點:O標記的為具備此種屬性


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM