udp server的connect和listen

 之前,学习tcp的时候,学习了epoll,主要应用于大连接IO事件的管理。能否将epoll和udp结合,也能管理大量的socket描述符呢?当时,查了点资料,就写了一篇博客[1]。udp是面向无连接的,在服务端,每当一个客户端数据包到来时,数据包中携带的ip和port,使得来自不同客户端的数据包是可以区分的。但是epoll是是对大量fd的管理,在新的请求到来时,就可以创建一个新的fd,用来标志一个新的连接。这个过程,[4]中有代码描述,抄在这里。

1 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))
2 创建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);
3 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)
4 将新创建的new_fd加入到epoll中并监听其可读等事件
client_ev.events = EPOLLIN;
client_ev.data.fd = new_fd ;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_fd , &client_ev)
5 当epoll_wait返回时,如果epoll_wait返回的事件fd是new_fd 那么就可以调用recvfrom来接收特定client的UDP包了
recvfrom(new_fd , recvbuf, sizeof(recvbuf), 0, (struct sockaddr * )&client_addr, &client_len)

 这个就是epoll+udp+connect。这个想法也不是无源之水,我当时似乎看了kcp[5]的代码。时间有点久了,我已经忘了。插曲,之前加入一个udx[6]协议作者创建的群,帮主主要目的是兜售他的udx协议代码,据说优化了十多年。里面有哥们问这个udp+epoll的代码是怎么回事,我打开链接,觉得很眼熟,博客我写的。现在回想一下,我个人认为这样做并没有什么性能的提升。直接对fd进行轮询,有数据到来之后,根据协议子段或者ip:port的hash,一样可以实现epoll+udp+connect的能力,只是后者把hash查找之类的操作留给了操作系统中的处理逻辑。quic代码中server端的dispatcher使用std::map来管理session,对一个fd进行轮询,从而进行数据分发。
 另外一个就是udp+listen,这样可以多个线程监听同一个端口,充分利用cpu的处理能力。这个更有意义。例子参考quic proxy中的代码[8]。
[1]UDP下的epoll并发框架
[2]UDP的epoll并发框架-UDP Listener解决OpenVPN的并发问题
[3]使用socket so_reuseport提高服务端性能
[4]告知你不为人知的 UDP:连接性和负载均衡
[5]kcp协议
[6]UDX协议
[7]UDX与TCP BBR有什么不同
[8]stellite quic

TCP (Transmission Control Protocol) 和 UDP (User Datagram Protocol) 都是传输层协议,在C语言中通常通过操作系统提供的库函数进行实现,例如在Linux下可以使用socket API。 **UDP 实现**: UDP是一个无连接的、不可靠的协议。在C语言中,创建一个UDP套接字通常是这样的: ```c #include <sys/socket.h> #include <netinet/in.h> int main() { struct sockaddr_in server_address; // 服务器地址结构 int sock = socket(AF_INET, SOCK_DGRAM, 0); // 创建UDP套接字 // 绑定到特定的端口 if (bind(sock, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) { // 错误处理 } // 发送数据 sendto(sock, data, strlen(data), 0, (struct sockaddr*)&client_address, sizeof(client_address)); // 接收数据 char buffer[1024]; recvfrom(sock, buffer, sizeof(buffer), 0, NULL, NULL); return 0; } ``` **TCP 实现**: TCP是有连接的、可靠的协议。创建一个TCP套接字和连接的过程更复杂: ```c #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> int main() { struct sockaddr_in server_address; int client_sock, server_sock; int connect_status; // 创建并绑定服务器套接字 server_sock = socket(AF_INET, SOCK_STREAM, 0); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = htonl(INADDR_ANY); // 使用任意地址 server_address.sin_port = htons(port_number); bind(server_sock, (struct sockaddr*)&server_address, sizeof(server_address)); // 开始监听 listen(server_sock, 5); // 建立客户端连接 client_sock = accept(server_sock, NULL, NULL); connect_status = connect(client_sock, (struct sockaddr*)&client_address, sizeof(client_address)); // 对于TCP,发送和接收数据通常涉及到更多的错误检查和确认机制 // ... close(client_sock); close(server_sock); return 0; } ``` 以上代码只是一个简化的示例,并未涵盖所有错误处理和同步细节。实际使用中需要根据需求添加适当的错误检查。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值