当前位置: 首页 > article >正文

【计算机网络】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); 
    // 1.string->uint32_t  2.htonl() -> inet_addr函数
    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.知道是谁发的
            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 clinetip, 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(clinetip.c_str());

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

// ./udpServer 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;            // 表明是否退出
    pthread_t _reader; // 这个线程负责 读取消息
};

(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?不需要
    // 写服务器的是一家公司,写client是无数家公司 -- 有OS自动形成端口进行bind!
    // client只要有端口号就行,而server的端口号一定要是明确的,众所周知的

    // bind:让文件信息 和 网络端口 产生关联
}

(2)run()

// 主线程负责 发送消息
void run()
{
    pthread_create(&_reader, nullptr, readMessage, (void *)&_sockfd);

    // 填写 要发送到的服务器的信息
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(_serverIp.c_str()); // 1.string->uint32_t  2.htonl()
    server.sin_port = htons(_serverPort);

    // 发送信息
    std::string message;
    char cmdline[1024];
    while (!_quit)
    {
        // std::cout << "Please Enter# ";
        // 这种输入:重点在于可以输入空格
        fgets(cmdline, sizeof(cmdline), stdin);
        cmdline[strlen(cmdline) - 1] = '\0'; // 处理掉输入进来的回车
        message = cmdline;

        sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server)); // 发送消息
        // 没有显示绑定时,第一次发消息的时候绑定port
    }
}

(3)readMessage()

// 这个线程负责 接受消息
static void *readMessage(void *args)
{
    int sockfd = *(static_cast<int *>(args));
    pthread_detach(pthread_self());

    while (true)
    {
        // 接收服务器返回的结果
        char buffer[1024]; // 接收到的信息存储到buffer中
        struct sockaddr_in temp;
        socklen_t temp_len = sizeof(temp);
        size_t n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&temp, &temp_len);
        if (n >= 0)
            buffer[n] = '\0';
        std::cout << buffer << std::endl;
    }

    return nullptr;
}

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;
}

三、整体调用逻辑

123


http://www.kler.cn/a/392190.html

相关文章:

  • 什么是cline?
  • ADO.NET知识总结4---SqlParameter参数
  • 每日一题-两个链表的第一个公共结点
  • Vscode辅助编码AI神器continue插件
  • 计算机网络基础——网络协议
  • 【人工智能计算机视觉】——深入详解人工智能计算机视觉之图像处理之基础图像处理技术
  • 汇总常用的114款AI视频创作工具,堪称运营神器,收藏备用!
  • OpenBayes 一周速览丨VASP 教程上线!HPC 助力材料计算;AllClear 公共云层去除数据集发布,含超 23k 个全球分布的兴趣区域
  • 一文了解珈和科技在农业遥感领域的服务内容和能力
  • 智慧医疗:纹理特征VS卷积特征
  • Kafka - 启用安全通信和认证机制_SSL + SASL
  • Python学习从0到1 day27 Python 高阶技巧 ④ 设计模式 — 工厂模式
  • 计算机组成原理——提高存储器访问速度
  • 发布一个npm组件库包
  • Java[面试题]-真实面试2.0
  • KALI-sqlmap更新
  • JVM 参数配置入门与优化案例
  • ubantu git
  • 回溯算法(组合问题)-- 游戏分组
  • btstack协议栈实战篇--HID Keyboard Classic
  • 预处理、编译、汇编和链接
  • 力扣 二叉树的直径-543
  • 详解Gemini API的使用:在国内实现大模型对话与目标检测教程
  • SpringBoot 实战:文件上传之秒传、断点续传、分片上传
  • 比速M3比速T3比速T5北汽制造007 勇士 锐铃维修手册电路图资料更新线路接线
  • 高效编程训练:Spring Boot系统设计与实践