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標記的為具備此種屬性