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

UDP英译汉网络词典

这里我们用UDP实现一个简单的英译汉小词典。我们还是仿照前一篇的UDP编程,将各自的组件封装起来,实现高内聚低耦合。

一. 字典翻译功能实现

首先我们将我们的字典知识库放在txt文本中。

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

然后我们来实现翻译功能。为了体现高内聚低耦合的思想,我们仍然封装成一个类。

const string defaultpath="./Dict.txt";

class Dict
{
public:
    Dict(const string& path=defaultpath)
    :_dict_conf_filepath(path)
    {}
    
    ~Dict()
    {}
private:
    unordered_map<string,string> _dict;
    string _dict_conf_filepath;
}; 

可以看到,类成员有两个,一个是Map类型的,对应我们先前txt文本中的一些汉英对照单词;另一个是string类型的,表明我们应该去哪里找汉英对照。

初始化时我们应该根据txt文本中的中英单词,填充_dict成员。

创建函数:

const string sep=": ";
bool Load()
{
    ifstream in(_dict_conf_filepath);
    if(!in.is_open())
    {
        LOG(FATAL,"open %s error\n",_dict_conf_filepath);
        return false;
    }
    string line;
    while(getline(in,line))
    {
        if(line.empty()) continue;
        auto pos=line.find(sep);//[)
        if(pos==string::npos) continue;
        string word=line.substr(0,pos);
        if(word.empty()) continue;
        string han=line.substr(pos+sep.size());
        if(han.empty()) continue;

        LOG(DEBUG,"load info, %s: %s\n",word,han);
        _dict.insert(make_pair(word,han));
    }

    in.close();
    LOG(DEBUG,"load %s success\n",_dict_conf_filepath.c_str());
    return true;
}

那么当我们上层调用函数寻找时,就可以根据_dict成员中找结果。

string Translate(const string& word,bool &ok)
{
    ok=true;
    auto iter=_dict.find(word);
    if(iter==_dict.end())
    {
        ok=false;
        return "未找到";
    }
    return iter->second;
}

最后我们加上命名空间,由此我们翻译功能实现代码整体如下:

#pragma once

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

using namespace std;

namespace dict_ns
{
    const string defaultpath="./Dict.txt";
    const string sep=": ";

    class Dict
    {
    private:
        bool Load()
        {
            ifstream in(_dict_conf_filepath);
            if(!in.is_open())
            {
                LOG(FATAL,"open %s error\n",_dict_conf_filepath);
                return false;
            }
            string line;
            while(getline(in,line))
            {
                if(line.empty()) continue;
                auto pos=line.find(sep);//[)
                if(pos==string::npos) continue;
                string word=line.substr(0,pos);
                if(word.empty()) continue;
                string han=line.substr(pos+sep.size());
                if(han.empty()) continue;

                LOG(DEBUG,"load info, %s: %s\n",word,han);
                _dict.insert(make_pair(word,han));
            }

            in.close();
            LOG(DEBUG,"load %s success\n",_dict_conf_filepath.c_str());
            return true;
        }
    public:
        Dict(const string& path=defaultpath)
        :_dict_conf_filepath(path)
        {
            Load();
        }

        string Translate(const string& word,bool &ok)
        {
            ok=true;
            auto iter=_dict.find(word);
            if(iter==_dict.end())
            {
                ok=false;
                return "未找到";
            }
            return iter->second;
        }

        ~Dict()
        {}
    private:
        unordered_map<string,string> _dict;
        string _dict_conf_filepath;
    }; 
}

二. 服务端代码实现

我们将服务端封装成一个类,并封装对应步骤在类函数中。

const static int defaultfd = -1;
using func_t=function<string(const string&,bool& ok)>;

class UdpServer
{
public:
    UdpServer(uint16_t port,func_t func)
        : _sockfd(defaultfd), _port(port), _func(func)
        ,_isrunning(false)
    {}
    ~UdpServer()
    {}
 
private:
    int _sockfd;
    uint16_t _port; // 服务器所用的端口号
    bool _isrunning;

    //给服务器设定回调,用来让上层进行注册业务的处理方法
    func_t _func;
};

此处有一个自定义类型func_t的变量,我们观察其参数结构,可以发现其实就是我们上面实现的翻译功能类中的Translate函数。我们通过这样的方式,实现高内聚低耦合,让上层实现翻译功能。

此处服务端同样不需要IP地址,与前面原因相同(不知道的同鞋可以看links)。

服务端初始成员函数:

void InitServer()
    {
        // 1.创建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);

        // 2.1 填充sockaddr_in结构
        struct sockaddr_in local;     // struct sockaddr_in 系统提供的数据类型,local是变量,用户栈上开辟空间
        bzero(&local, sizeof(local)); // 清空
        local.sin_family = AF_INET;
        local.sin_port = htons(_port); // port要经过网络传输给对面,即port先到网络,所以要将_port,从主机序列转化为网络序列
        local.sin_addr.s_addr=INADDR_ANY;//htonl(INADDR_ANY)

        // 2.2 bind sockfd和网络信息(IP(?)+Port)
        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");

    }

此处还是跟前面UDP编程一样。

服务端启动成员函数:

void Start()//所有的服务器,本质解决的是输入输出的问题!不想让网络通信模块和业务模块进行强耦合
{
    //一直运行,直到管理者不想运行了,服务器都是死循环
    _isrunning=true;
    while(true)
    {
        char request[1024];
        struct sockaddr_in peer;
        socklen_t len=sizeof(peer);
        //1.我们要让server先收数据
        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;
            string response=_func(request,ok);//将请求回调出去,在外部进行处理
            (void)ok;

            //2.我们要将server收到的数据,发回给对方
            sendto(_sockfd,response.c_str(),response.size(),0,(struct sockaddr*)&peer,len);
        }

    }
    _isrunning=false;
}

此处我们大致思路还是先通过recvfrom函数收到来自客户端的数据,然后再将翻译的结果返回给客户端。在这中间,就是我们的翻译函数,在服务端类中即我们的_func成员。

那么服务端代码合起来就是:

#pragma once
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <stdlib.h>
#include<functional>
#include "Log.hpp"
#include"InetAddr.hpp"
#include"Dict.hpp"

using namespace std;

enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    USAGE_ERROR
};

const static int defaultfd = -1;
using func_t=function<string(const string&,bool& ok)>;

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

    void InitServer()
    {
        // 1.创建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);

        // 2.1 填充sockaddr_in结构
        struct sockaddr_in local;     // struct sockaddr_in 系统提供的数据类型,local是变量,用户栈上开辟空间
        bzero(&local, sizeof(local)); // 清空
        local.sin_family = AF_INET;
        local.sin_port = htons(_port); // port要经过网络传输给对面,即port先到网络,所以要将_port,从主机序列转化为网络序列
        local.sin_addr.s_addr=INADDR_ANY;//htonl(INADDR_ANY)

        // 2.2 bind sockfd和网络信息(IP(?)+Port)
        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);
            //1.我们要让server先收数据
            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;
                string response=_func(request,ok);//将请求回调出去,在外部进行处理
                (void)ok;

                //2.我们要将server收到的数据,发回给对方
                sendto(_sockfd,response.c_str(),response.size(),0,(struct sockaddr*)&peer,len);
            }

        }
        _isrunning=false;
    }

    ~UdpServer()
    {
    }

private:
    int _sockfd;
    uint16_t _port; // 服务器所用的端口号
    bool _isrunning;

    //给服务器设定回调,用来让上层进行注册业务的处理方法
    func_t _func;
};

三. 服务端调用实现

此处还是跟UDP编程一样,因为我们实际只在服务端代码内部作了改动,在外层看起来调用都是没变的。

#include<iostream>
#include<memory>
#include"UdpServer.hpp"
#include"Log.hpp"
#include"Dict.hpp"
using namespace std;
using namespace dict_ns;

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

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

    //定义翻译模块
    Dict dict;

    //网络模块
    uint16_t port=stoi(argv[1]);
    unique_ptr<UdpServer> usvr=make_unique<UdpServer>(port,\
    bind(&Dict::Translate,&dict,placeholders::_1,placeholders::_2));//C++14
    usvr->InitServer();
    usvr->Start();

    return 0;
}

四. 客户端代码实现

此处也是没有变化的,所以我们可以体会到我们这种将不同功能的代码分别封装起来思想的好处。可以看到此处跟UDP编程其实变化不大。

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

using namespace std;


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


// ./udpclient serverip serverport
int main(int argc,char *argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        exit(1);
    }
    string serverip=argv[1];
    uint16_t serverport=stoi(argv[2]);

    //1.创建socket
    int sockfd=socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd<0)
    {
        cerr<<"socket error"<<endl;
    }

    //2.client一定要bind,client也有自己的ip和port,但是不建议显示(和server一样用bind函数)bind
    //a.那如何bind呢?当udp client首次发送数据的时候,os会自动随机的给client进行bind--为什么?要bind,必然要和port关联!防止client port冲突
    //b.什么时候bind?首次发送数据的时候

    //构建目标主机的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());

    string message;
    //3.直接通信即可
    while(true)
    {
        cout<<"Please Enter# ";
        getline(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;
            cout<<"server echo# "<<buffer<<endl;
        }
    }
    return 0;
}

五. 效果展示

在这里插入图片描述
此处打印出来的英汉对照有点格式问题,所以没有显示出来,但是我们可以发现翻译出来是没有问题的。

总结:

好了,到这里今天的知识就讲完了,大家有错误一点要在评论指出,我怕我一人搁这瞎bb,没人告诉我错误就寄了。

祝大家越来越好,不用关注我(疯狂暗示)


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

相关文章:

  • Servlet入门 Servlet生命周期 Servlet体系结构
  • Cyberchef配合Wireshark提取并解析HTTP/TLS流量数据包中的文件
  • 随时随地编码:香橙派Zero3上安装Code Server远程开发指南
  • 建筑施工特种作业人员安全生产知识试题
  • Django基础用法+Demo演示
  • qt QProcess详解
  • 在VB.net中,LINQ有什么查询表达式,举例说明
  • 掌握 Rust 中的 YAML 魔法:Serde_yaml 使用指南
  • QT在控件graphicsView中绘制箭头
  • Native开发与逆向第三篇 - hook JNI函数NewStringUTF
  • 构建视频生态技术基石:EasyCVR平台如何打破视频流媒体协议壁垒
  • kubeadm部署k8s1.25.3一主二从集群(Containerd)
  • 【GIT】说一说 Git 的常见命令和实践
  • uniapp中使用弹出框控制Tab栏区域显示与隐藏
  • Python编程实战营:四款实用小项目助你快速入门,从零开始打造你的个人项目集!
  • 【大模型理论篇】RoPE旋转位置编码底层数学原理分析
  • 深入理解Spring Boot的开箱即用与自动装配特性
  • 【爬虫软件】YouTube关键词搜索采集工具
  • 2024如何开始进入美业?美业创业步骤分享|博弈美业系统管理系统源码
  • Spark-第八周
  • 浅谈【数据结构】树与二叉树之哈夫曼树
  • 【Java设计模式】集合管道模式:简化数据操作
  • 买对不买贵,宠物空气净化器应该怎么选才能选到好的产品
  • 大数据技术之Flume 企业开发案例——负载均衡和故障转移(6)
  • SIGFPE (Arithmetic exception)
  • [Meachines] [Medium] Bastard Drupal 7 Module Services-RCE+MS15-051权限提升