• udp接收队列以及多次初始化的测试


    这一段时间基于udp的数据重放的过程中图像总是卡死,发现recv Q满了,但是不知道具体原因是啥,这里就先进行了udp的接收测试。

    一. 正常测试 

    1. send端每次发送1k,发送频率50hz;

    2. recv端每次接受1k,接受频率5hz;
    3. watch -n 1 netstat -anu 显示recv进程对应8888端口的recv Q在207000到162000之间波动,不会卡死。recv Q 的上限是207000左右。通过分析结果可以发现,当recv q满了之后,recv从队列里面拿出1k后,就会从接收发送端的1k进入队列,而是recv持续从队列里面拿数据,直到recv Q 降到162000左右,也就是队列占用率达到80%之后,才会接受send端发过来的数据。

    二. 当recv端cnt==800时,执行socket重新初始化

     if(cnt == 800)

           {

              serfd=socket(AF_INET,SOCK_DGRAM,0);

               printf("serfd = %d\n",serfd);

              // ret=bind(serfd,(struct sockaddr *)&seraddr,sizeof(seraddr));

           }

    1. socket初始化

      无论是否执行bind操作,接收端都会卡住,接收队列都会满。同时/proc/pid/fd下面会新增一个socket文件,对应的fd为4。如果执行bind,则会返回绑定失败,端口被占用。

    原因:经过上述的if语句后,serfd已经变成了4,对应的是一个新的socket文件,也就是说recv端会有一个新的接收队列,只是没有数据发送到这个新的队列。send端发送的数据依旧发送到serfd=3对应的队列中,但该队列没有人去读取了,造成recv Q 满了。表象就是卡住了,实际上是因为serfd=4对应的队列中没有数据。

        2. socket初始化,同时将recvfrom函数中用到的serfd换成3这个常量

    除了上述修改外,如果把recvfrom的serfd换成常量3,则虽然新生成4对应的fd,但是数据依然从3对应的队列中获取,也不会卡住,只是4对应队列中没有数据。

    三. 当recv端cnt==800时,执行socket重新初始化

     if(cnt == 800)

     {

    if(serfd>0) ret = close(serfd);

           printf("close serfd=%d\n",ret);

              serfd=socket(AF_INET,SOCK_DGRAM,0);

              sleep(1);

               printf("serfd = %d\n",serfd);

              ret=bind(serfd,(struct sockaddr *)&seraddr,sizeof(seraddr));

           }

    原因:

    在cnt 为800时,原来的fd会关闭,/proc/pid/fd/下面的文件3也会被删掉;之后,重新执行 serfd=socket(AF_INET,SOCK_DGRAM,0);后,会申请可用的fd,而这个时候因为3号fd已经释放了,所以重新申请到的fd还是3。只不过此时对应的inode号发生了变更,从2130436变成了2132215。而8888端口对应的recv Q中的100多k的数据直接清0,然后开始新的积累数据。

    测试程序

    1. send端:
    1. #include <stdio.h>
    2. #include <sys/socket.h>
    3. #include <sys/types.h>
    4. #include <string.h>
    5. #include <unistd.h>
    6. #include <netinet/in.h>
    7. #include <arpa/inet.h>
    8. #include <iostream>
    9. //创建UDP实现服务器和客户端的通信
    10. //创建socket连接
    11. int main()
    12. {
    13. //创建socket连接
    14. int clifd = 0;
    15. int cnt = 0;
    16. clifd = socket(AF_INET, SOCK_DGRAM, 0);
    17. if (clifd < 0)
    18. {
    19. perror("socke failed");
    20. return -1;
    21. }
    22. printf("socket success\n");
    23. //向服务器发送消息
    24. int tolen = 0;
    25. int ret = 0;
    26. char buf[1024] = {0};
    27. while (1)
    28. {
    29. // std::cin.getline(buf,1023);
    30. memset(buf, 0, 1024);
    31. cnt++;
    32. memcpy(buf, &cnt, sizeof(int));
    33. struct sockaddr_in seraddr = {0};
    34. seraddr.sin_family = AF_INET;
    35. seraddr.sin_addr.s_addr = inet_addr("192.168.61.6");
    36. seraddr.sin_port = htons(8888);
    37. tolen = sizeof(seraddr);
    38. ret = sendto(clifd, buf, strlen(buf), 0, (struct sockaddr *)&seraddr, tolen);
    39. if (ret < 0)
    40. {
    41. perror("sendto failed");
    42. close(clifd);
    43. return -1;
    44. }
    45. printf("sendto success, %d\n", cnt);
    46. //接收发送自服务器的消息
    47. // ret=recvfrom(clifd,buf,sizeof(buf),0,NULL,NULL);
    48. usleep(20000);
    49. // if(ret<0)
    50. // {
    51. // perror("recvfrom failed");
    52. // close(clifd);
    53. // return -1;
    54. // }
    55. // printf("recvfrom success\n");
    56. // printf("receive: %s\n",buf);
    57. }
    58. close(clifd);
    59. return 0;
    60. }

    1. recv端
    1. #include <stdio.h>
    2. #include <sys/socket.h>
    3. #include <sys/types.h>
    4. #include <string.h>
    5. #include <unistd.h>
    6. #include <netinet/in.h>
    7. #include <arpa/inet.h>
    8. #include <iostream>
    9. //创建UDP实现服务器和客户端的通信
    10. int main()
    11. {
    12. //创建socket连接
    13. int serfd = 0;
    14. int cnt = 0;
    15. serfd = socket(AF_INET, SOCK_DGRAM, 0);
    16. printf("serfd = %d\n", serfd);
    17. if (serfd < 0)
    18. {
    19. perror("socke failed");
    20. return -1;
    21. }
    22. printf("socket success\n");
    23. //绑定IP地址和端口信息
    24. int ret = 0;
    25. struct sockaddr_in seraddr = {0};
    26. seraddr.sin_family = AF_INET;
    27. seraddr.sin_addr.s_addr = inet_addr("192.168.61.6");
    28. seraddr.sin_port = htons(8888);
    29. ret = bind(serfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    30. if (ret < 0)
    31. {
    32. perror("bind failed");
    33. close(serfd);
    34. return -1;
    35. }
    36. printf("bind success\n");
    37. //接收发送自客户端的消息
    38. unsigned int addrlen = 0;
    39. char buf[1024] = {0};
    40. struct sockaddr_in clientaddr = {0};
    41. addrlen = sizeof(clientaddr);
    42. while (1)
    43. {
    44. memset(buf, 0, 1024);
    45. ret = recvfrom(serfd, buf, sizeof(buf), 0, (struct sockaddr *)&clientaddr, &addrlen);
    46. if (ret < 0)
    47. {
    48. perror("recvfrom failed");
    49. close(serfd);
    50. return -1;
    51. }
    52. memcpy(&cnt, buf, sizeof(int));
    53. printf("IP=%s,port=%u\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
    54. printf("recvfrom success\n");
    55. printf("receive: %d\n", cnt);
    56. if (cnt == 800)
    57. {
    58. if (serfd > 0)
    59. ret = close(serfd);
    60. printf("close serfd=%d\n", ret);
    61. sleep(5);
    62. serfd = socket(AF_INET, SOCK_DGRAM, 0);
    63. sleep(1);
    64. printf("serfd = %d\n", serfd);
    65. ret = bind(serfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    66. }
    67. //向客户端发送消息
    68. memset(buf, 0, sizeof(buf));
    69. //gets(buf);
    70. // std::cin.getline(buf,1023);
    71. //cnt++;
    72. //memcpy(buf,&cnt,sizeof(int));
    73. // ret=sendto(serfd,buf,strlen(buf),0,(struct sockaddr *)&clientaddr,addrlen);
    74. // if(ret<0)
    75. // {
    76. // printf("%ret = %d\n",ret);
    77. // perror("sendto failed");
    78. // close(serfd);
    79. // return -1;
    80. // }
    81. // printf("sendto success\n");
    82. usleep(200000);
    83. }
    84. close(serfd);
    85. return 0;
    86. }

    编译:

    g++ recv.cpp -o recv

    g++ send.cpp -o send

    五. 运行顺序

    1. 准备工作

    启动6个命令窗口,进入到上述文件目录下;

    2. 先在terminal#1运行recv程序,可以将之重定向到其它文件;

    ./recv > r3.txt

    3. 在terminal#2运行send程序

    ./send

    4. 在terminal#3查看rtxt的最后10行,每秒更新一次,主要用于确认cnt=800大概在什么时候;

    watch -n 1 tail -n10 r3.txt

     5. 在terminal#4查看recv对应的pid,然后进入到/proc/pid/fd查看socket文件,并每秒更新一次,查看socket文件是否变化;

    6. 查看udp丢包情况以及socket对应的文件的inode是否变化;

    watch -n 1 "cat /proc/net/udp |grep 22B8" 其中22B8是端口8888的十六进制表示。

    目前看丢包6121个;2705799就是目前8888端口对应socket文件的inode号。和/proc/pid/fd下面的文件一致;

     7. 查看udp的接收队列情况

    watch -n 1 netstat -anu

    可以看到目前recv Q保持高位运行。

  • 相关阅读:
    瀑布流布局2
    Golang标准库 container/list(双向链表) 的图文解说
    TSINGSEE智能分析网关简介及说明
    Java要抛弃祖宗的基业,Java程序员危险了!
    在腾讯干软件测试4年,来面试要求35k,让我见识到了真正的测试届天花板...
    LeetCode 热题 HOT 100 第七十四天 337. 打家劫舍 III 中等题 用python3求解
    linux 文件锁
    CSS 6 CSS 盒子模型
    自然语言处理实战项目18-NLP模型训练中的Logits与损失函数的计算应用项目
    vue轮播图使用swiper插件
  • 原文地址:https://blog.csdn.net/jinking01/article/details/125560279