一、
读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"); ... } }