帶外數據OOB與緊急模式URG

A,TCP支持帶外數據OOB?與緊急模式URG有什麼關係?
     TCP
支持帶外數據,但是隻有一個OOB字節,TCP的帶外數據是通過緊急模式URG實現的
.
B,
我們知道send(sendfd,"ABC",3,MSG_OOB),將發送3個字節的帶外數據OOB數據.但是這裏TCP又只支持一個字節的OOB,難道丟掉2個字節
?
     TCP
將把緊急模式URG 置位,緊急指針定位第三個字節("C")(這裏不管它的具體位置,緊急指針的作用就是提供定位那個OOB字節的信息),前兩個字節("AB")當作普通字節發送.其實TCP總是把最後一個字節當作OOB數據,其他的當作普通字節.不管你通過帶MSG_OOB標誌的sendxxx函數發送多少字節帶外數據OOB數據,發送端只把最後一個字節當作OOB數據,接收端也只能收到一個字節的OOB數據
.
C,
如果一定要發送多字節的帶外數據,讓接收端能一次收到多個字節的帶外數據.能不能做到
?
     
對於TCP協議,不能
!
D,
對於TCP,收到的帶外數據怎麼保存
?
     
兩種模式
:
     1,
OOBINLINE 模式,這是套接字的默認模式,OOB字節與普通字節分開存放.存放在一個OOB緩衝區中,當然TCP只有一個字節,可以用一個字節保存OOB數據
.
     2,OOBINLINE 
模式,OOB字節和普通字節一起存放,它和普通字節本來就是一起發送,當然可以一起存放
.
E,recv(recvfd,buff,256,MSG_OOB).
會有哪些結果
?
     recvxxxx函數,在MSG_OOB模式下,將在OOB緩衝區中尋找數據。
     如果發送端沒發送OOB字節,它返回錯誤.
     
如果發送端發送了OOB字節
:
     1,
對於非OOBINLINE 模式,它返回1字節的OOB數據
.
     2,
對於OOBINLINE 模式,它返回錯誤.因爲OOB字節沒有放到OOB緩衝區中
.
F,
如果發送端使用MSG_OOB模式,send(sendfd,sndbuff,64,MSG_OOB),發送了包含"OOB字節"64字節數據,然後用非MSG_OOB模式,send(sendfd,sndbuff,64,0)發送64字節,當接收端收到64+64字節的數據後,recv(recvfd,revbuff,256,0).會有哪些結果
?
1,
對於非OOBINLINE 模式,第一次recv(recvfd,revbuff,256,0)只返回前63字節的普通數據,接收緩衝區剩下64字節.要獲得1字節的OOB數據,必須使用MSG_OOB模式的revxxx函數.再次recv(recvfd,revbuff,256,0),返回第二次發送的64字節.一次recvxxx不跨越urg-mark標記
.
2,
對於OOBINLINE 模式,第一次recv(recvfd,revbuff,256,0)只返回前63字節的普通數據,接收緩衝區剩下65字節(OOB+64字節),第二次recv(recvfd,revbuff,256,0),對於windows,只返回一字節的OOB字節,需要第三次rev才能返回最後的64字節,對於linux/unix,第二次rev 就返回65字節(OOB+64字節).總之與協議棧的實現有關
.
G,
如果OOB字節沒被應用程序讀取,協議棧又收到了新的OOB字節,會出現什麼情況
?
    TCP
協議對每個socket保持一個URG指針,此時直接刷新URG指針,指向新的OOB字節
.
對於非OOBINLINE,舊的OOB字節直接被丟棄,被新的OOB字節覆蓋
.
對於OOBINLINE,舊的OOB字節仍然在接收緩衝區中,但被當着普通數據看待,每個socket只有一個URG指針,只能定位一個OOB字節.

許多傳輸層都支持帶外數據(Out-Of-Band data),有時候也稱爲快速數據(Expedited
Data).之所以有帶外數據的概念,是因爲有時候在一個網絡連接的終端想“快速”的告訴
網絡另一邊的終端一些信息.這個“快速”的意思是我們的“提示”信息會在正常的網絡
數據(有時候稱爲帶內數據In-Band data)之前到達網絡另一邊的終端.這說明,帶外數
據擁有比一般數據高的優先級.但是不要以爲帶外數據是通過兩條套接字連接來實現的.事
實上,帶外數據也是通過以有的連接來傳輸。
不幸的是,幾乎每個傳輸層都有不同的帶外數據的處理方法。我們下面研究的是TCP
模型的帶外數據,提供一個小小的例子來看看它是怎樣處理套接字的帶外數據,及調用套
接字API 的方法。
流套接字的抽象中包括了帶外數據這一概念,帶外數據是相連的每一對流套接字間一
個邏輯上獨立的傳輸通道。帶外數據是獨立於普通數據傳送給用戶的,這一抽象要求帶外
數據設備必須支持每一時刻至少一個帶外數據消息被可靠地傳送。這一消息可能包含至少
一個字節;並且在任何時刻僅有一個帶外數據信息等候發送。對於僅支持帶內數據的通訊
協議來說(例如緊急數據是與普通數據在同一序列中發送的),系統通常把緊急數據從普
通數據中分離出來單獨存放。這就允許用戶可以在順序接收緊急數據和非順序接收緊急數
據之間作出選擇(非順序接收時可以省去緩存重疊數據的麻煩)。在這種情況下,用戶也
可以“偷看一眼”緊急數據。
某一個應用程序也可能喜歡線內處理緊急數據,即把其作爲普通數據流的一部分。這
可以靠設置套接字選項中的SO_OOBINLINE 來實現。在這種情況下,應用程序可能希望
確定未讀數據中的哪一些是“緊急”的(“緊急”這一術語通常應用於線內帶外數據)。爲
了達到這個目的,在Sockets 的實現中就要在數據流保留一個邏輯記號來指出帶外數據從
哪一點開始發送.
select()函數可以用於處理對帶外數據到來的通知。
6.11.1 TCP 的帶外數據
TCP 上沒有真正意義上的“帶外數據”。TCP 是由一種叫做“緊急模式”的方法來傳
輸帶外數據的。假設一個進程向一個TCP 套接字寫入了N 個字節的數據,數據被TCP 套
接字的發送緩衝區緩存,等待被髮送到網絡上面.我們在圖6-10 可以看見數據的排列。
第6 章berkeley 套接字- 191 -
圖6-10 TCP 數據的排列
現在進程使用以MSG_OOB 爲參數的send()函數寫入一個單字節的"帶外數據",包
含一個ASCII 字符"a":
send(fd, “a”, 1, MSG_OOB);
TCP 將數據放在下一個可用的發送緩衝區中,並設置這個連接的"緊急指針"(urgent
pointer)指向下一個可用的緩衝區空間.圖6-11 表示了我們描述的這個狀態,並將帶外數
據(Out-Of-Band)表示爲"OOB"。
圖6-11 ODB 數據
TCP 的緊急指針的指向的位置是在程序發送的OOB 數據的後面。
由圖6-11 所表示的TCP 套接字的狀態,得知下一個將要發送的數據是TCP 的URG
(Urgent pointer)標誌,發送完URG 標誌,TCP 纔會發送下面的帶外數據的那個字節。
但是TCP 所一次發送的數據中,可能只包含了TCP 的URG 標誌,卻沒有包含我們所發送
的OOB 數據.是否會發生這種情況而取決於TCP 將要發送的數據隊列中,在OOB 數據
之前的數據的多少。如果在一次發送中,OOB 前的數據已經佔滿了名額,則TCP 只會發
送URG 標誌,不會發送OOB數據
這是一個TCP 緊急數據狀態的重要特性:TCP 的信息頭指出發送者進入了緊急模式(比
方說,在緊急偏移處設置了URG 標誌),但是緊急偏移處的數據並沒有必要一定要發送出
去.事實上,如果一個TCP 套接字的流傳送停止後(可能是接收方的套接字的接收緩衝區
沒有空餘空間),爲了發送帶外數據,系統會發送不包含數據的TCP 數據包,裏面標明這
是一個帶外數據.這也是我們使用帶外數據的一個有利點:TCP 連接就算是在不能向對方
- 192 - Linux網絡編程
發送數據的時候,也可以發送出一個帶外數據的信號。
如果我們像下面這樣發送一個多字節的帶外數據:
send(fd, “abc”, 3, MSG_OOB);
在這個例子中, TCP 的緊急指針指向了數據最後一位的後面, 所以只有最後一位數
據(“c”)才被系統認爲是“帶外數據”。
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
我們上面大致瞭解了發送方是怎樣發送“帶外數據”的了,下面我們來看一看接收方
是怎樣接收“帶外數據”的。
1.當TCP 收到一個包含URG 標誌的數據段時,TCP 會檢查“緊急指針”來驗證是
否緊急指針所指的數據已經到達本地。也就是說,無論這次是否是TCP 緊急模式從發送方
到接收方的第一次傳輸帶外數據。一般來說,TCP 傳輸數據會分成許多小的數據包來傳輸
(每個包的到達時間也不同)。可能有好幾個數據包中都包含緊急指針,但是這幾個包中
的緊急指針都是指向同一個位置的,也就是說多個緊急指針指向一個數據。需要注意的是,
對於這一個帶外數據,雖然有多個指針指向它,但是隻有第一個緊急指針會通知程序注意。
2.接收進程收到另外一個帶外數據的通知的條件是:有另外一個帶外數據的指針到
達.注意這裏是“另外一個帶外數據”的指針,不是上面的“一個帶外數據”的另外一個
指針。首先, SIGURG 信號回發送給套接字的屬主,這個取決於是否已經使用fcntl()函數
或ioctl()函數設定套接字的屬主和這個程序對SIGURG 信號的具體操作函數。其次,如果
一個程序正阻塞與對這個套接字描述符的select()函數的調用中,則select()函數會產生一個
例外,然後返回。
注意:進程收到帶外數據的通知的時候,並不會在乎帶外數據的真正數據是否到達。
3.當緊急指針所指的真正的帶外數據通過TCP 網絡到達接收端的時候,數據或者被
放入帶外數據緩衝區或是隻是簡單的和普通的網絡數據混合在一起。在缺省的條件下,
SO_OOBINLINE 套接字選項是不會被設置的,所以這個單字節的帶外數據並沒有被防在
套接字的接收緩存區中,而是被放入屬於這個套接字的一個單獨的帶外數據緩存區中。如
果這個進程想讀取這個帶外數據的具體內容的話,唯一的辦法就是調用recv,recvfrom,
或是recvmsg 函數,並且一定要指定MSG_OOB 標誌。
4.如果一個進程將套接字設置爲SO_OOBINLINE 屬性,則由緊急指針所指的,代表
帶外數據的那個字節將回被放在正常套接字緩衝區中的最左邊.在這種情況下,進程不能
指定MSG_OOB 來讀取這個一個字節的帶外數據,但是它可以知道帶外數據的到達時間:
通過檢查套接字的帶外數據標記.
有可能發生的一些錯誤:
5.如果當連接沒有發送帶外數據的時候進程來讀取帶外數據(比如說,通過MSG_OOB
參數來接收函數),則EINVAL 將會被返回。
6.當真正的帶外數據到達之前的時候,進程被通知(SIGURG 或是select 函數)有帶
外數據到達(也就是說帶外數據的通知信號已經到達),如果進程嘗試讀取帶外數據,則
返回EWOULDFBLOCK .進程所能做的只是去讀取套接字的接收緩存區.(也許,由於
緩存區的數據以滿,帶外數據的那個字節信息無法傳輸過來,這樣的話也許你需要清理一
下接收緩存區來給帶外數據空出一些空間)
7.如果進程嘗試多次讀取同一個帶外數據,則EINVAL 將會被返回。
8.如果進程將套接字屬性設置爲SO_OOBINLINE ,然後嘗試通過指定MSG_OOB
第6 章berkeley 套接字- 193 -
標誌來讀取帶外數據,則EINVAL 將會被返回。
下面我們將前面的套接字例程做一些變動來測試帶外數據的發送與接收.
6.11.2 OOB 傳輸套接字例程(服務器代碼Server.c)
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
/* 服務器要監聽的本地端口*/
#define MYPORT 4000
/* 能夠同時接受多少沒有accept 的連接*/
#define BACKLOG 10
void
sig_urg(int signo);
main()
{
/* 在sock_fd 上進行監聽,new_fd 接受新的連接*/
int sock_fd, new_fd ;
/* 用於存儲以前系統缺省的SIGURL 處理器的變量*/ void * old_sig_urg_handle ;
/* 自己的地址信息*/
struct sockaddr_in my_addr;
/* 連接者的地址信息*/
struct sockaddr_in their_addr;
int sin_size;
int n ;
char buff[100] ;
/* 這裏就是我們一直強調的錯誤檢查.如果調用socket() 出錯,則返回*/
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
- 194 - Linux網絡編程
/* 輸出錯誤提示並退出*/
perror(“socket”);
exit(1);
}
/* 主機字節順序*/
my_addr.sin_family = AF_INET;
/* 網絡字節順序,短整型*/
my_addr.sin_port = htons(MYPORT);
/* 將運行程序機器的IP 填充入s_addr */
my_addr.sin_addr.s_addr = INADDR_ANY;
/* 將此結構的其餘空間清零*/
bzero(&(my_addr.sin_zero), 8);
/* 這裏是我們一直強調的錯誤檢查!! */ if (bind(sockfd, (struct sockaddr *)&my_addr,
sizeof(struct sockaddr)) == -1)
{
/* 如果調用bind()失敗,則給出錯誤提示,退出*/
perror(“bind”);
exit(1);
}
/* 這裏是我們一直強調的錯誤檢查!! */
if (listen(sockfd, BACKLOG) == -1)
{
/* 如果調用listen 失敗,則給出錯誤提示,退出*/
perror(“listen”);
exit(1);
}
/* 設置SIGURG 的處理函數 sig_urg */
old_sig_urg_handle = signal(SIGURG, sig_urg);
/* 更改connfd 的屬主*/
fcntl(sockfd, F_SETOWN, getpid());
while(1)
{
第6 章berkeley 套接字- 195 -
/* 這裏是主accept()循環*/
sin_size = sizeof(struct sockaddr_in);
/* 這裏是我們一直強調的錯誤檢查!! */
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)
{
/* 如果調用accept()出現錯誤,則給出錯誤提示,進入下一個循環*/
perror(“accept”);
continue;
}
/* 服務器給出出現連接的信息*/
printf(“server: got connection from %s/n”, inet_ntoa(their_addr.sin_addr));
/* 這裏將建立一個子進程來和剛剛建立的套接字進行通訊*/
if (!fork())
/* 這裏是子進程*/
while(1)
{
if((n = recv(new_fd, buff, sizeof(buff)–1)) == 0)
{
printf(“received EOF/n”);
break ;
}
buff[n] = 0 ;
printf(“Recv %d bytes: %s/n”, n, buff);
}
/* 關閉new_fd 代表的這個套接字連接*/
close(new_fd);
}
}
/* 等待所有的子進程都退出*/
while(waitpid(-1,NULL,WNOHANG) > 0);
/* 恢復系統以前對SIGURG 的處理器*/
signal(SIGURG, old_sig_urg_handle);
}
void
sig_urg(int signo)
{
- 196 - Linux網絡編程
int n;
char buff[100] ;
printf(“SIGURG received/n”);
n = recv(new_fd, buff, sizeof(buff)– 1, MSG_OOB);
buff [ n ] = 0 ;
printf(“recv %d OOB byte: %s/n” , n,buff);
}
6.11.3 OOB 傳輸套接字例程(客戶端代碼Client.c)
下面是客戶端程序:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
/* 服務器程序監聽的端口號*/
#define PORT 4000
/* 我們一次所能夠接收的最大字節數*/
#define MAXDATASIZE 100
int
main(int argc, char *argv[])
{
/* 套接字描述符*/
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct hostent *he;
/* 連接者的主機信息*/
struct sockaddr_in their_addr;
/* 檢查參數信息*/
if (argc != 2)
{
第6 章berkeley 套接字- 197 -
/* 如果沒有參數,則給出使用方法後退出*/
fprintf(stderr,“usage: client hostname/n”);
exit(1);
}
/* 取得主機信息*/
if ((he=gethostbyname(argv[1])) == NULL)
/* 如果gethostbyname()發生錯誤,則顯示錯誤信息並退出*/
herror(“gethostbyname”);
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
/* 如果socket()調用出現錯誤則顯示錯誤信息並退出*/
perror(“socket”);
exit(1);
}
/* 主機字節順序*/
their_addr.sin_family = AF_INET;
/* 網絡字節順序,短整型*/
their_addr.sin_port = htons(PORT);
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
/* 將結構剩下的部分清零*/
bzero(&(their_addr.sin_zero), 8);
if(connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1)
{
/* 如果connect()建立連接錯誤,則顯示出錯誤信息,退出*/
perror(“connect”);
exit(1);
}
/* 這裏就是我們說的錯誤檢查! */
if (send(new_fd, “123”, 3, 0) == -1)
{
/* 如果錯誤,則給出錯誤提示,然後關閉這個新連接,退出*/
perror(“send”);
close(new_fd);
- 198 - Linux網絡編程
exit(0);
}
printf(“Send 3 byte of normal data/n”);
/* 睡眠1 秒*/
sleep(1);
if (send(new_fd, “4”, 1, MSG_OOB)== -1)
{
perror(“send”);
close(new_fd);
exit(0);
}
printf(“Send 1 byte of OOB data/n”);
sleep(1);
if (send(new_fd, “56”, 2, 0) == -1)
{
perror(“send”);
close(new_fd);
exit(0);
}
printf(“Send 2 bytes of normal data/n”);
sleep(1);
if (send(new_fd,“7”, 1, MSG_OOB)== -1)
{
perror(“send”);
close(new_fd);
exit(0);
}
printf(“Send 1 byte of OOB data/n”);
sleep(1);
if (send(new_fd, “89”, 2, MSG_OOB)== -1)
{
perror(“send”);
close(new_fd);
exit(0);
}
printf(“Send 2 bytes of normal data/n”);
sleep(1);
第6 章berkeley 套接字- 199 -
close(sockfd);
return 0;
}
6.11.4 編譯例子
注意:你顯然需要在運行client 之前啓動server.否則client 會執行出錯(顯示“Connection
refused”).
當只有一個連接的時候(因爲這個服務器是多進程的,所以如果有多個連接同時存在
可能會導致屏幕輸出混亂),可以得到下面的結果:(注意是使用我們下面的客戶程序來連
接的,並且假設你運行我們的服務器程序是在本地機器上面)
root@bbs# gcc –o server server.c
root@bbs# gcc –o client client.c
root@bbs# ./server
root@bbs# ./client 127.0.0.1
Send 3 bytes of normal data <- Client輸出
Recv 3 bytes: 123 <- Server輸出
Send 1 byte of OOB data <- Client輸出
SIGURG received <- Server輸出
Recv 1 OOB byte: 4 <- Server輸出
Send 2 bytes of normal data <- Client輸出
Recv 2 bytes: 56 <- Server輸出
Send 1 byte of OOB data <- Client輸出
SIGURG Received <- Server輸出
Recv 1 OOB byte: 7 <- Server輸出
received EOF <- Server輸出
這個結果正是我們想要的。每一個客戶端發送的帶外數據都導致服務器端產生了

SIGURG 信號,服務器端收到SIGURG 信號後,就去讀取帶外數據了。



來源:http://www.xuebuyuan.com/2184933.html

關於帶外數據:http://wenku.baidu.com/view/f04a4dff9e31433239689341?fr=prin

http://book.51cto.com/art/201306/400276.htm

http://baike.baidu.com/view/567593.htm

http://blog.csdn.net/todd911/article/details/22821529

發佈了17 篇原創文章 · 獲贊 33 · 訪問量 39萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章