前言
大家都知道,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报文是不得不考虑的问题