linux 高级i o函数,高级I/O函数
對(duì)于socket,最基本的輸入輸出函數(shù)就是,read和write。它們最基本,同樣功能也是最少的。Unix中有幾個(gè)函數(shù)是read/write的變種,在基本的輸入輸出功能上,還增加了一些非常使用的功能和特性,它們是:recv/send、readv/writev和recvmsg/sendmsg。
1、socket超時(shí)的實(shí)現(xiàn)
一般來(lái)說(shuō),要在對(duì)socket的I/O操作實(shí)現(xiàn)超時(shí),有3種方式:
·注冊(cè)SIGALRM信號(hào)的處理函數(shù),然后調(diào)用alrm,則若對(duì)socket的I/O操作阻塞時(shí)間大于alrm注冊(cè)的時(shí)間,則在alrm時(shí)間到時(shí)會(huì)阻塞會(huì)被SIGALRM打斷。這樣做有個(gè)缺點(diǎn),就是alrm的調(diào)用會(huì)影響到同一進(jìn)程的其他alrm調(diào)用。某些系統(tǒng)中,系統(tǒng)調(diào)用被信號(hào)打斷后,會(huì)自動(dòng)重新調(diào)用,在這種系統(tǒng)中可以使用setlongjmp/siglongjmp來(lái)跳出。
·使用SO_RCVTIMEO/SO_SNDTIMEO選項(xiàng)。注意,并不是所有的系統(tǒng)都支持這兩個(gè)選項(xiàng)。
·用select代替read/write。
前面兩種方式只對(duì)socket進(jìn)行輸入/輸出有效,而最后一種除了輸入/輸出以外,還對(duì)connect有效(socket必須處于nonblocking模式下)。
2、recv/send
函數(shù)原型:
#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個(gè)參數(shù)和read/write是一樣的,很好理解。最后一個(gè)flag參數(shù)的值可以是一下幾個(gè)常量中的一個(gè)或多個(gè)的或:
·MSG_DONTROUTE 適用于send,表示此次發(fā)送的數(shù)據(jù)包不經(jīng)過(guò)系統(tǒng)的路由過(guò)程,直接發(fā)送。
·MSG_DOWAIT 適用于recv/send,表示此次操作采用非阻塞方式(不一定所有的系統(tǒng)都支持此項(xiàng))。
·MSG_OOB 適用于recv/send,表示發(fā)送或接收“out-of-band data”(只能發(fā)送1個(gè)字節(jié)的out-of-band data)。
·MSG_PEEK 適用于recv,表示此次讀取的是緩存中數(shù)據(jù)的副本。
·MSG_WAITALL 適用于recv,表示調(diào)用將直接阻塞知道指定的指定的字節(jié)數(shù)被讀出(若中途遇到EOF或出錯(cuò),則也會(huì)少于指定字節(jié)數(shù))。
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
這兩個(gè)函數(shù)名字和read/write只多一個(gè)字母'v',功能更強(qiáng)大。readv可以將數(shù)據(jù)讀入到多個(gè)buffer中,而writev可以將多個(gè)buffer的數(shù)據(jù)輸出。iov指向一個(gè)iovec的數(shù)組,iovcnt為數(shù)組大小(linux中最大為1024)。
iovec結(jié)構(gòu)如下: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
這對(duì)函數(shù)可以說(shuō)是socket輸入/輸出的“萬(wàn)金油”,它們可以替代之前提到過(guò)的輸入/輸出函數(shù)。原型如下:#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指向相應(yīng)的socket地址,msg_namelen為地址大小,這兩個(gè)成員僅在socket未連接的情況下使用(需設(shè)置IP_RECVDSTADDR),對(duì)于已連接的socket(如TCP和已連接的UDP),msg_name應(yīng)置為NULL,msg_namelen為0。在使用recvmsg時(shí),msg_namelen是值-結(jié)果類(lèi)型。
msg_iov與msg_iovlen和readv/writev表示的意義一樣。
msg_flags僅對(duì)recvmsg有用,當(dāng)recvmsg調(diào)用時(shí),將flags賦值給msg_flags,然后內(nèi)核按照msg_flags進(jìn)行操作,返回時(shí)將更新(有可能)后的flags通過(guò)這個(gè)值返回;sendmsg僅使用flags。
附表,flag總結(jié)
msg_control成員很重要,下一節(jié)將詳細(xì)分析;msg_controllen為msg_control大小。
4、輔助數(shù)據(jù)
輔助數(shù)據(jù)是指msghdr中的msg_control和msg_controllen成員,它們又稱(chēng)謂“控制信息”。在某些情況下,這些信息是非常有用的。
附表,輔助數(shù)據(jù)總結(jié)(直接截圖,湊活著看)
輔助數(shù)據(jù)可以包含多條信息,每條信息存放在cmsghdr的結(jié)構(gòu)中:
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[] */
};
注意,數(shù)據(jù)區(qū)cmsg_data是可變的,按需求分配。
在處理cmsghdr時(shí),有5個(gè)實(shí)用的宏:#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、如何得知當(dāng)前緩沖區(qū)中有多少數(shù)據(jù)?
使用MSG_PEEK即可,這樣可以獲取緩沖區(qū)數(shù)據(jù)的副本,而不是“消耗”,副本的大小即為當(dāng)前緩沖區(qū)中的數(shù)據(jù)量。需注意的是,網(wǎng)卡每時(shí)每刻都有可能收到數(shù)據(jù),因此緩沖區(qū)的數(shù)據(jù)量是一直在變化的。
6、socket I/O與標(biāo)準(zhǔn)I/O
之前提到的函數(shù)(read/write、recv/send等)都是系統(tǒng)調(diào)用,除了系統(tǒng)調(diào)用之外,還可以使用標(biāo)準(zhǔn)I/O庫(kù)操作socket。標(biāo)準(zhǔn)I/O庫(kù)自帶緩沖機(jī)制,同時(shí)考慮了一些細(xì)節(jié),這給使用者帶來(lái)一定的方便。但是,方便的同時(shí),它帶來(lái)了新的問(wèn)題,這些問(wèn)題的根源就是緩沖機(jī)制!避免一些“奇怪”問(wèn)題的建議就是,不要使用標(biāo)準(zhǔn)I/O庫(kù)來(lái)處理socket。(見(jiàn)unpv13e 14.8)
總結(jié)
以上是生活随笔為你收集整理的linux 高级i o函数,高级I/O函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java中的输入语句判断正负_在java
- 下一篇: linux mei swap,Linux