sendto是udp发送报文的御用接口。他的函数原型如下:
cpp
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
在使用kvs的数据通道时发现sendto调用太频繁会导致丢包,而且返回值还与发送长度相等,没有一点报错的迹象。(给亚马逊点个点赞,他们对sendto的使用堪称教科书。先sendto而后poll)
于是自己写了一个sendto的测试用例,如下:
cpp
1 /*************************************************************************
2 *File Name: udp_client.h
3 *Author: Augus
4 *email: augus@lockin.com
5 *Created Time: 2025年01月25日 星期六 22时07分31秒
6 *Describtion:
7 ************************************************************************/
8
9 #ifndef __UDP_CLIENT_H__
10 #define __UDP_CLIENT_H__
11
12 #include <unistd.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <sys/types.h> /* See NOTES */
16 #include <sys/socket.h>
17 #include <arpa/inet.h>
18 #include <fcntl.h>
19 #include <netinet/in.h>
20 #include <netinet/tcp.h>
21 #include <string.h>
22 #include <errno.h>
23
24 #define DBG(fmt, arg...) printf("{%s:%d} " fmt "\n", __func__, __LINE__, ##arg)
25
26 #endif
cpp
1 /*************************************************************************
2 *File Name: udp_client.c
3 *Author: Augus
4 *email: augus@lockin.com
5 *Created Time: 2025年01月25日 星期六 21时52分18秒
6 *Describtion:
7 ************************************************************************/
8
9 #include "udp_client.h"
10
11
12 int main(){
13 int32_t sockfd;
14 int32_t ret;
15 int32_t flags;
16 int32_t count;
17 struct sockaddr* destAddr = NULL;
18 struct sockaddr_in ipv4Addr;
19 int32_t addrLen;
20 int32_t optionValue=0;
21 int32_t bufsize=1048;
22 char *buffer=malloc(bufsize);
23 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
24
25 flags = fcntl(sockfd, F_GETFL, 0);
26 ret=fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
27
28 optionValue = 1;
29 ret=setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &optionValue, sizeof(optionValue));
30
31 addrLen = sizeof(ipv4Addr);
32 memset(&ipv4Addr, 0x00, sizeof(ipv4Addr));
33 ipv4Addr.sin_family = AF_INET;
34 ipv4Addr.sin_port = htons(1234);
35 inet_pton(AF_INET,"192.168.31.207",&ipv4Addr.sin_addr);
36 destAddr = (struct sockaddr*) &ipv4Addr;
37 count=1000000;
38 while(count--){
39 ret = sendto(sockfd,buffer,bufsize,MSG_NOSIGNAL, destAddr, addrLen);
40 if(ret!=bufsize){
41 DBG("ret=%d,errno=%s",ret,strerror(errno));
42 }
43 // //!< 不调用usleep时发送1m包丢失689505包,包间隔30us,----------------出现EGAIN,----iftop统计发送速率300sMbps
44 //usleep(0); //!< 延时0us发送1m包丢失1817包,---------包间隔不稳定:几十us到几百us,没有EAGAIN---iftop统计发送速率16.5Mbps
45 //usleep(1); //!< 延时1us发送1m包丢失1214包,---------包间隔300us,---------------没有EAGAIN,-----iftop统计发送速率16.2Mbps
46 //usleep(10); //!< 延时10us发送1m包丢失831包,---------包间隔400us,---------------没有EAGAIN,-----iftop统计发送速率16.0Mbps
47 //usleep(100); //!< 延时100us发送1m包就会丢795包,------包间隔500us,---------------没有EAGAIN,iftop统计发送速率15Mbps
48 //usleep(1000);//!< 延时1000us发送1w包就没有丢包了,----包间隔1.45ms,--------------没有EAGAIN,iftop统计发送速率5.6Mbps
49 }
50 close(sockfd);
51 return 0;
52 }
测试结果如下:
//!< 不调用usleep时发送1m包丢失689505包,包间隔30us,----------------出现EGAIN报错
//!< 延时0us发送1m包丢失1817包,---------包间隔不稳:几十到几百us,--没有EAGAIN
//!< 延时1us发送1m包丢失1214包,---------包间隔300us,-------------------没有EAGAIN
//!< 延时10us发送1m包丢失831包,---------包间隔400us,-------------------没有EAGAIN
//!< 延时100us发送1m包就会丢795包,------包间隔500us,-----------------没有EAGAIN
//!< 延时1000us发送1w包就没有丢包了,----包间隔1.45ms,---------------没有EAGAIN
通过上述测试分析,足以说明sendto丢包是确实存在的,而且丢包的地方有两个层级:
- ip协议栈udp缓存溢出,sendto会返回-1。
- 网卡驱动txqueue溢出,sendto返回正常。