【计算机网络】UDP网络程序

一、服务端

1.udpServer.hpp

此文件负责实现一个udp服务器

#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

// 默认参数
static const std::string defaultIp = "0.0.0.0";
static const int gnum = 1024;

// 回调函数类型
typedef std::function<void (int, std::string, uint16_t, std::string)> func_t;

// 错误码
enum
{
    USAGE_ERR = 1,
    SOCKET_ERR,
    BIND_ERR,
    OPEN_ERR
};

// udp服务器
class udpServer
{
public:
    // 构造函数
    udpServer(func_t &callback, uint16_t &port, std::string &ip = defaultIp)
        :_callback(callback), _port(port), _ip(ip), _sockfd(-1)
    {}
    // 析构函数
    ~udpServer(){}
    // 初始化服务器
    void initServer();
    // 启动服务器
    void start();

private:
    uint16_t _port;  // 服务器进程的端口号
    std::string _ip; // 服务器的IP 一款网络服务器不建议指明一个IP
    int _sockfd;     // 创建socket后返回的文件描述符,利用这个fd进行读写
    func_t _callback;// 回调函数:利用这个函数处理业务
};

(1)initServer()

void initServer()
{
    // 1. 创建socket
    _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (_sockfd == -1)
    {
        cerr << "socket error" << errno << " : " << strerror(errno) << endl;
        exit(SOCKET_ERR);
    }
    std::cout << "socket success" << " : " << _sockfd << std::endl;

    // 2. 绑定 port ip
    // 未来服务器要明确的port,不能随意改变
    struct sockaddr_in local;
    bzero(&local, sizeof(local)); // 初始化结构体为全0
    local.sin_family = AF_INET;
    // 你如果要给别人发消息,你的port和ip要发送给对方
    // 所以port和ip要从当前主机到网络 h -> n
    local.sin_port = htons(_port);
    local.sin_addr.s_addr = inet_addr(_ip.c_str()); 
    // local.sin_addr.s_addr = htonl(INADDR_ANY);// 任意地址bind:INADDR_ANY:全0

    // 把创建的fd和端口号绑定
    int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
    if (n == -1)
    {
        cerr << "bind error" << errno << " : " << strerror(errno) << endl;
        exit(BIND_ERR);
    }
    // UDP Server 的预备工作完成
}

(2)start()

void start()
{
    // 服务器的本质其实就是一个死循环
    char buffer[gnum]; // 读到的数据放这里
    for (;;)
    {
        // 读取数据
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);                                                                 
        // recvfrom读取数据
        ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
        // 处理数据
        if (s > 0)
        {
            // 1.知道是谁发的(获取客户端ip/port)
            std::string clientip = inet_ntoa(peer.sin_addr); // IP
            uint16_t clientport = ntohs(peer.sin_port); // 端口号

            // 2.知道发的什么(获取客户端发送的内容)
            buffer[s] = '\0';
            std::string message = buffer;
            cout << clientip << "[" << clientport << "]# " << message << endl;

            // 我们只把数据读上来就完了吗?还需要对数据做处理
            _callback(_sockfd, clientip, clientport, message);
        }
    }
}

2.udpServer.cpp

此文件负责udp服务器的调用逻辑

#include "udpServer.hpp"
#include <cstdio>
#include <memory>
#include <fstream>
#include <signal.h>

// 使用手册
void Usage(std::string proc)
{
    std::cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}

// 对收到的数据做处理
void handlerMessage(int sockfd, std::string clientip, uint16_t clientport, std::string message)
{
    // 填写客户端信息
    struct sockaddr_in client;
    bzero(&client, sizeof(client));
    client.sin_family = AF_INET;
    client.sin_port = htons(clientport);
    client.sin_addr.s_addr = inet_addr(clientip.c_str());

    // 处理并发送消息(服务器 -> 客户端)
    std::string response_message = message;
    response_message = "[server echo] " + message;
    sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr*)&client, sizeof(client));
}

// ./udpServer local_port
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);

    std::unique_ptr<udpServer> usvr(new udpServer(handlerMessage, port));

    usvr->initServer();
    usvr->start();

    return 0;
}

二、客户端

1.udpClient.hpp

此文件负责实现一个udp客户端

#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>

class udpClient
{
public:
    // 构造函数
    udpClient(std::string &serverIp, const uint16_t &serverPort)
        : _serverIp(serverIp), _serverPort(serverPort), _sockfd(-1), _quit(false)
    {}
    // 析构函数
    ~udpClient(){}
    // 初始化客户端
    void initClient()
    // 这个线程负责 接受消息
    static void *readMessage(void *args)
    // 主线程负责 发送消息
    void run()

private:
    int _sockfd;           // 自己的fd
    std::string _serverIp; // 目的地
    uint16_t _serverPort;  // 目的地
    bool _quit;            // 表明是否退出
};

(1)initClient()

void initClient()
{
    // 1.创建socket
    _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (_sockfd == -1)
    {
        cerr << "socket error" << errno << " : " << strerror(errno) << endl;
        exit(2);
    }
    std::cout << "socket success" << " : " << _sockfd << std::endl;

    // 2.client要不要bind?要;client要不要显示的bind/程序员自己bind?不需要
}

① 客户访问服务器时需要一个确定的端口号,所以服务器的端口需要我们明确指出

② 客户端的端口号不用我们明确指出,因为写客户端的是许多家公司

假设A公司给Client_A定的端口号是8080,B公司给Client_B定的端口号也是8080

这样这两个程序就无法一起运行,不符合实际,所以客户端由OS自动形成端口进行bind!

(2)run()

// 主线程负责 发送消息
void run()
{
    // 填写服务器的ip/port
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(_serverIp.c_str());
    server.sin_port = htons(_serverPort);

    // 消息处理
    std::string message;
    char line[1024];
    while (!_quit)
    {
        // 发送消息(客户端 -> 服务器)
        std::cout << "Please Enter# ";
        // 这种输入:重点在于可以输入空格
        fgets(line, sizeof(line), stdin);
        line[strlen(line) - 1] = '\0'; // 处理掉输入进来的回车
        message = line;
        sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));
        // 没有显示绑定时,第一次发消息的时候绑定port

        // 接收消息(服务器 -> 客户端)
        char buffer[1024]; // 收到的消息放到这里
        struct sockaddr_in serverSock;
        socklen_t len = sizeof(serverSock);
        size_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&serverSock, &len);
        if (n >= 0) buffer[n] = 0;
        std::cout << buffer << std::endl;
    }
}

2.udpClient.cpp

此文件负责udp客户端的调用逻辑

#include "udpClient.hpp"
#include <memory>

// 使用手册
void Usage(std::string proc)
{
    std::cout << "\nUsage:\n\t" << proc << " server_ip server_port\n\n";
}

// ./udpClient server_ip server_port -> 输入目的地址(服务器地址)
int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    std::string serverIp = argv[1];
    uint16_t serverPort = atoi(argv[2]);

    std::unique_ptr<udpClient> ucli(new udpClient(serverIp, serverPort));

    ucli->initClient();
    ucli->run();

    return 0;
}

三、整体调用逻辑

1.双方调用逻辑

2.展示运行结果

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

定时器简介

TIM(Timer定时器)简介 在第一部分,我们主要讲的是定时器基本定时的功能&#xff0c;也就是定一个时间&#xff0c;然后让定时器每隔这个时间产生一个中断&#xff0c;来实现每隔一个固定时间执行一段程序的目的&#xff0c;比如你要做个时钟、秒表&#xff0c;或者使用一些程序…

【论文阅读】HITS: High-coverage LLM-based Unit Test Generation via Method Slicing

HITS: High-coverage LLM-based Unit Test Generation via Method Slicing 1. 来源出处 本文是发表在2024年39th IEEE/ACM International Conference on Automated Software Engineering (ASE)上的论文。作者包括Zejun Wang, Kaiibo Liu, Ge Li和Zhi Jin,他们来自北京的PKU …

多模态大模型开启AI社交新纪元,Soul App创始人张璐团队亮相2024 GITEX GLOBAL

随着AI在全球范围内的加速发展和广泛应用,各行业纷纷在此领域发力。作为全球最大的科技盛会之一,2024年的GITEX GLOBAL将目光再次聚焦于人工智能的飞速发展,吸引了超过6700家来自各个领域的企业参与。在这样的背景下,Soul App作为国内较早将AI技术应用于社交领域的平台,首次亮相…

爬虫开发工具与环境搭建——使用Postman和浏览器开发者工具

第三节&#xff1a;使用Postman和浏览器开发者工具 在网络爬虫开发过程中&#xff0c;我们经常需要对HTTP请求进行测试、分析和调试。Postman和浏览器开发者工具&#xff08;特别是Network面板和Console面板&#xff09;是两种最常用的工具&#xff0c;能够帮助开发者有效地捕…

Zabbix中文监控指标数据乱码

1&#xff09;点击主机&#xff0c;选择Zabbix server 中的 图形 一项&#xff0c;可以看到当前显示的为乱码 2&#xff09; 下载字体文件&#xff1a; https://gitcode.com/open-source-toolkit/4a3db/blob/main/SimHei.zip 解压unzip -x SimHei.zip 3&#xff09; 替换字体文…

HBase理论_HBase架构组件介绍

近来有些空闲时间&#xff0c;正好最近也在开发HBase相关内容&#xff0c;借此整理一下学习和对HBase组件的架构的记录和个人感受&#xff0c;付出了老夫不少心血啊&#xff0c;主要介绍的就是HBase的架构设计以及我的拓展内容。内容如有不当或有其他理解 matirx70163.com HB…

微信小程序自定义顶部导航栏(适配各种机型)

效果图 1.pages.js&#xff0c;需要自定义导航栏的页面设置"navigationStyle": "custom" 2.App.vue,获取设备高度及胶囊位置 onLaunch: function () {// 系统信息const systemInfo uni.getSystemInfoSync()// 胶囊按钮位置信息const menuButtonInfo uni.…

ArkTs简单入门案例:简单的图片切换应用界面

在鸿蒙 OS 应用开发的过程中&#xff0c;我们常常需要通过组合各种组件和编写相应的逻辑来实现丰富多样的功能。今天&#xff0c;我就来和大家详细解析一段实现简单图片切换功能的代码&#xff0c;希望能帮助到那些刚接触鸿蒙 OS 应用开发的朋友们。 一、代码导入部分 Entry …

【项目组件】第三方库——websocketpp

目录 第三方协议&#xff1a;websocket websocket简介 websocket特点 websocket协议切换 websocket协议格式段 websocketpp库介绍 endpoint server connection websocketpp库搭建服务器流程 基本框架实现 业务处理回调函数的实现 http_callback open_callback …

【手撕 Spring】 -- Bean 的创建以及获取

&#x1f308;手写简化版 Spring 框架&#xff1a;通过构建一个精简版的 Spring 框架&#xff0c;深入理解 Spring 的核心机制&#xff0c;掌握其设计思想&#xff0c;进一步提升编程能力 &#x1f308;项目代码地址&#xff1a;https://github.com/YYYUUU42/mini-Spring 如果该…

Jdbc学习笔记(四)--PreparedStatement对象、sql攻击(安全问题)

目录 &#xff08;一&#xff09;使用PreparedStatement对象的原因&#xff1a; 使用Statement对象编写sql语句会遇到的问题 ​编辑 &#xff08;二&#xff09;sql攻击 1.什么是sql攻击 2.演示sql攻击 &#xff08;三&#xff09;防止SQL攻击 1.PreparedStatement是什么 …

Jmeter中的定时器(二)

5--JSR223 Timmer 功能特点 自定义延迟逻辑&#xff1a;使用脚本语言动态计算请求之间的延迟时间。灵活控制&#xff1a;可以根据测试数据和条件动态调整延迟时间。支持多种脚本语言&#xff1a;支持 Groovy、JavaScript、BeanShell 等多种脚本语言。 支持的脚本语言 Groov…

【Istio】Istio原理

第一章 Istio原理 一、服务网格(servicemesh)1、六个时代2、服务网格定义及优缺点二、Istio1、Istio定义2、Istio安装3、Istio架构1.5版本之前1.5版本之后4、bookinfo案例架构部署5、CRD一、服务网格(servicemesh) 微服务:架构风格,职责单一,api通信 服务网格:微服务时代的…

4.远程访问及控制

SSH 简介&#xff1a; SSH&#xff08;Secure Shell&#xff09;协议是一种安全通道协议&#xff0c;对通信数据进行了加密处理&#xff0c;用于远程管理。 OpenSSH简介 OpenSSH 服务名称&#xff1a;sshd 服务端主程序&#xff1a;/usr/sbin/sshd 服务端配置文件&#xf…

精通rust宏系列教程-入门篇

Rust最令人敬畏和强大的特性之一是它使用和创建宏的能力。不幸的是&#xff0c;用于创建宏的语法可能相当令人生畏&#xff0c;并且对于新开发人员来说&#xff0c;这些示例可能会令人不知所措。我向你保证Rust宏非常容易理解&#xff0c;本文将为你介绍如何创建自己的宏。 什么…

设计模式之装饰器模式(SSO单点登录功能扩展,增加拦截用户访问方法范围场景)

前言&#xff1a; 两个本想描述一样的意思的词&#xff0c;只因一字只差就让人觉得一个是好牛&#xff0c;一个好搞笑。往往我们去开发编程写代码时也经常将一些不恰当的用法用于业务需求实现中&#xff0c;但却不能意识到。一方面是由于编码不多缺少较大型项目的实践&#xff…

kubernetes简单入门实战

本章将介绍如何在kubernetes集群中部署一个nginx服务&#xff0c;并且能够对其访问 Namespace Namespace是k8s系统中一个非常重要的资源&#xff0c;它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。 默认情况下&#xff0c;k8s集群中的所有的Pod都是可以相…

webpack5 + vue3 从零配置项目

前言 虽然在实际项目当中很少会从 0 到 1 配置一个项目&#xff0c;毕竟很多重复工作是没有必要的&#xff0c;脚手架将这些重复性的工作进行了整合&#xff0c;方便开发者使用。也正因如此&#xff0c;导致部分开发者过于依赖脚手架&#xff0c;却不清楚其内部的实现流程&…

Linux git-bash配置

参考资料 命令提示符Windows下的Git Bash配置&#xff0c;提升你的终端操作体验WindowsTerminal添加git-bash 目录 一. git-bash配置1.1 解决中文乱码1.2 修改命令提示符 二. WindowsTerminal配置git-bash2.1 添加git-bash到WindowsTerminal2.2 解决删除时窗口闪烁问题 三. VS…

(RK3566驱动开发 - 1).pinctrl和gpio子系统

一.设备树 pinctrl部分可以参考 rockchip 官方的绑定文档 &#xff1a;kernel/Documentation/devicetree/bindings/pinctrl PIN_BANK&#xff1a;引脚所属的组 - 本次例程使用的是 GPIO3_A1 这个引脚&#xff0c;所以所属的组为 3&#xff1b; PIN_BANK_IDX&#xff1a;引脚的…