• Udp 并发问题分析与总结


    一、tcp并发与udp并发的区别
           无论是epoll还是select,在观察有无数据就绪时,都是针对多个文件描述符。如果只有一个文件描述符,那么进程只要观察那一个文件描述符即可。在网络编程中,一个Socket对应一个文件描述符。Tcp协议的server在监听端口前初始化一个socket,每有一个新的连接,就新建一个socket。因此当tcp服务器面对高并发请求时,实际上有多个socket,也就是有多个文件描述符。Udp协议的Server没有真正意义上的“连接”的概念,在监听端口和响应请求时都只有一个socket,也就只有一个文件描述符。
    二、udp并发的常规思路
           大部分udp服务器是顺序迭代的,服务器等待客户端请求,然后读取请求,处理请求,发回响应。但是,当处理客户端请求需要很长时间,就需要考虑某种形式的并发。一个“长处理”可以理解为处理请求的时间明显大于发送请求的时间。
            并发的常见思路是使用多线程。服务器在读取一个新请求之后,可以交由一个线程处理,该线程在处理之后直接将响应内容发给客户端。另一方面,udp服务器和多个客户端交互,但是却没有多个socket。典型的解决方案是,服务器为每个客户端创建一个新的socket,并绑定一个新的端口。客户端以后就通过这个新的socket与服务器通信,获得响应。总结来说,udp并发服务器,针对多个客户端,可以创建多个socket;针对多个请求,可以使用多线程(线程池)进行处理。
    三、编程模型
    1.多个socket
    1.  
      for ( ; ; )
    2.  
      {
    3.  
      /* 等待新的客户端连接 */
    4.  
      recvform( &from_addr)
    5.  
      /* 创建一个新的进程,由该进程去处理 (线程也可以)*/
    6.  
      if (fork() == 0)
    7.  
      break; //子进程跳出循环
    8.  
      }
    9.  
      //child now here
    10.  
      peer = socket(AF_INET, SOCK_DGRAM, 0);
    11.  
      //绑定一个随机端口
    12.  
      myaddr.sin_port = htons(0);
    13.  
      bind(peer,(struct sockaddr *)&myaddr, sizeof(myaddr));
    14.  
      /*
    15.  
      把这个套接字跟客户端的地址连接起来
    16.  
      这也就意味之后之后套接字使用 send recv这些函数时
    17.  
      都是直接跟指定的客户端进行通信的
    18.  
      */
    19.  
      connect(peer, (struct sockaddr *)&from, sizeof from)
    20.  
      /*处理请求*/

    2.使用epoll进行处理
    /*
    1、UDP srv创建UDP socket(listen_fd), 设置socket为REUSEASSR,REUSEPORT和非阻塞
       同时bind本地地址local_addr
    */
    listen_fd = socket(PF_INET, SOCK_DGRAM, 0);
    fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFD, 0)|O_NONBLOCK)
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
    bind(listen_fd, (struct sockaddr *)&local_addr, sizeof(struct sockaddr));
    /*
    2、创建epoll fd, 并将listen_fd添加到epoll中,兵监听其可读事件
    */
    epoll_fd = epoll_create(100);
    ep_event.events = EPOLLIN |EPOLLET;
    ep_event.data.fd = listen_fd;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ep_event);
    while (1) 
    {
        in_fds = epoll_wait(epoll_fd, in_events, 1000, 5000);
    /*
    3、epoll_wait返回时,如果返回的是listen_fd, 调用recvfrom接受client
    第一个UDP包,并根据recvfrom返回client地址,创建一个新的socket(new_fd)
    设置new_fd为REUSEADDR,REUSEPORT和非阻塞,同时bind本地地址local_addr
    然后connect上recvfrom返回的client地址
    */
    for (i = 0; i < in_fds; i++)
    {
    if(in_events[i].data.fd = listen_fd)
    {
    recvfrom(listen_fd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &client_len);
    new_fd = socket(PF_INET, SOCK_DGRAM, 0);
    fcntl(new_fd, F_SETFL, fcntl(new_fd, F_GETFD, 0)|O_NONBLOCK);
    setsockopt(new_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    setsockopt(new_fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
    bind(new_fd, (struct sockaddr *)&local_addr, sizeof(struct sockaddr));
    connect(new_fd, (struct sockaddr *)&client_addr, sizeof(struct sockaddr));
    /*
    4、将新创建的new_fd加入到epoll中并监听其可读事件
    */
    client_ev.events = EPOLLIN;
    client_ev.data.fd = new_fd;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_fd, &client_addr);
    }
    else if (in_events[i].events & EPOLLIN)
    {
    /*
    5、当epoll_wait返回时,如果返回的是new_fd, 那么调用recvfrom来接收特定client的UDP包
    */
    recvfrom(new_fd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&client_addr, &client_len);
    data->fd = new_fd;
    data-> ptr= process(recvbuf); /*data中包括socket信息*/
    ev.data.ptr = data;
    ev.events = EPOLLOUT | EPOLLET;
    epoll_ctl(epoll_fd,EPOLL_CTL_MOD,new_fd,&ev);
    }
    else if (in_events[i].events & EPOLLOUT)
    {
    sockfd = data->fd;
    send( sockfd, data->ptr, strlen((char*)data->ptr), 0 );
    ev.data.ptr = data;
    ev.events = EPOLLIN | EPOLLET;
    epoll_ctl(epoll_fd,EPOLL_CTL_MOD,sockfd,&ev);
    }
    else
    {
     
    }
    }
    }  
  • 相关阅读:
    Qt之界面数据存储与获取(使用setUserData()和userData())
    UML中关联(Association)、聚合(Aggregation)和合成(Composition)之间的区别
    Entity Framework Model First下改变数据库脚本的生成方式
    keepalive学习
    函数、极限、连续
    C#集合基础与运用
    面向查询服务的参数化查询
    WinDbg 命令手册
    知识管理方法论
    项目管理Project
  • 原文地址:https://www.cnblogs.com/lidabo/p/14314556.html
Copyright © 2020-2023  润新知