使用UDP协议实现—翻译服务器

目录

前言

1.设计思路:

2.词库设计

3.设计客户端

4.设计服务端

5.编译客户端和服务端

6.测试结果

7.总结


前言

上一篇文章中,我们使用UDP协议编码完成了一个简单的服务器,实现数据通信,服务器设计出来后目的不仅仅只是实现数据通信,而是根据客户端发过来的请求,实现一定的需求,今天我们要介绍的是当客户端给服务端发送英文单词,然后服务端获取客户端的请求,将翻译结果返回给客户端,通过这样的方式,实现了一款英文翻译服务器。下面我们就一起具体来看看是如何编码完成。

1.设计思路:

如图所示

第一步:启动服务器,然后服务器加载词库

第二步:客户端向服务器,发送请求

第三步:服务器处理请求查找单词,将查找结果返回给客户端

第四步:客户端获取查询结果

2.词库设计

说明:在这里只是简单模拟实现一个词库,主要是实现业务逻辑

dict.txt:

bash 复制代码
aunt:姨母
brother:兄弟
cousin:堂兄弟
couple:夫妇
dad:爸爸
daughter:女儿
family:家
father:爸爸
grandchild:孙子
granddaughger:孙女
grandfather:祖父
grandma:外婆
grandpa:外公
granny	老奶奶

3.设计客户端

udpClient.hpp

cpp 复制代码
#pragma once
#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
namespace Client
{
    using namespace std;
    class udpClient
    {
    public:
        udpClient(const string &serverIp, const uint16_t serverPort)
            : _serverIp(serverIp), _serverPort(serverPort), _sockfd(-1) {}
        void initClient()
        {
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if (_sockfd == -1)
            {
                cerr << "socket error:" << errno << strerror(errno) << endl;
                exit(2);
            }
        }
        void run()
        {
            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);
            while (1)
            {
                string message;
                cout << "请输入你想要翻译的单词:";
                getline(cin,message);
                //发送请求
                sendto(_sockfd, message.c_str(), message.size(), 0, (const struct sockaddr *)&server, sizeof(server));
                char buffer[1024];
                struct sockaddr_in temp;
                socklen_t len = sizeof(temp);
                //接受查询翻译结果
                size_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &len);
                if (n >= 0)
                    buffer[n] = 0;
                cout << "翻译的结果为: " << buffer << endl;
            }
        }

    private:
        string _serverIp;
        int _sockfd;
        uint16_t _serverPort;
    };
}

udpClient.cc:启动客户端

cpp 复制代码
#include"udpClient.hpp"
#include<memory>
using namespace Client;
static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " server_ip server_port\n\n";
}
int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);
    unique_ptr<udpClient> uct(new udpClient(serverip,serverport));
    uct->initClient();
    uct->run();
    return 0;
}

4.设计服务端

udpServer.hpp

cpp 复制代码
#pragma once
#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

namespace Server
{
    using namespace std;
    const static string defaultIP = "0.0.0.0";
    enum {USAGE_ERR = 1, SOCKET_ERR, BIND_ERR,OPEN_ERR};
    typedef function<void(int,string,uint16_t,string)> func_t;
    class udpServer
    {
    public:
        udpServer(const func_t& cb,uint16_t port, const string &ip = defaultIP) 
        :_callback(cb),_port(port),_ip(ip),_sockfd(-1)
        {}
        void initServer()
        {
            _sockfd = socket(AF_INET,SOCK_DGRAM,0);
            if(_sockfd == -1)
            {
                cerr<<"socket error:" << errno << strerror(errno) << endl;
                exit(SOCKET_ERR);
            }
            struct sockaddr_in local;
            bzero(&local,sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = htonl(INADDR_ANY);
            int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
            if(n == -1)
            {
                cerr<<"bind error:" << errno << strerror(errno) << endl;
                exit(BIND_ERR);
            }
        }
        void startServer()
        {
            char buffer[1024];
            for(;;)
            {
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                ssize_t s = recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
                if(s)
                {
                    buffer[s] = { 0 };
                    string clientIp = inet_ntoa(peer.sin_addr);
                    uint16_t clientPort = ntohs(peer.sin_port);
                    string message = buffer;
                    cout << clientIp << "[" << clientPort << "]" << message << endl;
                    //服务器只负责接受数据,处理方法采用回调的方式交给上层处理
                    _callback(_sockfd,clientIp,clientPort,message);
                }
            }
        }
        ~udpServer()
        {}
    private:
        uint16_t _port;
        string _ip;
        int _sockfd;
        func_t _callback;
    };
}

udpServer.cc:启动服务端

cpp 复制代码
#include "udpServer.hpp"
#include <memory>
#include <unordered_map>
#include <fstream>
using namespace Server;
static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
const string DictTxt = "./dict.txt";
unordered_map<string,string> dict;
static bool cutString(string& str,string& s1,string& s2,const string& sep)
{
    auto pos = str.find(sep);
    if(pos == string::npos)
        return false;
    s1 = str.substr(0,pos);
    s2 = str.substr(pos + sep.size());
    return true;
}
static void initDict()
{
    ifstream in(DictTxt,ios::binary);
    if(!in.is_open())
    {
        cerr << "open fail:" << DictTxt << "error" << endl;
        exit(OPEN_ERR);
    }
    string line;
    string key,value;
    while(getline(in,line))
    {
        if(cutString(line,key,value,":"))
        {
            dict.insert(make_pair(key,value));
        }
    }
    in.close();
    cout << "load dict success" << endl;
}
//翻译:
void TranslationWord(int sockfd,string clientIp,uint16_t clientPort,string message)
{
    string response_message;
    auto iter = dict.find(message);
    if(iter == dict.end()) 
        response_message = "unknown";
    else 
        response_message = iter->second;

    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());

    sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr*)&client, sizeof(client));
}
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    //加载词库
    initDict();
    uint16_t port = atoi(argv[1]);
    unique_ptr<udpServer> usvr(new udpServer(TranslationWord,port));
    usvr->initServer();
    usvr->startServer();
    return 0;
}

5.编译客户端和服务端

makefile:

cpp 复制代码
.PHONY:all
all:udpServer udpClient
udpServer:udpServer.cc
	g++ -o $@ $^ -std=c++11
udpClient:udpClient.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -f udpServer udpClient

6.测试结果

如图所示:服务端能够准确处理客户端的请求,将翻译查询结果返回给客户端

7.总结

以上就是使用UDP协议实现的一款翻译服务器,细心的小伙伴也已经发现了,在上面的代码中服务器的任务只是接受请求,然后将请求的数据回调处理,让上层处理业务逻辑,这样的实现方式实现了服务器与业务逻辑代码之间的解耦,如果以后想实现一款别的需求的服务器,只需要更改上层的业务逻辑就可以了。

相关推荐
sunfove2 小时前
光网络的立交桥:光开关 (Optical Switch) 原理与主流技术解析
网络
Kevin Wang7275 小时前
欧拉系统服务部署注意事项
网络·windows
min1811234565 小时前
深度伪造内容的检测与溯源技术
大数据·网络·人工智能
汤愈韬5 小时前
NAT策略
网络协议·网络安全·security·huawei
汤愈韬5 小时前
Full Cone Nat
网络·网络协议·网络安全·security·huawei
zbtlink6 小时前
现在还需要带电池的路由器吗?是用来干嘛的?
网络·智能路由器
桌面运维家6 小时前
vDisk配置漂移怎么办?VOI/IDV架构故障快速修复
网络·架构
dalerkd6 小时前
忙里偷闲叙-谈谈最近两年
网络·安全·web安全
汤愈韬7 小时前
NAT ALG (应用层网关)
网络·网络协议·网络安全·security·huawei
运维栈记8 小时前
虚拟化网络的根基-网络命名空间
网络·docker·容器