2.3 UDP和Epoll结合 - UDP的Accept模型到此,为了充分利用多核CPU资源,进行UDP的多处理,我们会预先创建多个进程,每个进程都创建一个或多个绑定相同端口,相同IP地址(SO_REUSEADDR、SO_REUSEPORT)的UDP socket,这样利用内核的UDP socket查找算法来达到UDP的多进程负载均衡 。然而,这完全依赖于Linux内核处理UDP socket查找时的一个算法,我们不能保证其它的系统或者未来的Linux内核不会改变算法的行为;同时,算法的查找能否做到比较好的均匀分布到不同的UDP socket,(每个处理进程只处理自己初始化时候创建的那些UDP socket)负载是否均衡是个问题 。于是,我们多么想给UPD建立一个accept模型,按需分配UDP socket来处理 。
在高性能Server编程中,对于TCP Server而已有比较成熟的解决方案,TCP天然的连接性可以充分利用epoll等高性能event机制,采用多路复用、异步处理的方式,哪个worker进程空闲就去accept连接请求来处理,这样就可以达到比较高的并发,可以极限利用CPU资源 。然而对于UDP server而言,由于整个Svr就一个UDP socket,接收并响应所有的client请求,于是也就不存在什么多路复用的问题了 。UDP svr无法充分利用epoll的高性能event机制的主要原因是,UDP svr只有一个UDP socket来接收和响应所有client的请求 。然而如果能够为每个client都创建一个socket并虚拟一个“连接”与之对应,这样不就可以充分利用内核UDP层的socket查找结果和epoll的通知机制了么 。server端具体过程如下:
- UDP svr创建UDP socket fd,设置socket为REUSEADDR和REUSEPORT、同时bind本地地址local_addr listen_fd = socket(PF_INET, SOCK_DGRAM, 0) 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))
- 创建epoll fd,并将listen_fd放到epoll中 并监听其可读事件 epoll_fd = epoll_create(1000); ep_event.events = EPOLLIN|EPOLLET; ep_event.data.fd = listen_fd; epoll_ctl(epoll_fd , EPOLL_CTL_ADD, listen_fd, &ep_event) in_fds = epoll_wait(epoll_fd, in_events, 1000, -1);
- epoll_wait返回时,如果epoll_wait返回的事件fd是listen_fd,调用recvfrom接收client第一个UDP包并根据recvfrom返回的client地址, 创建一个新的socket(new_fd)与之对应,设置new_fd为REUSEADDR和REUSEPORT、同时bind本地地址local_addr,然后connect上recvfrom返回的client地址 recvfrom(listen_fd, buf, sizeof(buf), 0, (struct sockaddr )&client_addr, &client_len) new_fd = socket(PF_INET, SOCK_DGRAM, 0) setsockopt(new_fd , SOL_SOCKET, SO_REUSEADDR, &reuse,sizeof(reuse)) setsockopt(new_fd , SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse)) bind(new_fd , (struct sockaddr ) &local_addr, sizeof(struct sockaddr)); connect(new_fd , (struct sockaddr * ) &client_addr, sizeof(struct sockaddr)
- 将新创建的new_fd加入到epoll中并监听其可读等事件 client_ev.events = EPOLLIN; client_ev.data.fd = new_fd ; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_fd , &client_ev)
- 当epoll_wait返回时,如果epoll_wait返回的事件fd是new_fd 那么就可以调用recvfrom来接收特定client的UDP包了 recvfrom(new_fd , recvbuf, sizeof(recvbuf), 0, (struct sockaddr * )&client_addr, &client_len)
这里的UPD和Epoll结合方案,有以下几个注意点:
- [1] client要使用固定的ip和端口和server端通信,也就是client需要bind本地local address 。如果client没有bind本地local address,那么在发送UDP数据包的时候,可能是不同的Port了,这样如果server 端的new_fd connect的是client的Port_CA端口,那么当Client的Port_CB端口的UDP数据包来到server时,内核不会投递到new_fd,相反是投递到listen_fd 。由于需要bind和listen fd一样的IP地址和端口,因此SO_REUSEADDR和SO_REUSEPORT是必须的 。
- [2] 要小心处理上面步骤3中connect返回前,Client已经有多个UDP包到达Server端的情况 。如果server没处理好这个情况,在connect返回前,有2个UDP包到达server端了,这样server会new出两个new_fd1和new_fd2分别connect到client,那么后续的client的UDP到达server的时候,内核会投递UDP包给new_fd1和new_fd2中的一个
2.3 UDP Fork 模型 - UDP accept模型之按需建立UDP处理进程
推荐阅读
- 陕西|人在职场,有这四种迹象,恭喜你,说明领导要重用你了
- 你还知道哪些关于西施的故事 关于西施的传说视频
- 别说你会冲泡安化黑茶,魅力安化黑茶
- 听说你想入坑塔帐?看完这篇文章就够了
- 自动驾驶|自动驾驶出租车要来了 小马智行官宣:你敢坐吗?
- 青菜的做法大全 青菜的食疗功效你知道吗
- 失眠怎么办 4款食疗方为你打造舒心睡眠
- 中风吃什么好 9款食疗方辅助你治疗
- 1.《你当像鸟飞往你的山》?《你当像鸟飞往你的山》内容简介
- 慢性咽炎怎么治 7款食疗方滋润你的咽喉