Linux网络编程(四) 同时处理一个端口的UDP与TCP连接

news2024/11/17 11:55:06

bind系统调用的参数来看,一个socket只能与一个socket地址绑定,即一个socket只能用来监听一个端口。因此,服务器如果要同时监听多个端口,就必须创建多个socket,并将它们分别绑定到各个端口上。这样一来,服务器程序就需要同时管理多个监听socket,I/O复用技术就有了用武之地。

即使是同一个端口,如果服务器要同时处理该端口上的TCP和UDP请求,则也需要创建两个不同的socket:一个是流socket,另一个是数据报socket,并将它们都绑定到该端口上。

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>

#define MAX_EVENT_NUMBER 1024
#define TCP_BUFFER_SIZE 512
#define UDP_BUFFER_SIZE 1024

/* 设置文件为非阻塞 */
int setnonblocking(int fd)
{
    int old_option = fcntl(fd, F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_option);
    return old_option;
}

/* 添加epoll事件,ET边缘触发模式 */
void addfd(int epollfd, int fd)
{
    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    setnonblocking(fd);
}

int main()
{
    const char *ip = "127.0.0.1";
    int port = 8080;
    int ret = 0;

    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);
    
    /*创建TCP socket,并将其绑定到端口port上*/
    int tcpfd = socket(PF_INET, SOCK_STREAM, 0);
    ret = bind(tcpfd, (struct sockaddr *)&address, sizeof(address));
    assert(ret != -1);
    ret = listen(tcpfd, 5);
    assert(ret != -1);

    /*创建UDP socket,并将其绑定到端口port上*/
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);
    int udpfd = socket(PF_INET, SOCK_DGRAM, 0);
    ret = bind(udpfd, (struct sockaddr *)&address, sizeof(address));
    assert(ret != -1);

    /* 创建epoll事件表 */
    struct epoll_event events[MAX_EVENT_NUMBER];
    int epollfd = epoll_create(256);
    assert(epollfd != -1);

    /* 注册TCP socket和UDP socket上的可读事件 */
    addfd(epollfd, tcpfd);
    addfd(epollfd, udpfd);

    while (1)
    {
        int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
        if (number < 0)
        {
            printf("epoll failure\n");
            break;
        }
        // 处理事件
        for (int i = 0; i < number; i++)
        {
            int sockfd = events[i].data.fd;
            // 如果是tcp连接事件,那么将其注册到epoll事件表中
            if (sockfd == tcpfd)
            {
                struct sockaddr_in client_address;
                socklen_t client_addrlength = sizeof(client_address);
                int connfd = accept(tcpfd, (struct sockaddr *)&client_address, &client_addrlength);
                addfd(epollfd, connfd);
            }
            // 如果是udp连接事件,那么
            else if (sockfd == udpfd)
            {
                char buf[UDP_BUFFER_SIZE];
                memset(buf, '\0', UDP_BUFFER_SIZE);
                struct sockaddr_in client_address;
                socklen_t client_addrlength = sizeof(client_address);
                // 将udp中的数据读取到buf中
                ret = recvfrom(udpfd, buf, UDP_BUFFER_SIZE - 1, 0,
                               (struct sockaddr *)&client_address, &client_addrlength);
                // 如果收到了数据,打印
                if (ret > 0)
                {
                    printf("%s\n",buf);
                }
            }
            // 如果是一个socket有数据输入
            else if (events[i].events & EPOLLIN)
            {
                char buf[TCP_BUFFER_SIZE];
                while (1)
                {
                    memset(buf, '\0', TCP_BUFFER_SIZE);
                    ret = recv(sockfd, buf, TCP_BUFFER_SIZE - 1, 0);
                    if (ret < 0)
                    {   
                        // 如果当前操作非阻塞,操作无法立即完成,那么先不做处理
                        if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) break;
                        // 如果是出现了其他错误,那么关闭socket
                        close(sockfd);
                        break;
                    }
                    // 如果数据已经读完
                    else if (ret == 0)
                    {
                        close(sockfd);
                    }
                    // 如果是其他情况,那么返回收到的数据
                    else
                    {
                        printf("%s\n",buf);
                    }
                }
            }
            else
            {
                printf("something else happened\n");
            }
        }
    }
    close(tcpfd);
    return 0;
}

主要的精髓在这里

int tcpfd = socket(PF_INET, SOCK_STREAM, 0);
int udpfd = socket(PF_INET, SOCK_DGRAM, 0);

看一下客户端:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
  
#define SERVER_IP "127.0.0.1"  
#define SERVER_PORT 8080  
#define BUFFER_SIZE 1024  
  
int main() {  
    int tcp_socket, udp_socket;  
    struct sockaddr_in server_addr;  
    char buffer[BUFFER_SIZE];  
    ssize_t bytes_sent;  
  
    // 创建TCP socket  
    tcp_socket = socket(AF_INET, SOCK_STREAM, 0);  
    if (tcp_socket == -1) {  
        perror("TCP socket creation failed");  
        exit(EXIT_FAILURE);  
    }  
    
    // 创建UDP socket  
    udp_socket = socket(AF_INET, SOCK_DGRAM, 0); 
    if (udp_socket == -1) {  
        perror("UDP socket creation failed");  
        exit(EXIT_FAILURE);  
    }  

    // 设置服务器地址信息  
    memset(&server_addr, 0, sizeof(server_addr));  
    server_addr.sin_family = AF_INET;  
    server_addr.sin_port = htons(SERVER_PORT);  
    if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {  
        perror("Invalid server address");  
        close(tcp_socket);  
        close(udp_socket);  
        exit(EXIT_FAILURE);  
    }   

    // 连接到TCP服务器  
    if (connect(tcp_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) {  
        perror("TCP connection failed");  
        close(tcp_socket);  
        close(udp_socket);  
        exit(EXIT_FAILURE);  
    }  

    printf("Connected to server\n");  
  
    // 发送TCP消息给服务器  
    const char *tcp_message = "Hello, this is TCP message!\n";
    bytes_sent = send(tcp_socket, tcp_message, strlen(tcp_message), 0);  
    if (bytes_sent == -1) {
        perror("TCP send failed");
        close(tcp_socket);
        close(udp_socket);
        exit(EXIT_FAILURE);
    }

    // 发送UDP消息给服务器  
    const char *udp_message = "Hello, this is UDP message!\n";
    bytes_sent = sendto(udp_socket, udp_message, strlen(udp_message), 0, (struct sockaddr *) &server_addr, sizeof(server_addr));
    if (bytes_sent == -1) {
        perror("UDP sendto failed");
        close(tcp_socket);
        close(udp_socket);
        exit(EXIT_FAILURE);
    }

    // 关闭连接
    close(tcp_socket);
    close(udp_socket);

    return 0;
}

仿真

image-20240507214628171

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1656089.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Github下载的项目使用

根据该视频整理GitHub上的项目要怎么运行&#xff1f;一个视频教会你&#xff01;_哔哩哔哩_bilibili 方法一&#xff1a;从release中找。 方法二&#xff1a; 从官网中找&#xff08;位于右上角&#xff09; 方法三&#xff1a;看readme&#xff08;从readme中搜索以下词汇&a…

Milvus Cloud 的RAG 的广泛应用及其独特优势

一个典型的 RAG 框架可以分为检索器(Retriever)和生成器(Generator)两块,检索过程包括为数据(如 Documents)做切分、嵌入向量(Embedding)、并构建索引(Chunks Vectors),再通过向量检索以召回相关结果,而生成过程则是利用基于检索结果(Context)增强的 Prompt 来激…

【Qt 开发基础体系】字符串类应用和常用的数据类型

文章目录 1. Qt 字符串类应用1.1 操作字符串1.2 QString::append()函数1.3 QString::sprintf()函数1.4 QString::arg()函数 2. 查询字符串2.1 函数 QString::startsWith()2.2 函数 QString::contains()2.3 函数 QString::toInt()2.4 函数 QString::compare()2.5 将 QString 转换…

Github 50k star!吴恩达联合OpenAi共同编写<面向开发者的LLM入门教程> PDF推荐!

今天给大家推荐一本由吴恩达和OpenAI团队共同编写的关于大型语言模型&#xff08;LLM&#xff09;的权威教程<面向开发者的LLM入门教程>&#xff01;&#xff0c;在Github上已经高达50k star了&#xff0c;这含金量不用多说&#xff0c;在这里给大家强烈推荐一波&#xf…

电脑文件怎么加密?电脑涉密文件加密方法

文件加密是保护电脑涉密文件的重要方法&#xff0c;可以有效避免文件泄露风险。那么&#xff0c;电脑涉密文件该怎么加密呢&#xff1f;下面我们就来了解一下吧。 超级加密3000 在加密电脑涉密文件时&#xff0c;首先需要考虑的就是文件加密的安全性。因此&#xff0c;我们可以…

2024挂耳式耳机怎么选?5款高性价比开放式耳机推荐榜

近年来&#xff0c;开放式耳机受到了越来越多人的关注&#xff0c;特别是对于运动爱好者来说&#xff0c;在运动的过程中&#xff0c;传统的有线耳机不适合户外运动&#xff0c;不仅佩戴不稳&#xff0c;线还容易缠绕&#xff0c;而普通的蓝牙耳机长时间佩戴会感觉耳朵不适。在…

基于短时傅里叶变换域的一维信号邻域降噪方法(MATLAB)

基于傅里叶变换的信号频域表示及能量频域分布揭示了信号在频域的特征&#xff0c;但傅里叶变换是一种整体变换&#xff0c;只能了解信号的全局特性&#xff0c;不能有效检测信号频率随时间的变化情况。只有把时域和频域结合起来才能更好地反映非平稳信号的特征。时频分析的基本…

机器学习 - 梯度下降算法推导

要逐步推导多变量线性回归的梯度计算过程&#xff0c;我们首先需要明确模型和损失函数的形式&#xff0c;然后逐步求解每个参数的偏导数。这是梯度下降算法核心部分&#xff0c;因为这些偏导数将指导我们如何更新每个参数以最小化损失函数。 模型和损失函数 考虑一个多变量线…

Linux:进程通信(三)信号的捕捉

目录 一、信号捕捉函数 1、signal函数 2、sigaction函数 二、用户态与内核态 1、用户态 2、内核态 用户态与内核态转换 三、volatile关键字 四、SIGCHLD信号 一、信号捕捉函数 1、signal函数 signal函数是C语言标准库中的一个函数&#xff0c;用于处理Unix/Linux系…

web自动化系列-selenium执行js脚本|截图|识别验证码(十五)

1.执行脚本 如果你定位的元素通过各种方法都无法完成页面操作&#xff0c;最后的方法就是通过操作js脚本来完成 。 在selenium中提供了一个方法 &#xff0c;这个可以方法可以自行JS的脚本 。具体为&#xff1a; execute_script(js脚本) &#xff1a; js脚本代表要执行的脚本…

OC类与对象(下)

OC类与对象&#xff08;下&#xff09; 文章目录 OC类与对象&#xff08;下&#xff09;不是包装类的NSValue 和NSNumber处理对象打印对象和description方法 和 isEqual方法 类别类别语法部分利用类别进行模块化设计使用类别来调用私有方法 类的扩展协议与委托规范&#xff0c;…

BLIP和BLIP2 论文讲解

文章目录 BLIPIntroductionMethod模型架构预训练目标字幕和过滤&#xff08;Capfilt&#xff09; BLIP2IntroductionMethod模型结构Q-Former预训练第一阶段Q-Former预训练第二阶段 BLIP 论文&#xff1a; 《BLIP: Bootstrapping Language-Image Pre-training for Unified Visio…

Unity打开安卓设备不同的设置面板

1&#xff0c;打开安卓设备不同的设置面板&#xff0c;我还贴心的把Android官网的链接放下面了 2&#xff0c;使用也很方便&#xff1a;unity按钮事件上拖这个脚本&#xff0c;注册MyOpenAndroidSettings方法&#xff0c;参数 填 和枚举值相应的数字 // 功能&#xff1a;打开…

【c++】线程池的原理及实现

&#x1f4bb;文章目录 &#x1f4c4;前言线程池的原理概念工作原理 线程池的实现线程池的基础结构任务队列的实现工作线程的实现 线程池的应用与拓展线程池的拓展 &#x1f4d3;总结 &#x1f4c4;前言 不知道各位是否有试过点进限时抽奖网站、抢票网站呢&#xff1f;你是否好…

静态分析-RIPS-源码解析记录-02

这部分主要分析scanner.php的逻辑&#xff0c;在token流重构完成后&#xff0c;此时ini_get是否包含auto_prepend_file或者auto_append_file 取出的文件路径将和tokens数组结合&#xff0c;每一个文件都为一个包含require文件名的token数组 接着回到main.php中&#xff0c;此时…

一款功能强大的网络安全综合工具-PotatoTool

一、 简介 这款工具是一款功能强大的网络安全综合工具&#xff0c;旨在为安全从业者、红蓝对抗人员和网络安全爱好者提供全面的网络安全解决方案。它集成了多种实用功能&#xff0c;包括解密、分析、扫描、溯源等&#xff0c;为用户提供了便捷的操作界面和丰富的功能选择。 二…

《Fundamentals of Power Electronics》——状态空间平均法

文献中出现了许多交流变换器建模技术&#xff0c;包括电流注入法、电路平均法和状态空间平均法。尽管给定方法的支持者可能更喜欢用特定形式表示最终结果&#xff0c;但几乎所有方法的最终结果都是等效的。所有人都会赞同&#xff0c;平均和小信号线性化是PWM变换器建模的关键步…

厚德提问大佬答4:AI绘画生成的心得

遇到难题不要怕&#xff01;厚德提问大佬答&#xff01; 厚德提问大佬答 你是否对AI绘画感兴趣却无从下手&#xff1f;是否有很多疑问却苦于没有大佬解答带你飞&#xff1f;从此刻开始这些问题都将迎刃而解&#xff01;你感兴趣的话题&#xff0c;厚德云替你问&#xff0c;你解…

Element-plus修改input的placeholder文字颜色

需求 代码 .el-input__inner::placeholder {color: #666f8d !important; }

图像处理--空域滤波增强(原理)

一、均值滤波 线性滤波算法&#xff0c;采用的主要是邻域平均法。基本思想是使用几个像素灰度的某种平均值来代替一个原来像素的灰度值。可以新建一个MN的窗口以为中心&#xff0c;这个窗口S就是的邻域。假设新的新的像素灰度值为&#xff0c;则计算公式为 1.1 简单平均法 就是…