简单的Udp服务器

news/2024/5/18 16:08:37 标签: udp, 服务器, 单片机, c++, linux, centos, 网络协议

目录

  • 简单的UDP网络程序
    • 1.1 UdpServer.hpp
    • 1.2 UdpClient.cc
    • 1.3 main.cc
    • 1.4 makefile
    • 1.5 log.hpp

简单的UDP网络程序

1.1 UdpServer.hpp

#pragma once

#include <iostream>
using namespace std;

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"
#include <strings.h>
#include <functional>
#include <cstring>
#include <unordered_map>

const static int NUM = 1024;
const static string DEFAULT_IP = "0.0.0.0";
const static uint16_t DEFAULT_PORT=8080;

using func_t=function<string(string)>;

Log log;



class UdpServer
{
public:
    UdpServer(func_t func,uint16_t port=DEFAULT_PORT,string ip = DEFAULT_IP)
        : _ip(ip), _port(port), _sockid(-1),_func(func)
    {
    }

    ~UdpServer()
    {
        if (_sockid > 0)
        {
            close(_sockid);
        }
    }

    void Init()
    {
        // 创建套接字
        _sockid = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockid < 0)
        {
            log(Fatal, "socket failed");
            exit(2);
        }
        log(Info,"create socket successful, sockid:%d",_sockid);

        // 绑定
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        //绑定任意地址,可以接收任意发送给该主机的信息,而不是绑定一个具体的IP地址
        local.sin_addr.s_addr = INADDR_ANY;
        if (bind(_sockid, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            log(Fatal, "bind failed, errno:%d, error code:%s",errno,strerror(errno));
            exit(3);
        }
        log(Info, "Server bind successful");
    }

    //通过哈希表检查用户是否已经连上,如果没有就添加到连接的列表中
    void CheckUser(const struct sockaddr_in& client)
    {
        string clientIp=inet_ntoa(client.sin_addr);
        auto it=_online_client.find(clientIp);
        if(it==_online_client.end())
        {
            _online_client.insert({clientIp,client});
            std::cout << "[" << clientIp << ":" << ntohs(client.sin_port) << "] add to online user." << std::endl;
        }
    }

    //广播给所有人,即给所有连上该服务器的人都发送这条信息,类似于我们的微信群,
    //自己发出的信息所有人都能看见
    void BroadCast(const string& info,const string& clientip,const uint16_t& clientport)
    {
        for(const auto& it:_online_client)
        {
            string message="client";
            message+='[';
            message+="clientip:";
            message+=clientip;
            message+=' ';
            message+="clientport";
            message+=":";
            message+=to_string(clientport);
            message+="]# ";
            message+=info;

            sendto(_sockid,message.c_str(),message.size(),0,(struct sockaddr*)(&it.second),sizeof(it.second));
        }

    }

    //启动服务器
    void Run()
    {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        bzero(&client, sizeof(client));
        char buffer[NUM];
        bzero(buffer, sizeof(buffer));
        while (true)
        {
            ssize_t s = recvfrom(_sockid, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client, &len);
            if (s > 0)
            {
                buffer[s] = '\0';
                //cout << "client# " << buffer << endl;
                //printf("client[ip:%d,port:%d]# \n",client.sin_addr.s_addr,client.sin_port);
                char* clientip=inet_ntoa(client.sin_addr);
                //检查
                CheckUser(client);
                cout << "client[ip:"<<clientip<<" port:"<<ntohs(client.sin_port)<<"]# " << buffer << endl;

                uint16_t clientport=ntohs(client.sin_port);
                //广播给所有人
                BroadCast(buffer,clientip,clientport);
            }
            else if(s==0)
            {
                log(Warning,"client quit...");
                break;
            }
            else
            {
                log(Fatal,"recvfrom failed...");
                break;
            }

            //string ret=_func(buffer);
            //sendto(_sockid, ret.c_str(), ret.size(), 0, (struct sockaddr *)&client, len);
        }
    }

private:
    string _ip;
    uint16_t _port;
    int _sockid;
    //回调函数
    func_t _func;
    //通过IP地址映射标识一个已经连上服务器的客户端
    unordered_map<string,struct sockaddr_in> _online_client;
};

1.2 UdpClient.cc


#include <iostream>
using namespace std;

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <pthread.h>
#include <cstring>

//   重定向:2>/dev/pts/(1,2,3,4)

//UdpClient.cc

const static int NUM = 1024;

//客户端使用手册
void Usage(string argv)
{
    cout << "\n\t"
         << "Usage:" << argv << " ServerIp ServerPort" << endl<<endl;
}

struct ThreadData
{
    int sockid;
    struct sockaddr_in server;
    string ip;
};

//读取信息
void* recver_message(void* argv)
{
    //线程分离
    pthread_detach(pthread_self());

    ThreadData* td=static_cast<ThreadData*>(argv);

    char buffer[4096];
    memset(buffer,0,sizeof(buffer));
    while(true)
    {
        struct sockaddr_in t;
        socklen_t len=sizeof(t);
        ssize_t s=recvfrom(td->sockid,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&t,&len);
        string tip=inet_ntoa(t.sin_addr);
        if(s>0)
        {
            buffer[s]='\0';
            // cout<<"server# "<<tmp<<endl;
            printf("server[ip:%s,port:%d]# %s\n",tip.c_str(),ntohs(t.sin_port),buffer);
        }
    }

    return nullptr;
}


//发送信息
void * sender_message(void* argv)
{
    pthread_detach(pthread_self());
    ThreadData* td=static_cast<ThreadData*>(argv);

    std::string welcome = td->ip;
    welcome += " comming...";
    sendto(td->sockid, welcome.c_str(), welcome.size(), 0, (struct sockaddr *)&(td->server), sizeof(td->server));

    string buffer;
    while(true)
    {
        cerr<<"Please Enter# ";
        getline(cin,buffer);
        sendto(td->sockid,buffer.c_str(),buffer.size(),0,(struct sockaddr*)(&(td->server)),sizeof(td->server));
    }
    return nullptr;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }

    string ServerIp=argv[1];
    string str = argv[2];
    uint16_t ServerPort = (uint16_t)stoi(str.c_str());

    ThreadData td;

    //创建套接字
    int sockid=socket(AF_INET,SOCK_DGRAM,0);

    td.server.sin_family = AF_INET;
    td.server.sin_addr.s_addr=inet_addr(ServerIp.c_str());
    td.server.sin_port=htons(ServerPort);
    td.ip=ServerIp;
    td.sockid=sockid;
    socklen_t len=sizeof(td.server);

    pthread_t recver,sender;
    pthread_create(&recver,nullptr,recver_message,&td);
    pthread_create(&sender,nullptr,sender_message,&td);    

    while(true)
    {
        sleep(1);
    }

    close(sockid);
}

1.3 main.cc

#include <iostream>
using namespace std;
#include <string>
#include "UdpServer.hpp"
#include <vector>
#include <memory>

//服务器的启动方式
void Usage(string argv)
{
    cout << "\n\t"
         << "Usage:" << argv << " ServerPort" << endl
         << endl;
}

string func(string s)
{
    return s + " already handled\n";
}

//安全检查
bool SafeCheck(const string &cmd)
{
    //把客户端发过来的信息当作命令来解析,检查该信息是否合法
    vector<string> key_word = {"rm", "mv", "cp", "kill", "sudo", "unlink", "uninstall",
                               "yum", "top", "while"};
    for(const auto& s:key_word)
    {
        auto pos = cmd.find(s);
        if(pos!=string::npos)
        {
            return false;
        }
    }

    return true;
}

//执行指令
string ExcuteCommand(string cmd)
{
    if (!SafeCheck(cmd))
    {
        return "bad man\n";
    }
    //popen函数会自己创建子进程,创建管道,让子进程执行cmd.c_str()命令,
    //并通过管道把执行cmd命令的结果读取到FILE*的结构体对象中
    FILE *p = popen(cmd.c_str(), "r");
    if (nullptr == p)
    {
        perror("popen failed");
        exit(5);
    }
    string ret="\n";
    char buffer[4096];
    while (true)
    {
        //把执行命令后的结果按行读取出来
        char *s = fgets(buffer, sizeof(buffer) - 1, p);
        if (nullptr == s)
        {
            break;
        }
        ret += buffer;
    }
    pclose(p);
    return ret;
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(1);
    }

    string str = argv[1];
    uint16_t ServerPort = (uint16_t)stoi(str.c_str());

    unique_ptr<UdpServer> svr(new UdpServer(func, ServerPort));
    svr->Init();
    svr->Run();

    return 0;
}

1.4 makefile

.PHONY:all
all:Client Server

Client:UdpClient.cc
	g++ -o $@ $^ -std=c++11 -lpthread

Server:main.cc
	g++ -o $@ $^ -std=c++11 -lpthread

.PHONY:clean
clean:
	rm -f Client Server

1.5 log.hpp

#pragma once

#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string>
#include <time.h>
#include <stdarg.h>

// 日志等级
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4

#define Screen 1
#define OneFile 2
//向多个文件打印
#define Classfile 3
#define SIZE 1024

#define LogFile "log.txt"

class Log
{
public:
    Log()
    {
        printMethod = Screen;
        path = "./log/";
    }

    void Enable(int mothod)
    {
        printMethod = mothod;
    }

    string LevelToString(int level)
    {
        switch (level)
        {
        case Info:
        {
            return "Info";
        }
        case Debug:
        {
            return "Debug";
        }
        case Warning:
        {
            return "Warning";
        }
        case Error:
        {
            return "Error";
        }
        case Fatal:
        {
            return "Fatal";
        }
        default:
        {
            return "None";
        }
        }
    }

    void printlog(int level,const string& logtxt)
    {
        switch(printMethod)
        {
        case Screen:
        {
            cout<<logtxt<<endl;
            break;
        }
        case OneFile:
        {
            PrintOneFile(LogFile,logtxt);
            break;
        }
        case Classfile:
        {
            PrintClassfile(level,logtxt);
            break;
        }
        default:
        {
            break;
        }
        }
    }

    void PrintOneFile(const string& logname,const string& logtxt)
    {
        string _logname=path+logname;
        int fd=open(_logname.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);
        if(fd<0)
        {
            perror("open fail");
            return;
        }

        write(fd,logtxt.c_str(),logtxt.size());

        close(fd);

    }

    void PrintClassfile(int level,const string& logtxt)
    {
        string filename=LogFile;
        filename+='.';
        filename+=LevelToString(level);
        PrintOneFile(filename,logtxt);
    }

    void operator()(int level,const char* format,...)
    {
        time_t t=time(nullptr);
        struct tm* ctime=localtime(&t);
        char leftbuffer[SIZE];
        snprintf(leftbuffer,SIZE,"[%s][%d-%d-%d %d:%d:%d]",LevelToString(level).c_str(),
        ctime->tm_year+1900,ctime->tm_mon+1,ctime->tm_mday,
        ctime->tm_hour,ctime->tm_min,ctime->tm_sec);

        va_list s;
        va_start(s,format);
        char rightbuffer[SIZE]={0};
        vsnprintf(rightbuffer,SIZE,format,s);
        va_end(s);


        char logtxt[SIZE*2];
        snprintf(logtxt,sizeof(logtxt),"%s %s",leftbuffer,rightbuffer);

        printlog(level,logtxt);
    }

    ~Log()
    {
    }

private:
    // 打印方法
    int printMethod;
    string path;
};


http://www.niftyadmin.cn/n/5367460.html

相关文章

如何利用大模型结合文本语义实现文本相似度分析?

常规的文本相似度计算有TF-IDF&#xff0c;Simhash、编辑距离等方式&#xff0c;但是常规的文本相似度计算方式仅仅能对文本表面相似度进行分析计算&#xff0c;并不能结合语义分析&#xff0c;而如果使用机器学习、深度学习的方式费时费力&#xff0c;效果也不一定能达到我们满…

红队打靶练习:HEALTHCARE: 1

目录 信息收集 1、arp 2、nmap 3、nikto 4、whatweb 目录探测 1、gobuster 2、dirsearch WEB web信息收集 gobuster cms sqlmap 爆库 爆表 爆列 爆字段 FTP 提权 信息收集 本地提权 信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# arp-scan -l Inte…

win10没有调节亮度选项怎么办?

最近新装了win10&#xff0c;装机后”设置“-”显示“里面没有可以调节亮度的地方&#xff0c;这里记录一下解决方案。 解决方案 按WinX键&#xff0c;选择设备管理器&#xff0c;点击”显示适配器“&#xff0c;我这里默认是只有”Microsoft 基本显示适配器“&#xff0c;没有…

HCIA-Datacom实验指导手册:4.3 实验三:网络地址转换配置实验

HCIA-Datacom实验指导手册&#xff1a;4.3 实验三&#xff1a;网络地址转换配置实验 一、实验介绍&#xff1a;二、 思考题与附加内容 一、实验介绍&#xff1a; NAT的作用&#xff1a; 1、很大程度提高网络安全性。 2、控制内外网网络联通性问题。 特点&#xff1a; 1&#…

【EI会议征稿通知】第三届信号处理与通信安全国际学术会议(ICSPCS 2024)

第三届信号处理与通信安全国际学术会议&#xff08;ICSPCS 2024&#xff09; 2024 3rd International Conference on Signal Processing and Communication Security 信号处理和通信安全是现代信息技术应用的重要领域&#xff0c;近年来这两个领域的研究相互交叉促进&#xf…

CentOS镜像如何下载?在VMware中如何安装?

一、问题 CentOS镜像如何下载&#xff1f;在VMware中如何安装&#xff1f; 二、解决 1、CentOS镜像的下载 &#xff08;1&#xff09;官方网站 The CentOS Project &#xff08;2&#xff09;官方中文官网 CentOS 中文 官网 &#xff08;3&#xff09;选择CentOS Linux…

宝塔面板Node项目带参启动配置方法

宝塔面板Node项目带参启动配置方法 ​0x00 缘起​0x01 解决方法​0x02 系统信息​0x03 后记 ​0x00 缘起 编写的Nest项目根据启动时的环境变量加载不同的数据库配置, 在CentOS 7的终端中, 使用如下命令就可以启动成功: export NODE_ENVproduction; node dist/main可是使用宝塔…

正点原子--STM32通用定时器学习笔记(2)

1. 通用定时器输入捕获部分框图介绍 捕获/比较通道的输入部分&#xff08;通道1&#xff09; 采样频率&#xff1a;控制寄存器 1(TIMx_CR1)的CKD[1:0] ⬇⬇⬇​​​​​​​滤波方式选择&#xff1a; 捕获/ 比较模式寄存器 1(TIMx_CCMR1)的输入捕获部分⬇​​​​​​​⬇​…