linux 高级i o函数,高级I/O函数
對于socket,最基本的輸入輸出函數就是,read和write。它們最基本,同樣功能也是最少的。Unix中有幾個函數是read/write的變種,在基本的輸入輸出功能上,還增加了一些非常使用的功能和特性,它們是:recv/send、readv/writev和recvmsg/sendmsg。
1、socket超時的實現
一般來說,要在對socket的I/O操作實現超時,有3種方式:
·注冊SIGALRM信號的處理函數,然后調用alrm,則若對socket的I/O操作阻塞時間大于alrm注冊的時間,則在alrm時間到時會阻塞會被SIGALRM打斷。這樣做有個缺點,就是alrm的調用會影響到同一進程的其他alrm調用。某些系統中,系統調用被信號打斷后,會自動重新調用,在這種系統中可以使用setlongjmp/siglongjmp來跳出。
·使用SO_RCVTIMEO/SO_SNDTIMEO選項。注意,并不是所有的系統都支持這兩個選項。
·用select代替read/write。
前面兩種方式只對socket進行輸入/輸出有效,而最后一種除了輸入/輸出以外,還對connect有效(socket必須處于nonblocking模式下)。
2、recv/send
函數原型:
#include
ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);
ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags);
Both return: number of bytes read or written if OK, –1 on error
可以看到,前3個參數和read/write是一樣的,很好理解。最后一個flag參數的值可以是一下幾個常量中的一個或多個的或:
·MSG_DONTROUTE 適用于send,表示此次發送的數據包不經過系統的路由過程,直接發送。
·MSG_DOWAIT 適用于recv/send,表示此次操作采用非阻塞方式(不一定所有的系統都支持此項)。
·MSG_OOB 適用于recv/send,表示發送或接收“out-of-band data”(只能發送1個字節的out-of-band data)。
·MSG_PEEK 適用于recv,表示此次讀取的是緩存中數據的副本。
·MSG_WAITALL 適用于recv,表示調用將直接阻塞知道指定的指定的字節數被讀出(若中途遇到EOF或出錯,則也會少于指定字節數)。
2、readv/writev
原型:#include
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
Both return: number of bytes read or written, –1 on error
這兩個函數名字和read/write只多一個字母'v',功能更強大。readv可以將數據讀入到多個buffer中,而writev可以將多個buffer的數據輸出。iov指向一個iovec的數組,iovcnt為數組大小(linux中最大為1024)。
iovec結構如下:struct iovec {
void *iov_base; /* starting address of buffer */
size_t iov_len; /* size of buffer */
};
iov_base指向buffer的起始地址,iov_len為buffer的大小。
3、recvmsg/sendmsg
這對函數可以說是socket輸入/輸出的“萬金油”,它們可以替代之前提到過的輸入/輸出函數。原型如下:#include
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
Both return: number of bytes read or written if OK, –1 on error
大部分重要的都放在msg指向的msghdr中:struct msghdr {
void *msg_name; /* protocol address */
socklen_t msg_namelen; /* size of protocol address */
struct iovec *msg_iov; /* scatter/gather array */
int msg_iovlen; /* # elements in msg_iov */
void *msg_control; /* ancillary data (cmsghdr struct) */
socklen_t msg_controllen; /* length of ancillary data */
int msg_flags; /* flags returned by recvmsg() */
};
msg_name指向相應的socket地址,msg_namelen為地址大小,這兩個成員僅在socket未連接的情況下使用(需設置IP_RECVDSTADDR),對于已連接的socket(如TCP和已連接的UDP),msg_name應置為NULL,msg_namelen為0。在使用recvmsg時,msg_namelen是值-結果類型。
msg_iov與msg_iovlen和readv/writev表示的意義一樣。
msg_flags僅對recvmsg有用,當recvmsg調用時,將flags賦值給msg_flags,然后內核按照msg_flags進行操作,返回時將更新(有可能)后的flags通過這個值返回;sendmsg僅使用flags。
附表,flag總結
msg_control成員很重要,下一節將詳細分析;msg_controllen為msg_control大小。
4、輔助數據
輔助數據是指msghdr中的msg_control和msg_controllen成員,它們又稱謂“控制信息”。在某些情況下,這些信息是非常有用的。
附表,輔助數據總結(直接截圖,湊活著看)
輔助數據可以包含多條信息,每條信息存放在cmsghdr的結構中:
struct cmsghdr {
socklen_t cmsg_len; /* length in bytes, including this structure */
int cmsg_level; /* originating protocol */
int cmsg_type; /* protocol-specific type */
/* followed by unsigned char cmsg_data[] */
};
注意,數據區cmsg_data是可變的,按需求分配。
在處理cmsghdr時,有5個實用的宏:#include
#include /* for ALIGN macro on many implementations */
struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *mhdrptr) ;
#Returns: pointer to first cmsghdr structure or NULL if no ancillary data
struct cmsghdr *CMSG_NXTHDR(struct msghdr *mhdrptr, struct cmsghdr *cmsgptr) ;
#Returns: pointer to next cmsghdr structure or NULL if no more ancillary data objects
unsigned char *CMSG_DATA(struct cmsghdr *cmsgptr) ;
#Returns: pointer to first byte of data associated with cmsghdr structure
unsigned int CMSG_LEN(unsigned int length) ;
#Returns: value to store in cmsg_len given the amount of data
unsigned int CMSG_SPACE(unsigned int length) ;
#Returns: total size of an ancillary data object given the amount of data
圖例:
代碼片段:
struct msghdr msg;
struct cmsghdr *cmsgptr;
/* fill in msg structure */
/* call recvmsg() */
for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
if (cmsgptr->cmsg_level == ... &&
cmsgptr->cmsg_type == ... ) {
u_char *ptr;
ptr = CMSG_DATA(cmsgptr);
/* process data pointed to by ptr */
}
}
5、如何得知當前緩沖區中有多少數據?
使用MSG_PEEK即可,這樣可以獲取緩沖區數據的副本,而不是“消耗”,副本的大小即為當前緩沖區中的數據量。需注意的是,網卡每時每刻都有可能收到數據,因此緩沖區的數據量是一直在變化的。
6、socket I/O與標準I/O
之前提到的函數(read/write、recv/send等)都是系統調用,除了系統調用之外,還可以使用標準I/O庫操作socket。標準I/O庫自帶緩沖機制,同時考慮了一些細節,這給使用者帶來一定的方便。但是,方便的同時,它帶來了新的問題,這些問題的根源就是緩沖機制!避免一些“奇怪”問題的建議就是,不要使用標準I/O庫來處理socket。(見unpv13e 14.8)
總結
以上是生活随笔為你收集整理的linux 高级i o函数,高级I/O函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java中的输入语句判断正负_在java
- 下一篇: html如何让图片跟字体重叠,CSS设置