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來協商數據塊大小。