翻译功能

我们写的UDP服务端并不是只接收到数据就完了,还需要进行处理任务

我们可以在服务端udpServer.hpp中设置一个回调函数 _callback,具体的逻辑通过udpServer.cc中由外部进行传入

代码如下所示:

 		void start()
        {
            // 服务器的本质其实就是一个死循环
            char buffer[gnum];
            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 > 0)
                {
                    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);
                }
            }
        }

翻译功能:客户端输入一个单词,然后发送给服务端,然后去接收服务端翻译之后的结果。

首先提供一个字典dict:把鞋有英语和汉语对应的文件dicTxt加载进我们的unordered_map词典,此时的unordered_map就保存了字典的内容

dict.txt:外部文件可由自己填写补充,这里只是给个样例测试代码而已

apple:苹果
world:世界
hello:你好
goodman:你是一个好人
const std::string dictTxt="./dict.txt";//文件
unordered_map<string, string> dict;//字典

下面,初始化我们的字典:打开文件,把文件中的每一行切分成key和value,也就是英文和中文,然后把结果插入到dict中,也就是把结果放进dict中,代码如下:

static bool cutString(const string &target, string *s1, string *s2, const string &sep)
{
    //apple:苹果
    auto pos = target.find(sep);
    if(pos == string::npos) return false;
    *s1 = target.substr(0, pos); //[)
    *s2 = target.substr(pos + sep.size()); //[)
    return true;
}

static void initDict()
{
    ifstream in(dictTxt, std::ios::binary);
    if(!in.is_open())
    {
        cerr << "open file " << dictTxt << " error" << endl;
        exit(OPEN_ERR);
    }
    string line;
    std::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;
}

在udpServer.cc中通过函数handlerMessage处理数据,然后在把处理的结果反馈给客户端:

void handlerMessage(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));
}

客户端udpClient.hpp输入消息cin并发送sendto给服务端,然后服务端udpServer.hpp调用回调函数对消息进行翻译,翻译完成后把最终的结果在传送sendto给客户端,客户端udpClient.hpp在接收recvfrom翻译之后的结果,最终把翻译结果打印出来即可:

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

            string message;
            char cmdline[1024];
            while (!_quit)
            {
                cout<<"Please Enter#";
                cin>>message;
                sendto(_sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));

                char buffer[1024];
                struct sockaddr_in temp;
                socklen_t temp_len = sizeof(temp);
                size_t n = recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&temp_len);
                if(n>0) buffer[n] = 0;
                cout<<"服务器的翻译结果# "<<buffer<<endl;   
            }
        }

下面,进行测试运行结果:

//udpServer.cc
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);
    initDict();
    std::unique_ptr<udpServer> usvr(new udpServer(handlerMessage, port));
}
//udpClient.cc
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> ucli(new udpClient(serverip, serverport));
    ucli->initClient();
    ucli->run();
    return 0;
}

先启动服务端:

【网络】UDP的应用场景-LMLPHP

在启动客户端输入信息,给服务端传送消息,服务端收到消息打印出来,并将翻译完成的结果返回给客户端,客户端再把翻译后的结果打印出来,这就是上面所说的整个过程:

【网络】UDP的应用场景-LMLPHP

【网络】UDP的应用场景-LMLPHP

命令行解析

借用popen接口:(功能相当于pipe+fork,exec*)

#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);

command:传递进来的字符串,比如 ls -a -l ;type:以什么方式打开文件(r/w/a),我们通过一个函数execComand进行调用即可,只需要在udpServer.cc文件中修改传入的函数即可实现该功能:

void execCommand(int sockfd, string clientip, uint16_t clientport, string cmd)
{
    //1. cmd解析,ls -a -l
    //先禁止一下非法操作,防止有人搞破坏
    if(cmd.find("rm") != string::npos || cmd.find("mv") != string::npos || cmd.find("rmdir") != string::npos)
    {
        cerr << clientip << ":" << clientport << " 正在做一个非法的操作: " << cmd << endl;
        return;
    }

    string response;
    FILE *fp = popen(cmd.c_str(), "r");
    if(fp == nullptr) response = cmd + " exec failed";
    char line[1024];
    while(fgets(line, sizeof(line), fp))
    {
        response += line;
    }
    pclose(fp);

    // 开始返回
    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.c_str(), response.size(), 0, (struct sockaddr*)&client, sizeof(client));
}

测试运行:

//udpServer.cc
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(execCommand, port));
}

【网络】UDP的应用场景-LMLPHP

【网络】UDP的应用场景-LMLPHP

网络聊天室

我们需要去管理用户,而对于每个用户我们用IP和port来标识唯一性,那么多的用户我们可以用哈希表来进行统一管理:

class User
{
public:
    User(const string &ip, const uint16_t &port) : _ip(ip), _port(port)
    {
    }
    ~User()
    {
    }
    string ip(){ return _ip; }
    uint16_t port(){ return _port; }
private:
    string _ip;
    uint16_t _port;
};

class OnlineUser
{
public:
    OnlineUser() {}
    ~OnlineUser() {}
    void addUser(const string &ip, const uint16_t &port)
    {
        string id = ip + "-" + to_string(port);
        users.insert(make_pair(id, User(ip, port)));
    }
    void delUser(const string &ip, const uint16_t &port)
    {
        string id = ip + "-" + to_string(port);
        users.erase(id);
    }
    bool isOnline(const string &ip, const uint16_t &port)
    {
        string id = ip + "-" + to_string(port);
        return users.find(id) == users.end() ? false : true;
    }
    void broadcastMessage(int sockfd, const string &ip, const uint16_t &port, const string &message)
    {
        for (auto &user : users)
        {
            struct sockaddr_in client;
            bzero(&client, sizeof(client));

            client.sin_family = AF_INET;
            client.sin_port = htons(user.second.port());
            client.sin_addr.s_addr = inet_addr(user.second.ip().c_str());
            string s = ip + "-" + to_string(port) + "# ";
            s += message;
            sendto(sockfd, s.c_str(), s.size(), 0, (struct sockaddr *)&client, sizeof(client));
        }
    }

private:
    unordered_map<string, User> users;
};

在回调函数中,如果收到的消息是online,就把用户添加进哈希表。如果是offline,就从哈希表中删除

if (message == "online") onlineuser.addUser(clientip, clientport);
if (message == "offline") onlineuser.delUser(clientip, clientport);
OnlineUser onlineuser;
void routeMessage(int sockfd, string clientip, uint16_t clientport, string message)
{
    if (message == "online") onlineuser.addUser(clientip, clientport);
    if (message == "offline") onlineuser.delUser(clientip, clientport);
    if (onlineuser.isOnline(clientip, clientport))
    {
        // 消息的路由
        onlineuser.broadcastMessage(sockfd, clientip, clientport, message);
    }
    else
    {
        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());

        string response = "你还没有上线,请先上线,运行: online";

        sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, sizeof(client));
    }
}

多线程:客户端udpClient.hpp不能立即收到消息打印出来,为了解决这个问题我们可以使用多线程,一个线程专门接收消息,一个线程专门发送消息:让主线程负责发送消息,子线程负责接收消息:

static void *readMessage(void *args)
        {
            int sockfd = *(static_cast<int *>(args));
            pthread_detach(pthread_self());
            while (true)
            {
                char buffer[1024];
                struct sockaddr_in temp;
                socklen_t temp_len = sizeof(temp);
                size_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &temp_len);
                if (n >= 0)
                    buffer[n] = 0;
                cout << buffer << endl;
            }

            return nullptr;
        }

        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());
            server.sin_port = htons(_serverport);

            string message;
            char cmdline[1024];
            while (!_quit)
            {
                fprintf(stderr, "Enter# ");
                fflush(stderr);
                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));
            }
        }

【网络】UDP的应用场景-LMLPHP

【网络】UDP的应用场景-LMLPHP

UDP之Windows与Linux

UDP的实现可以在不同的平台上进行交互的,在这里我们以Linux充当服务端,windows充当客户端,进行连通

windows端代码:

#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)


#include <iostream>
#include <string>
#include <cstring>
#include <WinSock2.h>

#pragma comment(lib,"ws2_32.lib")

using namespace std;
uint16_t serverport = 8080;
string serverip = "8.134.152.121";

int main()
{
	WSAData wsd;
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		cout << "WSAStartup Error = " << WSAGetLastError() << endl;
		return 0;
	}
	else
	{
		cout << "WSAStartup Success" << endl;
	}

	SOCKET csock = socket(AF_INET, SOCK_DGRAM, 0);
	if (csock == SOCKET_ERROR)
	{
		cout << "socket Error = " << WSAGetLastError() << endl;
		return 1;
	}
	else
	{
		cout << "socket success" << endl;
	}
	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());
#define NUM 1024
	char inbuffer[NUM];
	string line;
	while (true)
	{
		cout << "Please Enter#";
		getline(cin, line);

		int n = sendto(csock, line.c_str(), line.size(), 0, (struct sockaddr*)&server, sizeof(server));
		if (n < 0)
		{
			cerr << "sendto error!" << endl;
			break;
		}
		struct sockaddr_in peer;
		int peerlen = sizeof(peer);
		inbuffer[0] = 0;
		n = recvfrom(csock, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&peer, &peerlen);
		if (n > 0)
		{
			inbuffer[n] = 0;
			cout << "server 返回的消息是#" << inbuffer << endl;
		}
		else break;
	}
	closesocket(csock);
	WSACleanup();
	return 0;
}

Linux端代码:

pragma once

#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#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;
    static const string defaultIp = "0.0.0.0";
    static const int gnum = 1024;
    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,const 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<<"sdocket error: "<<errno<<" : "<<strerror(errno)<<endl;
                exit(SOCKET_ERR);
            }
            cout<<"socket success: "<<" : "<<_sockfd<<endl;

            struct sockaddr_in local;
            bzero(&local,sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = inet_addr(_ip.c_str());

            int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
            if(n == -1)
            {
                cerr<<"bind errpr: "<<errno<<" : "<<strerror(errno)<<endl;
                exit(BIND_ERR);
            }
        }

        void start()
        {
            char buffer[gnum];
            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>0)
                {
                    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:
        int _sockfd;
        uint16_t _port;
        string _ip;
        func_t _callback;
    };
}
//udpServer.cc
#include <iostream>
#include <memory>
#include "udpServer.hpp"
using namespace std;
using namespace Server;

static void Usage(string proc)
{
    cout<<"\nUsage:\n\t"<<proc<<" locla_port\n\n";
}

void handlerMessage(int sockfd,string clientip,uint16_t clientport,string message)
{
    string response_message = message;
    response_message+=" [server echo]";

    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);
    }
    uint16_t port = atoi(argv[1]);

    std::unique_ptr<udpServer> usvr(new udpServer(handlerMessage,port));
    usvr->initServer();
    usvr->start();
    return 0;
}
05-31 00:59