Udp 并发问题分析与总结

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

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
{
}
}
}
原创粉丝点击