在socket建立时,linux的内核会为socket分配一个发送缓冲区和一个接收缓冲区,实际上是一个struct sk_buff的队列,当我们调用send/sendto/sendmsg及recv/recvfrom/recvmsg时,会发送或接收队列中的第一个数据。
现在我们只考虑接收的情况,如果甲一直不停的向乙发送消息,而乙中每次调用recv/recvfrom/recvmsg都想得到最新的数据,怎么办?
这就需要我们对linux的缓冲区进行清空。而缓冲区是在内核中的,不允许直接操作。还好,有两个函数可以解决这个问题。
第一个是int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len), 他是对
socket的设置。我们可以通过它修改接收缓冲区的大小。他还有很多功能,见man setsockopt.
int lLen, lRet
lRet = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &lLen, sizeof(int));
sock就是我们创建的socket 的描述符。
如果对linux内核代码进行研究,会发现lLen不是设置成所有的值都有效。
if (val > sysctl_rmem_max)
val = sysctl_rmem_max;
set_rcvbuf:
sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
/* FIXME: is this lower bound the right one? */
if ((val * 2) < SOCK_MIN_RCVBUF)
sk->sk_rcvbuf = SOCK_MIN_RCVBUF;
else
sk->sk_rcvbuf = val * 2;
break;
也就说,他有一个最大值和一个最小值。 最大值是262144, 最小值是256。
我们在看一下内核中缓冲区分配的函数
...
size = SKB_DATA_ALIGN(size);
...
skb->truesize = size + sizeof(struct sk_buff);
在sock_queue_rcv_skb函数中有
if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >=
(unsigned)sk->sk_rcvbuf) {
err = -ENOMEM;
goto out;
}
这样,当接收到一个数据包后,内核缓冲区会分配size + sizeof(struct sk_buff)大小的缓冲区,其中size是cache line对齐的。
也就是说,只有我们设置的缓冲区大于这个值,数据才会到用户空间。
好了,我们设置了接收区的大小以后,缓冲区中只有一个数据包,可以用select函数进行清空。
struct timeval stWait;
fd_set stFdSet;
int lRet;
stWait.tv_sec = 0;
stWait.tv_usec = 0;
while(1)
{
FD_ZEROS(&stFdSet);
FD_SET(g_lSock, &stFdSet);
lRet = select(FD_SETSIZE, &stFdSet, NULL, NULL, &stWait);
if(lRet == 0)
break;
recvfrom(g_lSock, cBuf, sizeof(cBuf), 0, (struct sockaddr *)tClient, &lLen);
}
然后再调用recvfrom接收最新的消息。