• udp epoll服务器的设计


    前言

           大家都知道,udp是无状态的。特点是简单高效。没有tcp三次握手等。但是在做udp服务器的时候,处理来自多个连接的udp请求时,需要check每个udp包,然后根据内容分给不同的session处理,这样就比较复杂,性能也特别低。那么是否可以使用epoll来管理多个udp连接呢?答案是肯定的。下面介绍具体方法。

    具体实现

            1. 服务器创建socket,bind 端口,加入epoll,等待连接。

                设置reuseport

            2. 客户端首次连接上来,创建socket,connect到客户端ip,端口。注意客户端要bind 端口

            3. 新创建的socket,加入epoll管理。后续处理udp交互

    伪代码流程

       服务器主流程

    1. struct epoll_event ev;
    2. struct epoll_event events[MAXEPOLLSIZE];
    3. /* 开启 socket */
    4. if ((listener = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
    5. {
    6. perror("socket create failed !");
    7. exit(1);
    8. }
    9. else
    10. {
    11. printf("socket create success \n");
    12. }
    13. /*设置socket属性,端口可以重用*/
    14. int opt=SO_REUSEADDR;
    15. setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    16. /* 设置非阻塞属性 */
    17. setnonblocking(listener);
    18. /* bind */
    19. bzero(&my_addr, sizeof(my_addr));
    20. my_addr.sin_family = PF_INET;
    21. my_addr.sin_port = htons(myport);
    22. my_addr.sin_addr.s_addr = INADDR_ANY;
    23. if (bind(listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1)
    24. {
    25. perror("bind");
    26. exit(1);
    27. }
    28. else
    29. {
    30. printf("IP and port bind success, port:%d \n", myport);
    31. }
    32. /* 创建 epoll 句柄,把监听 socket 加入到 epoll 集合里 */
    33. kdpfd = epoll_create(MAXEPOLLSIZE);
    34. len = sizeof(struct sockaddr_in);
    35. ev.events = EPOLLIN | EPOLLET;
    36. ev.data.fd = listener;
    37. if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) < 0)
    38. {
    39. fprintf(stderr, "epoll set insertion error: fd = %d \n", listener);
    40. return -1;
    41. }
    42. else
    43. {
    44. printf("listen socket added in epoll success \n");
    45. }
    46. while (1)
    47. {
    48. /* 等待有事件发生 */
    49. nfds = epoll_wait(kdpfd, events, 10000, -1);
    50. if (nfds == -1)
    51. {
    52. perror("epoll_wait");
    53. break;
    54. }
    55. /* 处理所有事件 */
    56. for (n = 0; n < nfds; ++n)
    57. {
    58. if (events[n].data.fd == listener)
    59. {
    60. /* 创建socket,connect client。加入epoll */
    61. accept_client(kdpfd,listener);
    62. }
    63. else
    64. {
    65. /* 处理udp包 */
    66. msg_process(kdpfd,events[n].data.fd);
    67. }
    68. }
    69. }

    连接客户端流程

    1. struct sockaddr_storage client_addr;
    2. socklen_t addr_size = sizeof(client_addr);
    3. char buf[1024];
    4. int ret = recvfrom(fd, buf,1024, 0, (struct sockaddr *)&client_addr,
    5. &addr_size);
    6. buf[ret] = '\0';
    7. char type=buf[0];
    8. char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
    9. ret = getnameinfo((struct sockaddr *)&client_addr, addr_size, hbuf,
    10. sizeof(hbuf), \
    11. sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
    12. printf("recvfrom client [%s:%s] : %d\n", hbuf, sbuf, buf[0]);
    13. /* connect 客户端,并且加入epoll */
    14. int new_sock = udp_socket_connect(epollfd, (struct sockaddr_in*)&client_addr);
    15. /* 发送消息给客户端*/
    16. do_write(epollfd, new_sock, buf);

     总结

            udp服务器在好多媒体领域有应用。怎么高效处理udp报文是不得不考虑的问题

  • 相关阅读:
    Ubtunu排查磁盘空间是否已满—并清理的方式
    Matlab:控制流
    如何走出长期精神内耗的状态?
    浅谈etcd服务注册与发现
    安装importlib_metadata模块后,依旧显示No module named ‘importlib_metadata’
    阿里云安全恶意程序检测(速通二)
    代码随想录算法训练营第十四天 |二叉树
    [JS Framework] 当前运行的基座不包含原生插件[XXX],请在manifest中配置该插件,重新制作包括该原生插件的自定义运行基座
    说前端页面性能监控
    jsonlite库
  • 原文地址:https://blog.csdn.net/occupy8/article/details/127954395