【Linux】网络基础_4

news2025/1/29 13:55:21

文章目录

  • 十、网络基础
    • 5. socket编程
      • 网络翻译服务
  • 未完待续


十、网络基础

5. socket编程

网络翻译服务

基于UDP,我们实现一个简单的翻译。
我们导入之前写的代码:
InetAddr.hpp

#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

class InetAddr
{
private:
    void GetAddress(std::string *ip, uint16_t *port)
    {
        *port = ntohs(_addr.sin_port);
        *ip = inet_ntoa(_addr.sin_addr);
    }

public:
    InetAddr(const struct sockaddr_in &addr)
        : _addr(addr)
    {
        // 根据套接字结构获取IP地址和端口号
        GetAddress(&_ip, &_port);
    }

    std::string Ip()
    {
        return _ip;
    }

    uint16_t Port()
    {
        return _port;
    }

    ~InetAddr()
    {}
private:
    // 套接字结构
    struct sockaddr_in _addr;
    // IP地址
    std::string _ip;
    // 端口号
    uint16_t _port;
};

LockGuard.hpp

#ifndef __LOCK_GUARD_HPP__
#define __LOCK_GUARD_HPP__

#include <iostream>
#include <pthread.h>

class LockGuard
{
public:
    // 构造函数加锁
    LockGuard(pthread_mutex_t *mutex)
        :_mutex(mutex)
    {
        pthread_mutex_lock(_mutex);
    }

    // 析构函数解锁
    ~LockGuard()
    {
        pthread_mutex_unlock(_mutex);
    }
private:
    pthread_mutex_t *_mutex;
};

#endif

Log.hpp

#pragma once

#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdarg>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include "LockGuard.hpp"

// 宏定义,用于定义日志格式
#define LOG(level, format, ...) do{LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__);}while (0)
// 将日志输入到文件
#define EnableFile() do{gIsSave = true;}while (0)
// 将日志输出到显示器
#define EnableScreen() do{gIsSave = false;}while (0)

bool gIsSave = false;
// 日志文件名
const std::string logname = "log.txt";

// 枚举日志级别
enum Level
{
    DEBUG = 0,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

// 保存日志到文件
void SaveFile(const std::string &filename, const std::string &message)
{
    std::ofstream out(filename, std::ios::app);
    if (!out.is_open())
    {
        return;
    }
    out << message;
    out.close();
}

// 日志级别转字符串
std::string LevelToString(int level)
{
    switch (level)
    {
    case DEBUG:
        return "Debug";
    case INFO:
        return "Info";
    case WARNING:
        return "Warning";
    case ERROR:
        return "Error";
    case FATAL:
        return "Fatal";
    default:
        return "Unknown";
    }
}

// 获取当前时间字符串
std::string GetTimeString()
{
    time_t curr_time = time(nullptr);
    struct tm *format_time = localtime(&curr_time);
    if (format_time == nullptr)
        return "None";
    char time_buffer[1024];
    snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
             format_time->tm_year + 1900,
             format_time->tm_mon + 1,
             format_time->tm_mday,
             format_time->tm_hour,
             format_time->tm_min,
             format_time->tm_sec);
    return time_buffer;
}

// 日志锁,同一时刻只能写一个日志
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

// 日志信息
void LogMessage(std::string filename, int line, bool issave, int level, const char *format, ...)
{
    // 日志级别
    std::string levelstr = LevelToString(level);
    // 时间
    std::string timestr = GetTimeString();
    // 进程id
    pid_t selfid = getpid();

    // 日志内容
    char buffer[1024];
    va_list arg;
    va_start(arg, format);
    vsnprintf(buffer, sizeof(buffer), format, arg);
    va_end(arg);

    // 日志格式化
    std::string message = "[" + timestr + "]" + "[" + levelstr + "]" +
                          "[" + std::to_string(selfid) + "]" +
                          "[" + filename + "]" + "[" + std::to_string(line) + "] " + buffer;
    LockGuard lockguard(&lock);

    // 输出日志
    if (!issave)
    {
        std::cout << message;
    }
    else
    {
        SaveFile(logname, message);
    }
}

UdpClient.cc

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

void Usage(std::string proc)
{
    std::cout << "Usage:\n\t" << proc << " local_ip local_prot\n" << std::endl;
}

int main(int argc, char* argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    // 获取本地IP地址和端口号
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        std::cerr << "socket error" << std::endl;
    }

    // 构建目标主机的socket信息
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport);
    server.sin_addr.s_addr = inet_addr(serverip.c_str());
    std::string message;
    // 循环发送消息
    while (true)
    {
        // 输入消息
        std::cout << "Please Enter# ";
        std::getline(std::cin, message);
        sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));

        // 接收消息
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        char buffer[1024];
        ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
        if (n > 0)
        {
            buffer[n] = '\0';
            std::cout << "server echo# " << buffer << std::endl;
        }
    }
    
    return 0;
}

首先我们需要一个翻译服务。我们先创建一个 词库
Dict.txt

apple: 苹果
banana: 香蕉
cat: 猫
dog: 狗
book: 书
pen: 笔
happy: 快乐的
sad: 悲伤的
run: 跑
jump: 跳
teacher: 老师
student: 学生
car: 汽车
bus: 公交车
love: 爱
hate: 恨
hello: 你好
goodbye: 再见
summer: 夏天
winter: 冬天

这个词库代表当前我们所能翻译的能力范围,如果想要翻译更多单词,则需要往里面添加单词。
接着我们要将词库里的单词一一存入一个键值对当中。当客户端访问时以便能够提供服务。
Dict.hpp

#pragma once

#include <iostream>
#include <unordered_map>
#include <string>
#include <fstream>
#include "Log.hpp"

namespace dict_ns
{
    // 默认字典文件路径
    const std::string defaultpath = "./Dict.txt";
    // 字典文件中词条和汉字之间的分隔符
    const std::string sep = ": ";

    class Dict
    {
    private:
        bool Load()
        {
            // 读取文件内容
            std::ifstream in(_dict_conf_filepath);
            if (!in.is_open())
            {
                LOG(FATAL, "open %s error\n", _dict_conf_filepath.c_str());
                return false;
            }
            std::string line;
            // 逐行读取
            while (std::getline(in, line))
            {
                // 跳过空行
                if (line.empty()) continue;
                // 解析词条和汉字
                auto pos = line.find(sep);
                if (pos == std::string::npos) continue;
                std::string word = line.substr(0, pos);
                if (word.empty()) continue;
                std::string han = line.substr(pos + sep.size());
                if (han.empty()) continue;
                LOG(DEBUG, "load info, %s: %s\n", word.c_str(), han.c_str());
                // 加入字典
                _dict.insert(std::make_pair(word, han));
            }
            // 关闭文件
            in.close();
            LOG(DEBUG, "load %s success\n", _dict_conf_filepath.c_str());
            return true;
        }
    public:
        Dict(const std::string& filepath = defaultpath)
            : _dict_conf_filepath(filepath)
        {
            Load();
        }

        std::string Translate(const std::string& word, bool& ok)
        {
            ok = true;
            // 查找词条
            auto iter = _dict.find(word);
            // 检测词条存不存在
            if (iter == _dict.end())
            {
                ok = false;
                LOG(ERROR, "word %s not found\n", word.c_str());
                return "未找到";
            }
            // 返回汉字
            return iter->second;
        }

        ~Dict()
        {}
    private:
        // 词典映射
        std::unordered_map<std::string, std::string> _dict;
        // 词典配置文件路径
        std::string _dict_conf_filepath;
    };
}

有了翻译服务就可以让服务器提供这个服务。为了让网络与服务之间解耦合,我们不能直接在服务器上直接调用函数,我们最好使用包装器包装一下函数,然后通过回调的方法来访问服务。
UdpServer.hpp

#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Log.hpp"
#include "InetAddr.hpp"

// 错误码
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    USAGE_ERROR
};

// 默认套接字
const static int defaultfd = -1;
using func_t = std::function<std::string(const std::string&, bool&ok)>;

class UdpServer
{
public:
    UdpServer(uint16_t port, func_t func)
        :_sockfd(defaultfd)
        ,_port(port)
        ,_isrunning(false)
        ,_func(func)
    {}

    void InitServer()
    {
        // 创建 UDP socket 套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            LOG(FATAL, "socket error, %s, %d\n", strerror(errno), errno);
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "socket create success, sockfd: %d\n", _sockfd);

        // 创建 sockaddr_in 结构体
        struct sockaddr_in local;
        // 清空结构体
        bzero(&local, sizeof(local));
        // 设置IP协议地址族为 AF_INET 即IPv4
        local.sin_family = AF_INET;
        // 端口号要经过网络传输,需要转换成网络字节序
        local.sin_port = htons(_port);
        // 绑定到本地地址,IP地址设置为 INADDR_ANY 即 0.0.0.0, 表示接收所有地址的消息
        local.sin_addr.s_addr = INADDR_ANY;

        // 将套接字和结构体绑定
        int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "bind error, %s, %d\n", strerror(errno), errno);
            exit(BIND_ERROR);
        }
        LOG(INFO, "socket bind success\n");
    }

    void Start()
    {
        // 启动服务器
        _isrunning = true;
        while (true)
        {
            // 接收数据
            char request[1024];
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            // 接收数据
            ssize_t n = recvfrom(_sockfd, request, sizeof(request) - 1, 0, (struct sockaddr*)&peer, &len);
            if (n > 0)
            {
                // 收到数据,打印
                request[n] = '\0';
                InetAddr addr(peer);
                LOG(DEBUG, "get message from [%s:%d]: %s\n", addr.Ip().c_str(), addr.Port(), request);

                bool ok;
                // 将请求传给回调函数
                std::string response = _func(request, ok);
                (void)ok;

                // 发送数据
                sendto(_sockfd, response.c_str(), response.size(), 0, (struct sockaddr*)&peer, len);
            }
            // sleep(1);
            // LOG(DEBUG, "server is running...\n");
        }
        _isrunning = false;
    }

    ~UdpServer()
    {}
private:
    int _sockfd;
    // 服务器所用端口号
    uint16_t _port;
    bool _isrunning;

    // 给服务器设置回调函数,即服务函数
    func_t _func;
};

主函数:
Main.cc

#include <iostream>
#include <memory>
#include "UdpServer.hpp"
#include "Dict.hpp"

using namespace dict_ns;

void Usage(std::string proc)
{
    std::cout << "Usage:\n\t" << proc << " local_prot\n" << std::endl;
}

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERROR);
    }
    EnableScreen();
    // std::string local_ip = argv[1];

    Dict dict;

    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<UdpServer> usvr(new UdpServer(port, std::bind(&Dict::Translate, &dict, std::placeholders::_1, std::placeholders::_2)));
    // 初始化服务器
    usvr->InitServer();
    // 启动服务器
    usvr->Start();
    return 0;
}

Makefile

.PHONY:all
all:udpserver udpclient

udpserver:Main.cc
	g++ -o $@ $^ -std=c++11
udpclient:UdpClient.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f udpserver udpclient

OK,我们来使用一下。
服务端:./udpserver 8888
客户端:./udpclient 127.0.0.1 8888

在这里插入图片描述
成功实现了简陋的网络翻译服务。


未完待续

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

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

相关文章

2000-2022年各地级市能源消费数据(夜间灯光ArcGIS计算)

2000-2022年各地级市能源消费数据&#xff08;夜间灯光ArcGIS计算&#xff09; 1、时间&#xff1a;2000-2022年 2、指标&#xff1a;城市、省份、年份、能源消费总量(百吨标准煤) 3、范围&#xff1a;337个地级市 4、计算方法&#xff1a; 利用ArcGIS计算各地级市的DN总和…

【python案例】基于Python 爬虫的房地产数据可视化分析设计与实现

引言 研究背景与意义 房地产行业在我国属于支柱性产业&#xff0c;在我国社会经济发展中一直扮演着重要角色。房价问题&#xff0c;尤其是大中城市的房价问题&#xff0c;一直是政府、大众和众多研究人员关注的热点。如何科学地预测房价是房价问题的研究方向之一。随着互联网…

[WUSTCTF2020]朴实无华1

打开题目 扫目录用dirsearch扫&#xff0c;为节省建议只扫常见的目录&#xff0c;配置是&#xff1a; ./dirsearch.py -e bak,zip,txt,tgz,php -u http:..... -s 3 -t 20 访问一下 根据提示&#xff0c;再访问一次 提示不在这&#xff0c;抓包看看 根据提示&#xff0c;改ge…

(免费领源码)java#springboot#mysql大学校园旧物捐赠网站 25109-计算机毕业设计项目选题推荐

摘 要 在网络信息的时代&#xff0c;众多的软件被开发出来&#xff0c;给用户带来了很大的选择余地&#xff0c;而且人们越来越追求更个性的需求。在这种时代背景下&#xff0c;企业只能以用户为导向&#xff0c;按品种分类规划&#xff0c;以产品的持续创新作为企业最重要的竞…

cocos creator绘制网格背景(基于矢量绘图)

在2D游戏开发中&#xff0c;设计2D地图的背景实现通常有以下几种方式&#xff1a; 静态背景图&#xff1a; 最简单的方式是使用静态背景图&#xff0c;即将整个背景作为一个静态图像加载到游戏中。这种方式适用于简单的游戏或者背景不需要变化的场景。 平铺背景图&#xff1a;…

Mathematica 矩阵基础操作指南

使用 Mathematica 进行矩阵操作的指南 目录 使用 Mathematica 进行矩阵操作的指南引言创建矩阵矩阵运算加法与减法 矩阵乘法矩阵转置逐元素运算 矩阵的行列式与逆行列式逆矩阵 矩阵分解LU 分解QR 分解特征值与特征向量 矩阵的可视化矩阵的热图矩阵的网格图 末 引言 最近笔者在…

分布式事务-使用队列实现最终一致性

分布式事务-扣减库存 一、最终一致性架构图 1、服务 左侧&#xff1a;创建订单服务Server1 右侧&#xff1a;扣减库存服务Server2 中间&#xff1a;独立消息服务Server3 2、中间件&#xff1a; Kafka-MQ、MySQL-db 二、步骤 0、定义MQ&#xff0c;三个状态 prepareconf…

spring原理(自学第八天)

aop的实现原理 AOP 底层实现方式之一是代理&#xff0c;由代理结合通知和目标&#xff0c;提供增强功能 除此以外&#xff0c;aspectj 提供了两种另外的 AOP 底层实现&#xff1a; 第一种是通过 ajc 编译器在编译 class 类文件时&#xff0c;就把通知的增强功能&#xff0c;织…

传输层_计算机网络

文章目录 运输层UDPTCPTCP连接管理TCP三次握手TCP四次挥手 可靠机制流量控制拥塞控制 QUIC 运输层 网络层提供了主机之间的逻辑通信 运输层为运行在不同主机上的进程之间提供了逻辑通信 UDP(用户数据报协议)提供一种不可靠、无连接的服务&#xff0c;数据报 TCP(传输控制协议)…

【Linux详解】基础IO:软硬连接 | 动静态库管理

目录 软硬链接 1. 介绍 2.理解 2.1 如何理解硬链接&#xff1f; 2.2 如何理解软连接&#xff1f; 动静态库 1.介绍 1.1 使用 1.2 什么是库&#xff1f; 2.生成 2.1 静态库 2.2 动态库&#xff1a; 软硬链接 1. 介绍 1.1 软连接 是一个独立文件&#xff0c;具有独…

Stable Diffusion绘画 | 文生图-高分辨率修复-Hires.fix

开启「高分辨率修复」的作用是&#xff0c;提高图片分辨率&#xff0c;增加细节&#xff0c;从而让画面变得更清晰。 之所以不采取直接通过调整宽高来提高分辨率&#xff0c; 是因为绝大多数模型在训练时&#xff0c;精度都是 512x512&#xff0c;如果生成图片时&#xff0c;直…

云仓酒庄新纪元:雷盛红酒LEESON 401澳洲赤霞珠盛大发布

云仓酒庄新纪元&#xff1a;雷盛红酒LEESON 401澳洲赤霞珠盛大发布 在全球酒业市场的激烈竞争中&#xff0c;云仓酒庄始终以其敏锐的市场洞察力和卓越的产品品质引领行业潮流。近日&#xff0c;云仓酒庄宣布其精心筹备的雷盛红酒LEESON 401系列——澳洲原瓶进口赤霞珠正式上线…

API 接口设计原则:RESTful 与 GraphQL

RESTful 接口 REST 的全称是 REpresentational State Transfer&#xff0c;是一种 Web API 的设计风格 RESTful API 设计 6 大原则 一个 RESTful 风格的接口应该满足如下的 6 点原则&#xff1a; 统一接口&#xff1a;For example, the HTTP-based REST APIs make use of th…

小巧免费的笔记本电池检测工具

BatteryInfoView是一款免费的笔记本电池检测软件&#xff0c;适用于笔记本电脑和上网本。该软件能够提供电池的详细信息&#xff0c;包括电池名称、制造商名称、序列号、制造日期、电源状态&#xff08;充电/放电&#xff09;、当前电池容量、完全充电容量、设计容量、充电放电…

区块链核心概念与技术架构简介

引言 区块链&#xff0c;一种分布式账本技术&#xff0c;不仅为数字货币提供了基础设施&#xff0c;更在金融、供应链、物联网等多个领域展现出广泛的应用前景。区块链技术被认为是继蒸汽机、电力、互联网之后&#xff0c;下一代颠覆性的核心技术。 如果说蒸汽机释放了人们的…

Radxa ROCK 5B+开发板使用4G模块(移远EC200A)

目录 1、移远EC200A介绍2、ROCK 5B安装模组3、bsp编译内核4、使用4G模块拨号上网5、注意事项 前一篇博客&#xff1a;Radxa ROCK 5B开发板基本配置和上手测试 1、移远EC200A介绍 EC200A 系列是移远通信专为 M2M 和 IoT 领域设计的 LTE Cat 4 无线通信模块&#xff0c;采用 3G…

萱仔大模型学习记录5-langchain实战

前面我的bertlora微调已经跑出了不错的结果&#xff0c;我也学会了如何在bert上使用Lora进行微调&#xff0c;我后续会补充一个医疗意图识别的项目于这个系列&#xff0c;现在这个医疗意图识别代码还暂时不准备公开。我就继续按照我的计划学习一番LangChain。 LangChain是一个用…

yolov5 part2

two-stage &#xff08;两阶段&#xff09;&#xff1a;Faster-rcnn Mask-Rcnn系列 one-stage &#xff08;单阶段&#xff09;&#xff1a;YOLO系列 最核心的优势&#xff1a;速度非常快&#xff0c;适合实时监测任务。但是缺点也有&#xff0c;效果可能不好 速度较慢在2018…

Http自定义Header导致的跨域问题

最近写一个小项目&#xff0c;前后端分离&#xff0c;在调试过程中访问远程接口&#xff0c;出现了CORS问题&#xff0c;接口使用的laravel框架&#xff0c;于是添加了解决跨域的中间件&#xff0c;但是前端显示仍存在跨域问题&#xff0c;以为自己写的有问题&#xff0c;检查了…

等待唤醒机制两种实现方法-阻塞队列

桌子上有面条-》吃货执行 桌子上没面条-》生产者制造执行 1、消费者等待 消费者先抢到CPU执行权&#xff0c;发现桌子上没有面条&#xff0c;于是变成等待wait状态&#xff0c;并释放CPU执行权&#xff0c;此时的CPU肯定会被厨师抢到&#xff0c;初始开始做面条&#xff0c;…