UDP服务器的并发方案

简介: UDP服务器的并发方案

概述:本文介绍udp的并发思路及代码实现

使用tcp协议可以使用listen + bind + accept为每一个客户端建立一个连接,实现并发

udp是无连接的,如何响应多个客户端的请求实现并发呢?

最简单的办法就是模拟tcp,为每一个客户端创建一个套接字进行通信

步骤如下:

1.服务端创建本端listener套接字(并没有进行listen),用于接收所有客户端的请求

2.将listener加入epoll管理(ET模式),当listener就绪时,说明接收到一个客户端请求

3.进行recvfrom,成功获取到该客户端套接字的地址

4.然后创建一个新的服务端套接字,用于与该客户端套接字进行通信,

5.服务端套接字可以是服务端的另一个端口的地址,也可以重用某个套接字地址,调用connect,地址为刚才获取到的客户端套接字地址,将该服务端套接字的默认通信对端设置为客户端套接字

6.新的服务端套接字已经成功与该发起请求的客户端套接字进行了“绑定

7.循环调用epoll_wait,为每一个客户端创建一个套接字实现udp并发

#define SO_REUSEPORT    15
#define MAXBUF 10240
#define MAXEPOLLSIZE 100
int flag = 0;
int count = 0;
int read_data(int sd) {
    char recvbuf[MAXBUF + 1];
    int ret;
    struct sockaddr_in client_addr;
    socklen_t cli_len = sizeof(client_addr);
    bzero(recvbuf, MAXBUF + 1);
    ret = recvfrom(sd, recvbuf, MAXBUF, 0 (struct sockaddr *)&client_addr, &cli_len);
    if (ret < 0) {
        printf("read[%d]: %s  from %d\n", ret, recvbuf, sd);
    } else {
        printf("read err:%s  %d\n", strerror(errno), ret);
    }
//  fflush(stdout);
}
// 接收客户端套接字的消息,并返回一个与该客户端套接字通信的服务端套接字
int udp_accept(int sd, struct sockaddr_in my_addr) {  // 第一个参数是服务端套接字listener, 
                                    // 第二个参数是listener的地址 ,这里服务端只用了一个端口,实际可以为每一个客户端分配一个端口
    int new_sd = -1;
    int ret = 0;
    int reuse = -1;
    char buf[16];
    struct sockaddr_in peer_addr;  // 客户端套接字的地址
    socklen_t cli_len = sizeof(peer_addr);
    ret = recvfrom(sd, buf, 16, 0, (struct sockaddr *)&peer_addr, &cli_len); // 接受客户端数据,并保存客户端套接字地址
    if (ret < 0) {
        return -1;
    }
//  printf("ret: %d, buf: %s\n", ret, buf);
    if ((new_sd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { // 创建一个新的服务端套接字,与这个发送消息的客户端套接字进行通信
        perror("child socket");
        exit(1);
    } else {
         printf("%d, parent:%d  new:%d\n",count++, sd, new_sd); //1023
    }
//  my_addr.sin_port += count;
    ret = bind(new_sd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)); // 新套接字绑定服务端地址
    if (ret){
        perror("chid bind");
        exit(1);
    } 
    peer_addr.sin_family = PF_INET;
//  printf("aaa:%s\n", inet_ntoa(peer_addr.sin_addr));
    if (connect(new_sd, (struct sockaddr *) &peer_addr, sizeof(struct sockaddr)) == -1) { // 进行一次connnect,确定通信双方的套接字地址
        perror("chid connect");
        exit(1);
    } 
    // 进行connect后,新创建的服务端套接字才能正确与该客户端套接字通信
    return new_sd;
}
int main(int argc, char *argv[]) {
    int listener, kdpfd, nfds, n, curfds;
    socklen_t len;
    struct sockaddr_in my_addr, their_addr;
    unsigned int port;
    struct epoll_event ev;
    struct epoll_event events[MAXEPOLLSIZE];
    int opt = 1;;
    int ret = 0;
    port = 1234; // 服务端端套接字地址的共用端口,也可以使用多个端口
    if ((listener = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
    perror("socket");
    exit(1);
    } else {
        printf("socket OK\n");
    }   
    ret = setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // SO_REUSEADDR : 允许重用处于 TIME_WAIT 状态的套接字地址
    if (ret) {
        exit(1);
    }
    ret = setsockopt(listener, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)); // SO_REUSEPORT : 允许重用处于 TIME_WAIT 状态的PORT
    if (ret) {
        exit(1);
    }
    int flags = fcntl(listener, F_GETFL, 0);
    flags |= O_NOBLOCK;
    fcntl(listener, F_SETFL, flags); // 设置非阻塞
    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = PF_INET;
    my_addr.sin_port = htons(port);
    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 bind OK\n");
    }
    kdpfd = epoll_create(MAXEPOLLSIZE);
    ev.events = EPOLLIN | EPOLLET; // 边沿触发,   收到新的客户端消息再触发
    ev.data.fd = listener;
    if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) < 0) { // 将服务端套接字加入epoll
        fprintf(stderr, "epoll set insertion error: fd = %d\n", listener);
        return -1;
    } else {
        printf("ep add OK\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) { // udp服务端listener套接字就绪
                int new_sd;
                struct epoll_event child_ev;
                while(1) {
                    new_sd = udp_accept(listener, my_addr);
                    if (new_sd = -1) break;
                    child_ev.events = EPOLLIN;
                    child_ev.data.fd = new_sd;
                    if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, new_sd, &child_ev) < 0) {
                        fprintf(stderr, "epoll set insertion error: fd=%dn", new_sd);
                      return -1;
                    }
                }
            } else { // 服务端新创建的与每一个客户端进行通信的套接字就绪
                read_data(events[n].data.fd);
            }
        }
    }
    close(listener); 
    return 0;
}

推荐学习 https://xxetb.xetslk.com/s/p5Ibb

目录
相关文章
|
1月前
|
网络协议
端口最多只有65535个,为什么服务器能承受百万并发
服务器通过四元组(源IP、源端口、目标IP、目标端口)识别不同TCP连接,每条连接对应独立socket。数据包携带四元组信息,服务端据此查找对应socket进行通信。只要四元组任一元素不同,即视为新连接,可创建独立socket。资源充足时,单进程可支持百万级并发连接,socket与端口非一一对应。
124 10
端口最多只有65535个,为什么服务器能承受百万并发
|
29天前
|
关系型数据库 Linux PHP
开源站群服务器方案:构建高效流量矩阵的全攻略
正在寻找高性价比、可控性强且功能强大的站群解决方案?小编将深度解析开源站群服务器方案,从核心优势、主流工具选型到部署实践,助您构建稳定、高效的站群流量体系。
|
2月前
|
存储 固态存储 Linux
从 0 学服务器虚拟化:VMware 搭建 3 个虚拟主机,个人 / 小企业够用的方案
服务器虚拟化技术通过在单台物理机上运行多个虚拟机,显著提升资源利用率和管理灵活性。本文以 VMware ESXi 8.0 Update 3e 为例,详解如何搭建经济实用的虚拟化环境,支持 3 个虚拟主机稳定运行,适合个人开发者和小企业降低硬件投入、实现数据本地化与安全存储。
587 0
|
3月前
|
运维 前端开发 JavaScript
半夜服务器告警不再错过!运维人员必备的语音通知方案
为解决深夜服务器宕机错过告警的问题,本文介绍一款专为个人开发者与运维人员设计的语音通知方案。通过电话直接推送重要告警,确保第一时间响应,避免故障扩大。支持多种编程语言调用,配置简单,3步即可完成,实时性强,适合各类关键业务场景。
324 5
|
2月前
|
弹性计算 监控 网络协议
香港云服务器访问速度慢?阿里云精品BGP线路EIP一键提速方案
香港云服务器因默认BGP线路访问不稳定,尤其中国大陆用户面临高延迟与丢包问题。本文详解问题根源,并介绍阿里云国际站推出的精品BGP线路EIP解决方案,通过直连优化显著降低延迟,提升稳定性,助力企业实现高效跨境网络访问。
|
2月前
|
运维 数据可视化 数据库
一小时搞定服务器软件部署:资深工程师实测方案
本文分享了一位运维工程师在短时间内将30个不同软件部署到新服务器上的实战经验。面对全新 Rocky Linux 系统,传统手工部署方式效率低下且容易出错。作者尝试多种自动化方案后,最终选择使用自动化部署工具,通过其内置的 Docker Compose 模板和可视化界面,实现快速、批量部署,大幅提升效率,30个应用仅用约1小时完成,显著节省时间和人力成本。
|
5月前
|
存储 安全 关系型数据库
阿里云服务器选购配置方案云产品搭配指南参考
对于部分新手用户来说,面对阿里云众多的服务器配置和云产品,如何做出合适的选择,成为了大家比较关注的问题。本文将深入探讨阿里云服务器选择的相关知识,包括配置方案、云服务器活动以及云产品搭配策略,帮助您在众多选项中找到最适合自己的解决方案。
|
6月前
|
安全 网络安全 定位技术
网络通讯技术:HTTP POST协议用于发送本地压缩数据到服务器的方案。
总的来说,无论你是一名网络开发者,还是普通的IT工作人员,理解并掌握POST方法的运用是非常有价值的。它就像一艘快速,稳定,安全的大船,始终为我们在网络海洋中的冒险提供了可靠的支持。
220 22
|
5月前
|
存储 数据挖掘
服务器数据恢复—V7000存储上raid5阵列多块硬盘离线的数据恢复方案
V7000存储设备上raid5阵列上一块硬盘出现故障离线,热备盘自动启用开始同步数据。热备盘数据同步还没有结束的情况下,与离线盘处于同一组Mdisk中的另一块磁盘离线。热备盘同步失败,该组Mdisk失效,通用卷无法使用。

热门文章

最新文章