UNIX网络编程——sockatmark函数
? ? ? ?每當(dāng)收到一個(gè)帶外數(shù)據(jù)時(shí),就有一個(gè)與之關(guān)聯(lián)的帶外標(biāo)記。這是發(fā)送進(jìn)程發(fā)送帶外字節(jié)時(shí)該字節(jié)在發(fā)送端普通數(shù)據(jù)流中的位置。在從套接字讀入期間,接收進(jìn)程通過調(diào)用sockatmark函數(shù)確定是否處于帶外標(biāo)記。
#include <sys/socket.h> int sockatmark(int sockfd); /* 返回值:如果在帶外標(biāo)記上為1, 不在標(biāo)記上為0, 出錯(cuò)為-1 */? ? ? ?本函數(shù)時(shí)POSIX創(chuàng)造的,如下給出了常見的SIOCATMARK? ioctl完成的本函數(shù)的一個(gè)實(shí)現(xiàn):
#include "unp.h"int sockatmark(int fd) {int flag;if (ioctl(fd, SIOCATMARK, &flag) < 0)return(-1);return(flag != 0); }? ? ? ?不管接收進(jìn)程在線(SO_OOBINLINE套接字選項(xiàng))還是帶外(MGS_OOB標(biāo)志)接收帶外數(shù)據(jù),帶外標(biāo)記都適合。帶外標(biāo)記的常見用法之一是接收進(jìn)程特殊的對(duì)待所有數(shù)據(jù),直到標(biāo)記通過。
1.例子???
????? 我們現(xiàn)在給出一個(gè)簡(jiǎn)單的例子說明帶外標(biāo)記的以下兩個(gè)特性:
(1)帶外標(biāo)記總是指向普通數(shù)據(jù)最后一個(gè)字節(jié)緊后的位置。這意味著,如果帶外數(shù)據(jù)在線接收,那么如果下一個(gè)待讀入的字節(jié)時(shí)使用MSG_OOB標(biāo)志發(fā)送的,sockatmask就返回真。而如果SO_OOBINLINE套接字選項(xiàng)沒有開啟,那么,若下一個(gè)待讀入的字節(jié)是跟在帶外數(shù)據(jù)后發(fā)送的第一個(gè)字節(jié),sockatmark就返回真。
(2)讀操作總是停在帶外標(biāo)記上。也就是說,如果在套接字接收緩沖區(qū)有100個(gè)字節(jié),不過在帶外標(biāo)記之前只有5個(gè)字節(jié),而進(jìn)程執(zhí)行一個(gè)請(qǐng)求100個(gè)字節(jié)的read調(diào)用,那么返回的是帶外標(biāo)記之前的5個(gè)字節(jié)。這種在帶外標(biāo)記上強(qiáng)制停止讀操作的做法使得進(jìn)程能夠調(diào)用sockatmark確實(shí)緩沖區(qū)指針是否處于帶外標(biāo)記。
???? 如下是我們的發(fā)送程序。它發(fā)送3個(gè)字節(jié)普通數(shù)據(jù),1個(gè)字節(jié)帶外數(shù)據(jù),再跟1個(gè)字節(jié)普通數(shù)據(jù)。每個(gè)輸出操作之間沒有停頓。
#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);} }
讀入來自發(fā)送進(jìn)程的所有數(shù)據(jù)
21-30?? 程序循環(huán)調(diào)用read,并顯示收到的數(shù)據(jù)。不過在調(diào)用read之前,先調(diào)用sockatmark檢查緩沖區(qū)指針是否處于帶外標(biāo)記。
我們運(yùn)行本程序得到如下輸出:
read 3 bytes:123 at OOB mask read 2bytes:45 recvived EOF? ? ? ?盡管接收進(jìn)程首次調(diào)用read時(shí)接收端TCP已經(jīng)接收了所有數(shù)據(jù)(因?yàn)榻邮者M(jìn)程調(diào)用了sleep),但是首次read調(diào)用因遇到帶外標(biāo)記而僅僅返回3個(gè)字節(jié)即在第四個(gè)字節(jié)(OOB標(biāo)記)將會(huì)停在這里。下一個(gè)讀入的字節(jié)時(shí)帶外字節(jié)(值為4),因?yàn)槲覀冊(cè)缦雀嬷獌?nèi)核在線放置帶外數(shù)據(jù)。2.例子
?我們現(xiàn)在給出另一個(gè)簡(jiǎn)單的例子,用于展示早先提到過的帶外數(shù)據(jù)的另外兩個(gè)特性。
(1)即使因?yàn)榱髁靠刂贫V拱l(fā)送數(shù)據(jù),TCP仍然發(fā)送帶外數(shù)據(jù)的通知(即它的緊急指針)。
(2)在帶外數(shù)據(jù)到達(dá)之前,接收進(jìn)程可能被通知說發(fā)送進(jìn)程已經(jīng)發(fā)送了帶外數(shù)據(jù)(使用SIGURG信號(hào)或通過select)。如果接收進(jìn)程接著指定MSG_OOB調(diào)用recv,而帶外數(shù)據(jù)卻尚未到達(dá),recv將返回EWOULDBLOCK錯(cuò)誤。
如下是發(fā)送程序:
#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?? 該進(jìn)程把它的套接字發(fā)送緩沖區(qū)大小設(shè)置為32768,寫出16384字節(jié)的普通數(shù)據(jù),然后睡眠5秒鐘。我們稍后將看到接收進(jìn)程把它的套接字接收緩沖區(qū)大小設(shè)置為4096,因此發(fā)送進(jìn)程的這些操作確保發(fā)送端TCP填滿接收端得套接字接收緩沖區(qū)。發(fā)送進(jìn)程接著發(fā)送單字節(jié)的帶外數(shù)據(jù),后跟1024字節(jié)的普通數(shù)據(jù),然后終止。
?
?
?如下是接收程序:
#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?? 接收進(jìn)程把監(jiān)聽套接字接收緩沖區(qū)大小設(shè)置為4096.連接建立之后,這個(gè)大小將傳承給已連接套接字。接收進(jìn)程接著accept連接,建立一個(gè)SIGURG信號(hào)處理函數(shù),并建立套接字的屬主。主程序然后再一個(gè)無窮循環(huán)中調(diào)用pause。
31-41?? 信號(hào)處理函數(shù)調(diào)用recv讀入帶外數(shù)據(jù)。
? ? ? ?我們先啟動(dòng)接收進(jìn)程,接著啟動(dòng)發(fā)送進(jìn)程,以下是來自發(fā)送進(jìn)程的輸出:
SIGURG received recv error:Resource temporarily unavailable
? ? ? ?接收進(jìn)程的輸出結(jié)果說明了(2)。
? ? ? ?發(fā)送端TCP向接收端TCP發(fā)送了帶外通知,由此產(chǎn)生遞交給接收進(jìn)程的SIGURG信號(hào)。然而當(dāng)接收進(jìn)程指定MSG_OOB標(biāo)志調(diào)用recv時(shí),相應(yīng)帶外字節(jié)不能讀入因?yàn)閹鈹?shù)據(jù)還沒有到達(dá)。
? ? ? ?解決辦法是讓接收進(jìn)程通知讀入已排隊(duì)的普通數(shù)據(jù),在套接字接收緩沖區(qū)中騰出空間。這將導(dǎo)致接收端TCP向發(fā)送端通告一個(gè)非零的窗口,最終允許發(fā)送帶外字節(jié)。3.例子
????? 我們下一個(gè)例子展示了一個(gè)給定TCP連接只有一個(gè)帶外標(biāo)記,如果在接收進(jìn)程讀入某個(gè)現(xiàn)有帶外數(shù)據(jù)之前有新的帶外數(shù)據(jù)到達(dá),先前的標(biāo)記就丟失。
????? 下面是發(fā)送程序:
? ? ? ?接收端,它在接收連接之后睡眠5秒,以允許來自發(fā)送端得數(shù)據(jù)到達(dá)接收TCP。以下是接收進(jìn)程的輸出:
read 5 bytes:12345 at OOB mark read 2bytes:67 received EOF? ? ? ?第二個(gè)帶外字節(jié)(6)的到來覆寫了第一個(gè)帶外字節(jié)(4)到來時(shí)存放的帶外標(biāo)記。正像我們所說,每個(gè)TCP連接最多只有一個(gè)帶外標(biāo)記。
轉(zhuǎn)載于:https://www.cnblogs.com/hehehaha/p/6332568.html
總結(jié)
以上是生活随笔為你收集整理的UNIX网络编程——sockatmark函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Eclipse字符集设置方式
- 下一篇: PHP的SPFA,由于是之前的c代码,风