Ip_ssize_t recv(Ip_fd sock, void *buf, Ip_size_t len, int flags);
Ip_ssize_t recvfrom(Ip_fd fd, void *buf, Ip_size_t len, int flags,struct Ip_sockaddr *from, Ip_socklen_t *fromlen);
这个buf大小需要考虑啥,有啥讲究吗?
1) 先来看看着实现代码(代码源自ipnet):
IP_GLOBAL int
ipnet_sock_pkt_recv(Ipnet_socket *sock, struct Ip_msghdr *msg, int flags)
{
Ipcom_pkt *pkt;
int pkt_offset;
int buf_len;
Ip_u8 *buf;
int bytes;
Ip_size_t i;
int read_bytes;
// 获得socket recv队列里的第一个pkt
bytes = ipnet_krecvfrom(sock, flags, msg->msg_name, &msg->msg_namelen, &pkt);
if (bytes < 0)
return bytes;
if (msg->msg_control != IP_NULL)
ipnet_add_ancillary_data(msg, sock, pkt);
pkt_offset = pkt->start;
for (bytes = 0, i = 0; i < msg->msg_iovlen && pkt_offset < pkt->end; i++)
{
buf = (Ip_u8 *) msg->msg_iov[i].iov_base;
buf_len = msg->msg_iov[i].iov_len;
/* Copy over minimum of bytes requested and available, i.e. truncation is ok. */
// 比较buffer和pkt的大小,read_bytes值取较小的
read_bytes = IP_MIN(buf_len, (int) pkt->end - pkt_offset);
if (read_bytes > 0)
{
// 拷贝数据到用户缓存区buffer
ipcom_copy_to_user(buf, &pkt->data[pkt_offset], read_bytes);
bytes += read_bytes;
pkt_offset += read_bytes;
}
}
// 还有数据未被处理,设置 IP_MSG_TRUNC标志(Is set if the packet was truncated)
if (pkt->end > pkt_offset)
IP_BIT_SET(msg->msg_flags, IP_MSG_TRUNC);
if (IP_BIT_ISFALSE(flags, IP_MSG_PEEK))
/* Release packet. */
// 释放协议栈空间的buffer
ipcom_pkt_free(pkt, IP_FLAG_FC_STACKCONTEXT);
return bytes;
}
从上面的代码逻辑来看,协议栈
1)用户态的程序通过socket收包时,是一个个packet为单位处理的。
2)如果用户态的缓存区大于报文大小,拷贝有效的数据后,不会再收下一个报文。
3)如果用户态的缓存区小于报文大小,截断拷贝,设置TUNC标志。
4)如果没有设置IP_MSG_PEEK(Leave the data on the receive buffer )标志,释放该报文的缓存。
2) 怎么处理呢?
应用层协议,软件来负责处理
参考协议定义,客户端/服务端交互各个阶段的消息大小有各自定义,收包的buffer大于或者等于消息大小。
TLV(type+length+value),length表示的就是后面消息的长度。
协议提供协商,如sftp的OACK来协商数据块大小。