UNIX网络编程——sockatmark函数
? ? ? ?每當收到一個帶外數據時,就有一個與之關聯的帶外標記。這是發送進程發送帶外字節時該字節在發送端普通數據流中的位置。在從套接字讀入期間,接收進程通過調用sockatmark函數確定是否處于帶外標記。
#include <sys/socket.h> int sockatmark(int sockfd); /* 返回值:如果在帶外標記上為1, 不在標記上為0, 出錯為-1 */? ? ? ?本函數時POSIX創造的,如下給出了常見的SIOCATMARK? ioctl完成的本函數的一個實現:
#include "unp.h"int sockatmark(int fd) {int flag;if (ioctl(fd, SIOCATMARK, &flag) < 0)return(-1);return(flag != 0); }? ? ? ?不管接收進程在線(SO_OOBINLINE套接字選項)還是帶外(MGS_OOB標志)接收帶外數據,帶外標記都適合。帶外標記的常見用法之一是接收進程特殊的對待所有數據,直到標記通過。
1.例子???
????? 我們現在給出一個簡單的例子說明帶外標記的以下兩個特性:
(1)帶外標記總是指向普通數據最后一個字節緊后的位置。這意味著,如果帶外數據在線接收,那么如果下一個待讀入的字節時使用MSG_OOB標志發送的,sockatmask就返回真。而如果SO_OOBINLINE套接字選項沒有開啟,那么,若下一個待讀入的字節是跟在帶外數據后發送的第一個字節,sockatmark就返回真。
(2)讀操作總是停在帶外標記上。也就是說,如果在套接字接收緩沖區有100個字節,不過在帶外標記之前只有5個字節,而進程執行一個請求100個字節的read調用,那么返回的是帶外標記之前的5個字節。這種在帶外標記上強制停止讀操作的做法使得進程能夠調用sockatmark確實緩沖區指針是否處于帶外標記。
???? 如下是我們的發送程序。它發送3個字節普通數據,1個字節帶外數據,再跟1個字節普通數據。每個輸出操作之間沒有停頓。
#include "unp.h"int main(int argc, char **argv) {int listenfd, connfd, n, on=1;char buff[100];if (argc == 2)listenfd = Tcp_listen(NULL, argv[1], NULL);else if (argc == 3)listenfd = Tcp_listen(argv[1], argv[2], NULL);elseerr_quit("usage: tcprecv04 [ <host> ] <port#>");Setsockopt(listenfd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on));connfd = Accept(listenfd, NULL, NULL);sleep(5);for ( ; ; ) {if (Sockatmark(connfd))printf("at OOB mark\n");if ( (n = Read(connfd, buff, sizeof(buff)-1)) == 0) {printf("received EOF\n");exit(0);}buff[n] = 0; /* null terminate */printf("read %d bytes: %s\n", n, buff);} }
讀入來自發送進程的所有數據
21-30?? 程序循環調用read,并顯示收到的數據。不過在調用read之前,先調用sockatmark檢查緩沖區指針是否處于帶外標記。
我們運行本程序得到如下輸出:
read 3 bytes:123 at OOB mask read 2bytes:45 recvived EOF? ? ? ?盡管接收進程首次調用read時接收端TCP已經接收了所有數據(因為接收進程調用了sleep),但是首次read調用因遇到帶外標記而僅僅返回3個字節即在第四個字節(OOB標記)將會停在這里。下一個讀入的字節時帶外字節(值為4),因為我們早先告知內核在線放置帶外數據。2.例子
?我們現在給出另一個簡單的例子,用于展示早先提到過的帶外數據的另外兩個特性。
(1)即使因為流量控制而停止發送數據,TCP仍然發送帶外數據的通知(即它的緊急指針)。
(2)在帶外數據到達之前,接收進程可能被通知說發送進程已經發送了帶外數據(使用SIGURG信號或通過select)。如果接收進程接著指定MSG_OOB調用recv,而帶外數據卻尚未到達,recv將返回EWOULDBLOCK錯誤。
如下是發送程序:
#include "unp.h"int main(int argc, char **argv) {int sockfd, size;char buff[16384];if (argc != 3)err_quit("usage: tcpsend05 <host> <port#>");sockfd = Tcp_connect(argv[1], argv[2]);size = 32768;Setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));Write(sockfd, buff, 16384);printf("wrote 16384 bytes of normal data\n");sleep(5);Send(sockfd, "a", 1, MSG_OOB);printf("wrote 1 byte of OOB data\n");Write(sockfd, buff, 1024);printf("wrote 1024 bytes of normal data\n");exit(0); }15-25?? 該進程把它的套接字發送緩沖區大小設置為32768,寫出16384字節的普通數據,然后睡眠5秒鐘。我們稍后將看到接收進程把它的套接字接收緩沖區大小設置為4096,因此發送進程的這些操作確保發送端TCP填滿接收端得套接字接收緩沖區。發送進程接著發送單字節的帶外數據,后跟1024字節的普通數據,然后終止。
?
?
?如下是接收程序:
#include "unp.h"int listenfd, connfd;void sig_urg(int);int main(int argc, char **argv) {int size;if (argc == 2)listenfd = Tcp_listen(NULL, argv[1], NULL);else if (argc == 3)listenfd = Tcp_listen(argv[1], argv[2], NULL);elseerr_quit("usage: tcprecv05 [ <host> ] <port#>");size = 4096;Setsockopt(listenfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));connfd = Accept(listenfd, NULL, NULL);Signal(SIGURG, sig_urg);Fcntl(connfd, F_SETOWN, getpid());for ( ; ; )pause(); }void sig_urg(int signo) {int n;char buff[2048];printf("SIGURG received\n");n = Recv(connfd, buff, sizeof(buff)-1, MSG_OOB);buff[n] = 0; /* null terminate */printf("read %d OOB byte\n", n); }19-28?? 接收進程把監聽套接字接收緩沖區大小設置為4096.連接建立之后,這個大小將傳承給已連接套接字。接收進程接著accept連接,建立一個SIGURG信號處理函數,并建立套接字的屬主。主程序然后再一個無窮循環中調用pause。
31-41?? 信號處理函數調用recv讀入帶外數據。
? ? ? ?我們先啟動接收進程,接著啟動發送進程,以下是來自發送進程的輸出:
SIGURG received recv error:Resource temporarily unavailable
? ? ? ?接收進程的輸出結果說明了(2)。
? ? ? ?發送端TCP向接收端TCP發送了帶外通知,由此產生遞交給接收進程的SIGURG信號。然而當接收進程指定MSG_OOB標志調用recv時,相應帶外字節不能讀入因為帶外數據還沒有到達。
? ? ? ?解決辦法是讓接收進程通知讀入已排隊的普通數據,在套接字接收緩沖區中騰出空間。這將導致接收端TCP向發送端通告一個非零的窗口,最終允許發送帶外字節。3.例子
????? 我們下一個例子展示了一個給定TCP連接只有一個帶外標記,如果在接收進程讀入某個現有帶外數據之前有新的帶外數據到達,先前的標記就丟失。
????? 下面是發送程序:
? ? ? ?接收端,它在接收連接之后睡眠5秒,以允許來自發送端得數據到達接收TCP。以下是接收進程的輸出:
read 5 bytes:12345 at OOB mark read 2bytes:67 received EOF? ? ? ?第二個帶外字節(6)的到來覆寫了第一個帶外字節(4)到來時存放的帶外標記。正像我們所說,每個TCP連接最多只有一個帶外標記。
轉載于:https://www.cnblogs.com/hehehaha/p/6332568.html
總結
以上是生活随笔為你收集整理的UNIX网络编程——sockatmark函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Eclipse字符集设置方式
- 下一篇: PHP的SPFA,由于是之前的c代码,风