【Network】UDP Application Scenarios

translation function

The UDP server we wrote is not just finished receiving data, but also needs to perform processing tasks .

We can set a callback function _callback in the server udpServer.hpp, and the specific logic is passed in from the outside through udpServer.cc

The code looks like this:

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

Translation function: the client enters a word, then sends it to the server, and then receives the translated result from the server.

First provide a dictionary dict: load the file dicTxt corresponding to English and Chinese into our unordered_mapdictionary, and the unordered_map at this time saves the content of the dictionary :

dict.txt: External files can be filled in and supplemented by yourself, here is just a sample test code

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

Next, initialize our dictionary: open the file, divide each line in the file into key and value, that is, English and Chinese, and then insert the result into the dict, that is, put the result into the dict, the code is as follows:

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

Process the data through the function handlerMessage in udpServer.cc, and then feed back the processing result to the client:

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

The client udpClient.hpp inputs the message cin and sends sendto to the server, and then the server udpServer.hpp calls the callback function to translate the message. After the translation is completed, the final result is sent to the client in sendto, and the client udpClient.hpp is receiving The result after recvfrom translation, and finally print out the translation result:

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

Below, the test run results:

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

Start the server first:

image-20230530191842224

Enter information at the startup client, send a message to the server, the server receives the message and prints it out, and returns the translated result to the client, and the client prints the translated result, which is the above-mentioned whole process:

image-20230530191942072

image-20230530192002279

command line parsing

Borrow popen interface: (function equivalent to pipe+fork, exec*)

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

command: The string passed in, such as ls -a -l; type: How to open the file (r/w/a), we can call it through a function execCommand, only need to modify the incoming function in the udpServer.cc file to achieve this Function:

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

Test run:

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

image-20230530194723712

image-20230530194732874

Internet chat room

We need to manage users, and for each user we use IP and port to identify uniqueness, so many users we can use hash table for unified management:

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

In the callback function, if the received message is online, add the user to the hash table. If it is offline, delete it from the hash table

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

Multi-threading: The client udpClient.hpp cannot receive the message immediately and print it out. To solve this problem, we can use multi-threading. One thread is dedicated to receiving messages, and one thread is dedicated to sending messages: let the main thread be responsible for sending messages, and the sub-threads are responsible for receiving messages :

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

image-20230530213639848

image-20230530213650726

Windows and Linux of UDP

The implementation of UDP can interact on different platforms. Here we use Linux as the server and windows as the client to connect

Windows side code:

#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 code:

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

Guess you like

Origin blog.csdn.net/weixin_60478154/article/details/130958990