关于TCP带外数据

时间:2021-04-17 03:59:55

一、

读UNP,看到24章的带外数据。书上还给出了一个小例子 ------ 一个服务端接收普通数据和带外数据,另外有个客户端发送普通数据和带外数据。接收端的普通数据和带外数据是分开接收的。普通数据就用普通的recv,而带外数据是在SIGURG信号处理函数里面用recv(... MSG_OOB)接收的。我冒出个想法,在SIGURG信号处理函数里面,如果我在recv( MSG_OOB)之前用普通的recv接收普通的数据,能否接收到什么。代码如下:

void
sig_urg(int signo)
{
printf("SIGURG received\n");

// 我增加的
if ((n = recv(connfd, buf, size(buf)-1, MSG_DONTWAIT)) == -1) {
if (errno == EAGAIN)
perror("err, EAGAIN");
}

if ((n = recv(connfd, buf, sizeof(buf)-1, MSG_OOB) == -1) {
perror("recv MSG_OOB error");
return;
}
buf[n] = '\0';
printf("read %d bytes: %s\n", n, buf);
}

 

中间那几行是我增加的。我是这么想的:这个信号处理函数被调用时,肯定是套接字进程收到了SIGURG信号了,即套接字中有紧急数据。但是此时并没有普通数据(这个通过发送端控制)。所以我增加的那一段代码,应该走到perror("err, EAGAIN")。然后接着往下,recv(... MSG_OOB)应该能接收到带外数据(发送端控制带外数据发送过来)。可是运行结果出我意料:recv(...MSG_OOB)出现argument invalid错误,也就是说没有收到带外数据。把我增加的那部分代码去掉,就能接收带外数据。

 recv(connfd, buf, size(buf)-1, MSG_DONTWAIT))把带外数据弄没了,为什么?这个是接收普通数据的啊,为什么会影响到带外数据呢?要看看内核是如何实现的。

还有一点要注意:如果带外数据发了2个字节过来,接收端怎么处理?接收端触发SIGURT信号,进入sig_urg函数,recv( MSG_OOB)只能接收到一个字节----带外数据的最后一个字节。剩下的字节会由普通的recv(...)读取到。

二、sockatmark函数

既然有了sig_urg, recv( MSG_OOB)了,为什么还要这个函数呢?查一下man page,它几句话就表达清楚了。 sockatmark()  returns  a value indicating whether or not the socket referred to by the file descriptor fd is at the out-of-band mark...。

现在来看看到底"at the out-of-band mark"指什么语义,它是指接收到一个TCP包(头部设置了URG),还是指真正的带外数据到达。注意这两者是有区别的。书上有句话:”在接收进程已被告知对端发送了一个带外数据(通过SIGURG或select)的前提下,如果接收进程试图读入该字节,但是该字节尚未达到,...“。怎么会有这种情况发生呢?当发送端调用recv(fd, "aaaaa", 5, MSG_OOB),tcp封包,但是由于自己缓冲满了,或者对端MSS窗口很小而容不下5个字节时,就会产生两个TCP包,第一个包设置了URG但真正带外数据并不在内,后面第二个包才真正包含那个带外数据。这时候接收端就会出现书上那句话的情况。

而sockatmark就是用来判断真正的带外数据到达。man page中的例子很好的说明了它的用途:

  //The following code can be used after receipt of a SIGURG signal to read (and discard) all  data  up  to  the       mark, and then read the byte of data at the mark:           char buf[BUF_LEN];           char oobdata;           int atmark, s;           for (;;) {               atmark = sockatmark(fd);               if (atmark == -1) {                   perror("sockatmark");                   break;               }               if (atmark)                   break;               s = read(fd, buf, BUF_LEN) <= 0);               if (s == -1)                   perror("read");               if (s <= 0)                   break;           }           if (atmark == 1) {               if (recv(fd, &oobdata, 1, MSG_OOB) == -1) {                   perror("recv");                   ...               }           }