socket收包函数 buffer大小的设置

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来协商数据块大小。

### 关于Socket函数的信息 #### 接函数 `recv` 的使用方法和参数说明 在 C 语言中,用于从套接字读取数据的主要函数是 `recv()`。此函数可以从已连接的套接字上接数据。 头文件含如下: ```c #include <sys/types.h> #include <sys/socket.h> ``` 函数原型定义为: ```c ssize_t recv(int sockfd, void *buf, size_t len, int flags); ``` - **sockfd**: 套接字描述符,标识要从中接数据的套接字[^1]。 - **buf**: 数据缓冲区指针,指向用来存储接到的数据的内存区域[^1]。 - **len**: 缓冲区长度,表示可以存入多少字节的数据。 - **flags**: 控制选项标志位,通常设置为0;也可以通过组合特定常量来改变行为方式,比如MSG_PEEK(预览消息而不移除它)。 该函数返回实际接到的字节数,如果遇到错误则返回 -1 并设置 errno 变量以指示具体原因;当对方关闭连接时,则会返回 0 表明已经到达文件结束位置 (EOF) 。 #### 示例代码展示如何利用 `recv` 函数实现简单的服务器端接客户端发送过来的消息 下面是一个简单例子展示了怎样在一个TCP服务端程序里调用 `recv` 来获取来自客户端的信息: ```c // Server side code to receive message from client using recv() #define BUFFER_SIZE 1024 int main() { struct sockaddr_in address; int sock = 0, valread; struct sockaddr_in serv_addr; char buffer[BUFFER_SIZE] = {0}; // Create a socket file descriptor if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("\n Socket creation error \n"); return -1; } // Configure settings of the server address structure memset(&serv_addr, '0', sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(PORT); // Bind created socket with provided IP and port number if(bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr))<0){ perror("bind failed."); close(sock); exit(EXIT_FAILURE); } // Listen on this socket for incoming connections if(listen(sock , 3) < 0){ perror("listen"); close(sock); exit(EXIT_FAILURE); } // Accept an incoming connection request int new_socket; if((new_socket = accept(sock ,(struct sockaddr*)&address,(socklen_t*)&addrlen))<0){ perror("accept"); close(sock); exit(EXIT_FAILURE); } while(true){ bzero(buffer,BUFFER_SIZE); // Receive data into buffer until end-of-file or error occurs. valread = recv(new_socket , buffer, BUFFER_SIZE, 0); if(valread <= 0){break;} // Print received string printf("%s\n",buffer ); } // Close sockets after communication ends close(new_socket); close(sock); } ``` 上述代码片段实现了基本的服务端逻辑,其中含了创建套接口、绑定地址信息、监听请求以及接受新连接的过程,并且循环不断地尝试从新的连接中读取消息直到连接被终止为止[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值