首页 / 资讯中心 / 文章详情

Socket ‘send()‘ :返回值 100 代表什么

Socket ‘send()‘ :返回值 100 代表什么 ★ FEATURED ARTICLE
结论send()返回 100只表示本机内核 TCP 发送缓冲区收下了 100 字节不表示对端已经收到更不表示对端应用程序已经读到。1.send()在干什么典型调用ssize_tnsend(sockfd,buf,len,0);对TCP来说send()做的事是用户进程 buf --拷贝-- 本机内核 socket 发送缓冲区send buffer --返回-- n 成功拷进去的字节数它不会等到网卡把数据发出去对端 TCP 回 ACK对端recv()把数据读走所以它是交给本机协议栈算成功不是对端交付成功。UDP 的send()/sendto()语义更弱多数情况下只表示交给本机协议栈去发连进了可靠发送队列都谈不上。下面默认讲TCP。2. 返回值怎么读返回值含义你该做什么n 0成功写入内核缓冲区n 字节若n len剩下的要继续发n 0TCP 上极少见不要当成功按错误/异常处理n -1调用失败看errno例子返回 100假设charbuf[200];ssize_tnsend(fd,buf,200,0);// n 100含义内核收下了buf[0..99]buf[100..199]还没进发送缓冲区需要再send(fd, buf100, 100, 0)直到发完或出错这叫partial send部分发送。TCP 是字节流一次send不保证把你给的len全部吃完。阻塞套接字上空间不够时往往会卡住等到能再塞一些所以返回值小于 len相对少但标准不保证一定一次发完规范写法仍是循环发送。非阻塞套接字上缓冲区不够就立刻返回已写入的字节或EAGAIN非常常见只发了一部分。3. 数据实际走了哪几层① send() 返回 100 只到本机内核 send buffer ② 内核按 MSS 切片从网卡发出 可能还在本机排队对端完全不知道 ③ 对端内核收到 TCP 段回 ACK 到了对端协议栈对端应用未必读 ④ 对端 recv() 返回 0 到了对端应用程序 ⑤ 对端业务回 “OK / 处理完成” 到了业务确认阶段send()100能证明吗本机入队能已经上电线不能对端 TCP 收到并 ACK不能对端应用读到不能对端业务处理完不能TCP 的可靠是在连接还活着的前提下协议栈会重传、排序、去重尽量把字节流交给对端内核。这套机制在send()返回之后才慢慢发生返回值本身不是这套机制的完成通知。4. 为什么会返回 100 而不是 200常见原因发送缓冲区不够塞下全部数据对端接收窗口小、自己发得太快、网络拥塞本机SO_SNDBUF满了。非阻塞 socket能塞多少塞多少立刻返回。被信号打断较少见可能只完成了一部分。协议/实现限制一次系统调用拷贝量有上限但通常仍应循环发。5. 正确发送循环直到发完模板size_tsent0;while(sentlen){ssize_tnsend(fd,bufsent,len-sent,0);if(n0){if(errnoEINTR)continue;// 被信号打断重试if(errnoEAGAIN||errnoEWOULDBLOCK){// 非阻塞缓冲区满等可写再发select/poll/epollbreak;}// EPIPE / ECONNRESET对端关掉或复位perror(send);break;}sent(size_t)n;}// sent len 才算“本机全部入队完成”入队完成 ≠ 对端收到。这只是你这边调用层的完成。6. 失败时errno常见含义errno含义EAGAIN/EWOULDBLOCK非阻塞且暂时发不出去缓冲满EINTR系统调用被信号打断EPIPE对端已关闭写方向再发会 SIGPIPE可忽略该信号ECONNRESET对端 RST连接被重置ENOTCONN还没 connect 成功ECONNREFUSED等多出现在 connect不是 send 主场景注意对端已经挂了之前塞进缓冲区的数据仍可能让 send 连续成功好几次直到探测到死亡keepalive、写时发现 RST、超时。7. 怎样才算对端收到1传输层收到TCP ACK内核收到对端 ACK表示对端协议栈收下了这些字节。应用默认看不到这次 ACK。send()不等这个。有TCP_INFO、SIOCOUTQ看发送队列残留那是排查手段不是业务确认。2应用层读到对端recv()对端程序调用recv()/read()得到数据。如果对端进程卡死、没读数据堆在对端接收缓冲区你这边窗口会逐渐为 0后面send会阻塞或返回部分/EAGAIN。3业务确认应用协议回包例如请求带序号应答带同样序号固定回OK\nRPC 的 response工业现场Modbus-TCP、示教器、自定义帧都是自己定义帧头长度收到完整帧后再回确认。不能靠send返回值。8. 和粘包、拆包的关系因为 TCP 是字节流send两次对端可能recv一次粘包send一次 200 字节对端可能先recv80 再recv120拆包send返回 100只是你这边分两次入队对端看到的仍是连续字节没有“两次消息”的边界所以应用必须自己定边界先发 4 字节长度再发 payload或用分隔符接收端循环recv拼到一帧完整再解析send返回 100 和粘包不是一回事返回 100 是发送端部分入队粘包是接收端一次读到了多帧。9. 阻塞 vs 非阻塞和返回值的关系阻塞 socket缓冲区有空位尽量多写常一次写完但仍应循环缓冲区满阻塞等待直到能写入或出错返回 100当时只写入了 100若len200阻塞模式下很多实现会继续等到能写但可移植代码不要假设一次写完非阻塞 socket能写多少写多少立刻返回一个字节都写不了-1EAGAIN返回 100 非常正常配合epollEPOLLOUT再继续send剩余数据。10.send()vswrite()vssendmsg()对已连接 TCPwrite(fd, buf, len)和send(fd, buf, len, 0)类似都是写入发送缓冲区send多一个flags如MSG_NOSIGNAL、MSG_DONTWAIT返回值含义一样成功拷进内核的字节数11. 对照表你看到的现象实际含义send返回 100本机缓冲区多了 100 字节待发send返回 200 且 len200本机全部入队对端仍可能没收到对端抓包看到这段 TCP已经在网络上仍可能丢靠重传本机看到 ACK对端内核收到对端recv返回 100对端应用读到 100 字节对端回业务 ACK对端逻辑处理了
阅读完成 · 觉得有帮助?
咨询建站