大家都知道,udp是无状态的。特点是简单高效。没有tcp三次握手等。但是在做udp服务器的时候,处理来自多个连接的udp请求时,需要check每个udp包,然后根据内容分给不同的session处理,这样就比较复杂,性能也特别低。那么是否可以使用epoll来管理多个udp连接呢?答案是肯定的。下面介绍具体方法。
1. 服务器创建socket,bind 端口,加入epoll,等待连接。
设置reuseport
2. 客户端首次连接上来,创建socket,connect到客户端ip,端口。注意客户端要bind 端口
3. 新创建的socket,加入epoll管理。后续处理udp交互
服务器主流程
- struct epoll_event ev;
- struct epoll_event events[MAXEPOLLSIZE];
-
- /* 开启 socket */
- if ((listener = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
- {
- perror("socket create failed !");
- exit(1);
- }
- else
- {
- printf("socket create success \n");
- }
-
- /*设置socket属性,端口可以重用*/
- int opt=SO_REUSEADDR;
- setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
-
- /* 设置非阻塞属性 */
- setnonblocking(listener);
- /* bind */
- bzero(&my_addr, sizeof(my_addr));
- my_addr.sin_family = PF_INET;
- my_addr.sin_port = htons(myport);
- my_addr.sin_addr.s_addr = INADDR_ANY;
- if (bind(listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1)
- {
- perror("bind");
- exit(1);
- }
- else
- {
- printf("IP and port bind success, port:%d \n", myport);
- }
-
- /* 创建 epoll 句柄,把监听 socket 加入到 epoll 集合里 */
- kdpfd = epoll_create(MAXEPOLLSIZE);
- len = sizeof(struct sockaddr_in);
- ev.events = EPOLLIN | EPOLLET;
- ev.data.fd = listener;
- if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) < 0)
- {
- fprintf(stderr, "epoll set insertion error: fd = %d \n", listener);
- return -1;
- }
- else
- {
- printf("listen socket added in epoll success \n");
- }
-
- while (1)
- {
- /* 等待有事件发生 */
- nfds = epoll_wait(kdpfd, events, 10000, -1);
- if (nfds == -1)
- {
- perror("epoll_wait");
- break;
- }
-
- /* 处理所有事件 */
- for (n = 0; n < nfds; ++n)
- {
- if (events[n].data.fd == listener)
- {
- /* 创建socket,connect client。加入epoll */
- accept_client(kdpfd,listener);
- }
- else
- {
- /* 处理udp包 */
- msg_process(kdpfd,events[n].data.fd);
- }
- }
- }
连接客户端流程
- struct sockaddr_storage client_addr;
- socklen_t addr_size = sizeof(client_addr);
- char buf[1024];
- int ret = recvfrom(fd, buf,1024, 0, (struct sockaddr *)&client_addr,
- &addr_size);
-
- buf[ret] = '\0';
- char type=buf[0];
- char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
- ret = getnameinfo((struct sockaddr *)&client_addr, addr_size, hbuf,
- sizeof(hbuf), \
- sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
-
-
- printf("recvfrom client [%s:%s] : %d\n", hbuf, sbuf, buf[0]);
-
- /* connect 客户端,并且加入epoll */
- int new_sock = udp_socket_connect(epollfd, (struct sockaddr_in*)&client_addr);
-
- /* 发送消息给客户端*/
- do_write(epollfd, new_sock, buf);
udp服务器在好多媒体领域有应用。怎么高效处理udp报文是不得不考虑的问题