標籤:
這部分是進階插座 I/O 。
設定通訊端逾時警示,使用更方便的資料轉送功能。
通訊端 I/O 設定作業逾時有三種方法:
- 轉讓 alarm 效能,製作時,它指定逾時 SIGALRM 訊號;
- 在 select 函數中設定逾時堵塞等待 I/O,以替代直接堵塞在 read 或write 調用上;
- 使用 SO_RCVTIMEO 和 SO_SNDTIMEO 通訊端選項(這兩個選項僅僅是一部分實現支援)。
以下使用 alarm 產生的 SIGALRM 訊號為 connect 函數設定逾時,當然系統會為 connect 函數設定逾時限制,這裡我們僅僅是表示 SIGALRM 訊號在設定逾時的作用。
#include <signal.h>#include <errno.h>#include <stdio.h>#include <unistd.h>#include <sys/socket.h>typedef void Sigfunc(int);extern void err_sys(const char *,...);static Sigfunc *M_signal(int signo, Sigfunc *func);static Sigfunc *MySignal(int signo, Sigfunc *func);static void connect_alarm(int);int Myconnect_timo(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec){ Sigfunc *sigfunc; int n; /* SIGALRM 訊號處理函數。並儲存現有訊號處理函數 */ sigfunc = MySignal(SIGALRM, connect_alarm); /* 設定alarm逾時 */ if(alarm(nsec) != 0)/* 若已經設定了逾時,則alarm返回的是當前剩餘秒數。否則返回0 */ printf("alarm was already set\n");/* 提示:已經設定過alarm逾時 */ if( (n = connect(sockfd, saptr, salen)) < 0) {/* 由逾時處理函數調用中斷導致串連失敗,則關閉通訊端,並設定是由逾時導致的失敗 */ close(sockfd); if(errno == EINTR) errno = ETIMEDOUT; } /* 關閉 alarm */ alarm(0); /* 恢複原來的處理函數 */ MySignal(SIGALRM, sigfunc); return(n);}static void connect_alarm(int signo){ printf("flag: %d\n", signo); return;/* just interrupt the connect */}static Sigfunc *MySignal(int signo, Sigfunc *func){ Sigfunc *sigfunc; if( (sigfunc = M_signal(signo, func)) == SIG_ERR) err_sys("signal error"); return (sigfunc);}static Sigfunc *M_signal(int signo, Sigfunc *func){ struct sigaction act, oact; /* 設定訊號處理函數 */ act.sa_handler = func; /* 初始化訊號集 */ sigemptyset(&act.sa_mask); act.sa_flags = 0; if(signo == SIGALRM) {/* 若是SIGALRM訊號,則系統不會自己主動重新啟動 */#ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT;#endif } else {/* 其餘訊號設定為系統會自己主動重新啟動 */#ifdef SA_RESTART act.sa_flags |= SA_RESTART;#endif } /* 調用 sigaction 函數 */ if(sigaction(signo, &act, &oact) < 0) return(SIG_ERR); return(oact.sa_handler);}
使用 select 函數為 recvfrom 設定逾時。有關select 函數的解說能夠參考文章《I/O 多工》。
#include <sys/select.h>#include <stdlib.h>extern void err_sys(const char *,...);/* 在指定的時間內等待描寫敘述符變為可讀 */int readable_timeo(int fd, int sec){fd_setrset;struct timevaltv; /* 初始化fd_set 結構,並加入描寫敘述符 */FD_ZERO(&rset);FD_SET(fd, &rset); /* 設定逾時的時間 */tv.tv_sec = sec;/* 秒數 */tv.tv_usec = 0;/* 微秒 */ /* 調用select函數,使進程堵塞於select的逾時等待描寫敘述符變為可讀 */return(select(fd+1, &rset, NULL, NULL, &tv));/* 4> 0 if descriptor is readable */}/* end readable_timeo */intRead_timeo(int fd, int sec){intn;if ( (n = readable_timeo(fd, sec)) < 0)err_sys("readable_timeo error");return(n);}
使用 使用 SO_RCVTIMEO 和 SO_SNDTIMEO 通訊端選項,使用這兩個通訊端選項設定描寫敘述符時,其逾時設定將應用於該描寫敘述符的全部讀或寫操作上。SO_RCVTIMEO 通訊端選項僅僅能應用於讀操作,而 SO_SNDTIMEO 通訊端選項僅僅能應用於寫操作。
這兩者僅支援一部分實現。比如在 connect 函數不能使用這兩個通訊端選項。
recv 和 send 函數
/* 傳輸資料 *//* * 函數功能:發送資料; * 傳回值:若成功則返回傳送的位元組數。若出錯則返回-1; * 函數原型: */#include <sys/socket.h>ssize_t send(int sockfd, void *buff, size_t nbytes, int flags);/* * 說明 * 該函數的功能相似與write函數,除了有標識符flags之外。其它的同樣; * flags標識符的取值例如以下: * (1)MSG_DONTROUTE 勿將資料路由出本網 * (2)MSG_DONTWAIT 同意非堵塞操作 * (3)MSG_EOR 假設協議支援,此為記錄結束 * (4)MSG_OOB 假設協議支援。發送帶外資料 * * 若send成功返回,並不必定表示串連還有一端的進程接收資料,僅僅能說資料已經無錯誤地發送到網路。 * * 對於支援為報文設限的協議,若報文超過協議所支援的最大尺寸,send失敗並將errno設為EMSGSIZE; * 對於位元組流協議。send會堵塞直到整個資料被傳輸; *//* * 函數功能:接收資料; * 傳回值:以位元組計數的訊息長度,若無可用訊息或對方已經按序結束則返回0。若出錯則返回-1; * 函數原型: */#include <sys/socket.h>ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);/* * 說明 * 該函數的功能相似與read函數,除了有標識符flags之外,其它的同樣; * flags標識符的取值例如以下: * (1)MSG_PEEK 返回報文內容而不真正取走報文。即查看可讀取資料 * (2)MSG_TRUNC 即使報文被截斷,要求返回的是報文的實際長度 * (3)MSG_WAITALL 等待直到全部資料可用 * (4)MSG_OOB 假設協議支援,發送帶外資料 * (5)MSG_DONTWAIT 同意非堵塞操作 * */
以下是 flags 標誌的功能:
- MSG_DONTROUTE:本標誌告知核心。目的主機在某個直接連接的本網上,因而無需運行路由表尋找。
- MSG_DONTWAIT:本標誌在無需開啟對應通訊端的非堵塞標誌的前提下,把單個 I/O 操作暫時指定為非堵塞,接著運行 I/O 操作,然後關閉非堵塞標誌;
- MSG_OOB:若協議支援,則可發送帶外資料;
- MSG_PEEK :本標誌適用於 recv 和 recvfrom 函數,它同意查看已可讀取的資料,並且系統不在 recv 或 recvfrom 返回後丟棄這些資料。
- MSG_WAITALL:等待全部資料可用。
readv 和 writev 函數
這兩個函數類似於 read 和 write 函數,可是它們支援多個緩衝區操作。其定義例如以下:
/* 讀、寫多個非連續的緩衝區 *//* * 函數功能:讀取資料到多個非連續的緩衝區,或從多個非連續緩衝區寫資料到檔案。 * 傳回值:若成功則返回已讀、寫的位元組數,若出錯則返回-1。 * 函數原型: */#include <sys/uio.h>ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);/* * 說明: * iovec的指標結構例如以下: */struct iovec{ void *iov_base; /* starting address of buffer */ size_t iov_len; /* size of buffer */};
recvmsg 和 sendmsg 函數
這兩個函數是通用的 I/O 函數,前面介紹的 I/O 函數都能夠使用這兩個函數替換。其定義例如以下:
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);/* * 說明 * 該函數能夠使用不止一個的選擇來通過通訊端發送資料,能夠指定多重緩衝區資料轉送,相似與readv函數; * msghdr結構至少包括下面成員: */struct msghdr{ void *msg_name; /* optional address */ socklen_t msg_namelen; /* address size in bytes */ struct iovec *msg_iov; /* array of IO buffers */ int msg_iovlen; /* number of elements in array */ void *msg_control; /* ancillary data */ socklen_t msg_controllen; /* number of ancillary bytes */ int msg_flags; /* flags for recevied message */};
參考資料:
《Unix 網路編程》
著作權聲明:本文博主原創文章,部落格,未經同意不得轉載。
《網路編程》先進 I/O