Advertisement

Linux第三十九章

阅读量:

🐶博主主页:[@ᰔᩚ. 一怀明月ꦿ]( "@ᰔᩚ. 一怀明月ꦿ")

❤️‍🔥专栏系列:线性代数C初学者入门训练题解CC的使用文章「初学」C++linux

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

popen

sz和rz

简单的聊天室

Comm.hpp

InetAddr.hpp

Lockguard.hpp

Log.hpp

nocopy.hpp

thread.hpp

ThreadPool.hpp

Udpserver.hpp

main.cc

UdpClient.cc

Makefile

运行结果


popen

在Linux系统中,popen函数用于通过管道连接来执行shell命令。

它允许你在程序中调用shell命令,and通过标准输入和标准输出与其交互。

popen函数的原型如下:

复制代码
>       1. FILE *popen(const char *command, const char *mode);

>  
>       2. 其中,command参数是一个字符串,表示要执行的shell命令。mode参数是一个字符串,
>  
>       3. 指定管道连接的模式,可以是"r"(读模式)或"w"(写模式)。
>  
>       4. popen函数会返回一个FILE类型的指针,可以像操作普通文件一样使用它来读取或写入数据。
>  
>  
>  
>  
> ```
>
>
>
> **以下是一个示例,展示如何使用popen函数执行一个shell命令并读取其输出:**
>
>
复制代码
  1. #include <stdio.h>

以下是基于给定规则对原文的改写

复制代码
>       1. 在 Ubuntu 和 Debian 等基于 Debian 的发行版中

>  
>       2. sudo apt-get update
>  
>       3. sudo apt-get install lrzsz
>  
>  
>  
>  
> ```
>
>
>
> ### 简单的聊天室
>
>
>
> #### Comm.hpp
>
>
>
> 用于定义一些错误码
>
>
复制代码
  1. #pragma once
复制代码
  2. enum

  3. {

  4.     Usage_Err=1,

  5.     Socket_Err,

  6.     Bind_Err

  7. };
复制代码
#### InetAddr.hpp



用于将网络字节序的ip地址转为主机字节序、用于将网络字节序的端口号转为主机字节序
复制代码
>       1. #pragma once

>  
>       2.  
>  
>       3. #include <iostream>
>  
>       4. #include <string>
>  
>       5. #include <sys/types.h>
>  
>       6. #include <sys/socket.h>
>  
>       7. #include <arpa/inet.h>
>  
>       8. #include <netinet/in.h>
>  
>       9.  
>  
>       10. using namespace std;
>  
>       11.  
>  
>       12. //用于将网络字节序的ip地址转为主机字节序
>  
>       13. //用于将网络字节序的端口号转为主机字节序
>  
>       14. class InetAddr
>  
>       15. {
>  
>       16. public:
>  
>       17.     InetAddr(struct sockaddr_in &addr):_addr(addr)
>  
>       18.     {
>  
>       19.         _port = ntohs(_addr.sin_port);   // 想看看客户端的端口号,ntohs(peer.sin_port),因为我们是从网络拿的数据,我需要将网络字节序转为主机序列
>  
>       20.         _ip = inet_ntoa(_addr.sin_addr); // 想看看客户端的ip,将网络字节序的ip地址转为主机字节序
>  
>       21.     }
>  
>       22.     string Ip()
>  
>       23.     {
>  
>       24.         return _ip;
>  
>       25.     }
>  
>       26.     uint16_t Port()
>  
>       27.     {
>  
>       28.         return _port;
>  
>       29.     }
>  
>       30.     string PrintDebug()
>  
>       31.     {
>  
>       32.         string clientinfo = _ip + ":" + to_string(_port);
>  
>       33.         return clientinfo;
>  
>       34.     }
>  
>       35.  
>  
>       36.     const struct sockaddr_in& GetAddr()
>  
>       37.     {
>  
>       38.         return _addr;
>  
>       39.     }
>  
>       40.  
>  
>       41.     bool operator==(InetAddr& addr)
>  
>       42.     {
>  
>       43.         return this->_ip==addr.Ip()&&this->_port==addr.Port();
>  
>       44.     }
>  
>       45.     ~InetAddr()
>  
>       46.     {
>  
>       47.     }
>  
>       48.  
>  
>       49. private:
>  
>       50.     string _ip;
>  
>       51.     uint16_t _port;
>  
>       52.     struct sockaddr_in _addr;
>  
>       53. };
>  
>  
>  
>  
> ```
>
>
>
> #### Lockguard.hpp
>
>
>
> 用于创建锁
>
>
复制代码
  1. #pragma once

Log.hpp的作用是存储日志信息,并且可以选择将日志输出到显示器、一个文件或根据日志级别的分类文件中。

复制代码
>       1. #include <iostream>

>  
>       2. #include <stdarg.h>
>  
>       3. #include <string>
>  
>       4. #include <ctime>
>  
>       5. #include <unistd.h>
>  
>       6. #include <fstream>
>  
>       7. #include <sys/types.h>
>  
>       8. #include <sys/stat.h>
>  
>       9. #include <fcntl.h>
>  
>       10. using namespace std;
>  
>       11.  
>  
>       12. // 定义一个枚举,表示日志级别
>  
>       13. enum
>  
>       14. {
>  
>       15.     Debug = 0,
>  
>       16.     Info,
>  
>       17.     Warning,
>  
>       18.     Error,
>  
>       19.     Fatal
>  
>       20. };
>  
>       21.  
>  
>       22. // 定义一个枚举,表示输出方式
>  
>       23. enum
>  
>       24. {
>  
>       25.     Screen = 10,
>  
>       26.     OneFile,
>  
>       27.     ClassFile
>  
>       28. };
>  
>       29.  
>  
>       30. // 定义一个函数,将日志级别转换为字符串
>  
>       31. string Leveltostring(int level)
>  
>       32. {
>  
>       33.     switch (level)
>  
>       34.     {
>  
>       35.     case Debug:
>  
>       36.         return "Debug";
>  
>       37.     case Info:
>  
>       38.         return "Info";
>  
>       39.     case Warning:
>  
>       40.         return "Warning";
>  
>       41.     case Error:
>  
>       42.         return "Error";
>  
>       43.     case Fatal:
>  
>       44.         return "Fatal";
>  
>       45.     default:
>  
>       46.         return "Unknown";
>  
>       47.     }
>  
>       48. }
>  
>       49.  
>  
>       50. const int default_style = Screen;       // 默认想显示器打印
>  
>       51. const string default_filename = "log."; // 默认的文件名是log.
>  
>       52. const string logdir="log";
>  
>       53. // 定义一个日志类
>  
>       54. class Log
>  
>       55. {
>  
>       56. public:
>  
>       57.     Log() : style(default_style), filename(default_filename)
>  
>       58.     {
>  
>       59.         mkdir(logdir.c_str(), 0777);// 创建一个目录
>  
>       60.     }
>  
>       61.     // 定义一个函数,将时间戳转换为字符串
>  
>       62.     string Timelocaltime()
>  
>       63.     {
>  
>       64.         time_t curtime = time(nullptr);
>  
>       65.         struct tm *curr = localtime(&curtime);
>  
>       66.  
>  
>       67.         char time_buffer[128];
>  
>       68.         snprintf(time_buffer, sizeof(time_buffer), "%d年-%d月-%d日 %d时:%d分:%d秒",
>  
>       69.                  curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday, curr->tm_hour, curr->tm_min, curr->tm_sec);
>  
>       70.         return time_buffer;
>  
>       71.     }
>  
>       72.  
>  
>       73.     // 定义一个函数,设置一个style,向哪里输出,默认是向屏幕输出
>  
>       74.     void SetStyle(int style) // 设置一个style,向哪里输出,默认是向屏幕输出
>  
>       75.     {
>  
>       76.         this->style = style;
>  
>       77.     }
>  
>       78.     // 定义一个函数,设置一个文件名
>  
>       79.     void SetFilename(const string &filename)
>  
>       80.     {
>  
>       81.         this->filename = filename;
>  
>       82.     }
>  
>       83.  
>  
>       84.     // 定义一个函数,将日志写入文件
>  
>       85.     void WriteLogToOneFile(const string &logname, const string &message)
>  
>       86.     {
>  
>       87.         int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
>  
>       88.         if (fd < 0)
>  
>       89.         {
>  
>       90.             exit(-1);
>  
>       91.         }
>  
>       92.  
>  
>       93.         write(fd, message.c_str(), message.size());
>  
>       94.         close(fd);
>  
>       95.     }
>  
>       96.  
>  
>       97.     // 定义一个函数,将日志写入文件
>  
>       98.     void WriteLogToClassFile(const string &levelstr, const string &message) // 将日志写入文件
>  
>       99.     {
>  
>       100.         string logname = logdir;
>  
>       101.         logname += "/";
>  
>       102.         logname += filename;
>  
>       103.         logname += levelstr;
>  
>       104.         WriteLogToOneFile(logname, message);
>  
>       105.     }
>  
>       106.  
>  
>       107.     //
>  
>       108.     void WriteLog(const string &levelstr, const string &message)
>  
>       109.     {
>  
>       110.  
>  
>       111.         switch (style)
>  
>       112.         {
>  
>       113.         case Screen:
>  
>       114.             cout << message;
>  
>       115.             break;
>  
>       116.         case OneFile:
>  
>       117.             WriteLogToClassFile("all", message);
>  
>       118.             break;
>  
>       119.         case ClassFile:
>  
>       120.             WriteLogToClassFile(levelstr, message);
>  
>       121.             break;
>  
>       122.         default:
>  
>       123.             break;
>  
>       124.         }
>  
>       125.     }
>  
>       126.     void LogMessage(int level, const char *format, ...)
>  
>       127.     {
>  
>       128.         char rightbuffer[1024]; // 日志的内容
>  
>       129.         va_list args;           // va_list其实就是char* 类型的
>  
>       130.         va_start(args, format); // 获取可变参数的位置,由args去指向可变参数部分
>  
>       131.         vsnprintf(rightbuffer, sizeof(rightbuffer), format, args);
>  
>       132.         va_end(args); // 相等于args=nullptr
>  
>       133.  
>  
>       134.         char leftbuffer[1024];
>  
>       135.         string levelstr = Leveltostring(level);
>  
>       136.         string curtime = Timelocaltime();
>  
>       137.         string idstr = to_string(getpid());
>  
>       138.         snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s]", levelstr.c_str(), curtime.c_str(), idstr.c_str());
>  
>       139.  
>  
>       140.         string loginfo = leftbuffer;
>  
>       141.         loginfo.append(rightbuffer);
>  
>       142.  
>  
>       143.         WriteLog(levelstr, loginfo);
>  
>       144.     }
>  
>       145.  
>  
>       146.     ~Log()
>  
>       147.     {
>  
>       148.     }
>  
>       149.  
>  
>       150. private:
>  
>       151.     int style;
>  
>       152.     string filename;
>  
>       153. };
>  
>  
>  
>  
> ```
>
>
>
> #### nocopy.hpp
>
>
>
> 主要是用来设计一个不能继承的类
>
>
复制代码
  1. #pragma once
复制代码
  2.  

  3. #include<iostream>

  4.  

  5. class nocopy

  6. {

  7.     public:

  8.     nocopy()

  9.     {}

  10.     nocopy(const nocopy&)=delete;

  11.     const nocopy& operator=(const nocopy&)=delete;

  12.     ~nocopy()

  13.     {}

  14. };
复制代码
#### thread.hpp



用于创建线程
复制代码
>       1. #pragma once

>  
>       2. #include<iostream>
>  
>       3. #include<string>
>  
>       4. #include<functional>
>  
>       5. #include<pthread.h>
>  
>       6.  
>  
>       7. using namespace std;
>  
>       8.  
>  
>       9. //typedef function<void()> func_t
>  
>       10. template<class T>
>  
>       11. using func_t=function<void(T&)>;
>  
>       12.  
>  
>       13. template<class T>
>  
>       14. class Thread
>  
>       15. {
>  
>       16. public:
>  
>       17.     Thread(const string& threadname,func_t<T> func,T& data)
>  
>       18.     :_tid(0),_threadname(threadname),_isrunning(false),_func(func),_data(data)
>  
>       19.     {}
>  
>       20.  
>  
>       21.     static void* Threadroutine(void* args)//类内成员方法,其第一个参数是this指针,所以会导致编译错误
>  
>       22.    //这里使用static,让Thraedroutine成为类的方法,
>  
>       23.     {
>  
>       24.         (void)args;//仅仅是为了消除警告,变量未使用
>  
>       25.  
>  
>       26.         Thread* ts=static_cast<Thread*>(args);
>  
>       27.         ts->_func(ts->_data);
>  
>       28.         return nullptr;
>  
>       29.     }
>  
>       30.  
>  
>       31.     bool Start()
>  
>       32.     {
>  
>       33.         int n=pthread_create(&_tid,nullptr,Threadroutine,this);//把当前对象传递给线程执行的方法
>  
>       34.         if(n==0)
>  
>       35.         {
>  
>       36.             _isrunning=true;
>  
>       37.             return true;
>  
>       38.         }
>  
>       39.         else return false;
>  
>       40.     }
>  
>       41.  
>  
>       42.     bool Join()
>  
>       43.     {
>  
>       44.         if(!_isrunning)return true;
>  
>       45.         int n=pthread_join(_tid,nullptr);
>  
>       46.         if(n==0)
>  
>       47.         {
>  
>       48.             _isrunning=false;
>  
>       49.             return true;
>  
>       50.         }
>  
>       51.         return false;
>  
>       52.     }
>  
>       53.  
>  
>       54.     bool Isrunning()
>  
>       55.     {
>  
>       56.         return _isrunning;
>  
>       57.     }
>  
>       58.  
>  
>       59.     string Threadname()
>  
>       60.     {
>  
>       61.         return _threadname;
>  
>       62.     }
>  
>       63. private:
>  
>       64.     pthread_t _tid;
>  
>       65.     string _threadname;
>  
>       66.     bool _isrunning;
>  
>       67.     func_t<T> _func;
>  
>       68.     T _data;
>  
>       69. };
>  
>  
>  
>  
> ```
>
>
>
> #### ThreadPool.hpp
>
>
>
> 用于创建线程池
>
>
复制代码
  1. #pragma once
复制代码
  2.  

  3. #include <iostream>

  4. #include <queue>

  5. #include "Log.hpp"

  6. #include "thread.hpp"

  7. #include "Lockguard.hpp"

  8. #include <functional>

  9. #include<unistd.h>

  10. using namespace std;

  11. using namespace std::placeholders;

  12. static int defaultnum=5;

  13.  

  14. Log lg;//全局的日志对象,用于记录线程池的日志信息

  15.  

  16.  

  17. //给线程执行的方法传递的参数

  18. class ThreadData

  19. {

  20.     public:

  21.     ThreadData(const string& threadname)

  22.     :_threadname(threadname)

  23.     {}

  24.     string _threadname;

  25. };

  26.  

  27. template <class T>

  28. class ThreadPool

  29. {

  30. public:

  31.     static ThreadPool<T>* Getinstance()

  32.     {

  33.         {

  34.         Lockguard lockguard(&_mutex_q);

  35.         if(instance==nullptr)

  36.         {

  37.             lg.LogMessage(Info,"单例创建成功...\n");

  38.             instance=new ThreadPool<T>();

  39.         }

  40.         }

  41.         return instance;

  42.     }

  43. private:

  44.     ThreadPool(int thread_num=defaultnum)

  45.         : _thread_num(thread_num)

  46.     {

  47.         pthread_mutex_init(&_mutex,nullptr);

  48.         pthread_cond_init(&_cond,nullptr);

  49.  

  50.         //构建线程

  51.         for(int i=0;i<_thread_num;++i)

  52.         {

  53.             string threadname="thread -";

  54.             threadname+=to_string(i+1);

  55.             ThreadData td(threadname);

  56.             //这里使用了bind绑定成员函数

  57.             Thread<ThreadData> t(threadname, bind(&ThreadPool<T>::ThreadRun,this,_1),td);

  58.             lg.LogMessage(Info,"%s is created ...\n",threadname.c_str());

  59.            _threads.push_back(t);

  60.         }

  61.     }

  62. public:

  63.     //启动线程池,让线程执行自己的方法thread_routine

  64.     bool Start()

  65.     {

  66.         //启动

  67.         for(auto& thread:_threads)

  68.         {

  69.             thread.Start();

  70.             lg.LogMessage(Info,"%s is running...\n",thread.Threadname().c_str());

  71.         }

  72.         return true;

  73.     }

  74.  

  75.     //封装了pthread_cond_wait

  76.     void ThreadWait(const ThreadData &td)

  77.     {

  78.         lg.LogMessage(Debug,"no task,%s is sleeping...\n",td._threadname.c_str());

  79.         pthread_cond_wait(&_cond,&_mutex);

  80.     }

  81.     //封装了pthread_cond_signal

  82.     void ThreadWakeup()

  83.     {

  84.         lg.LogMessage(Debug,"have task\n");

  85.         pthread_cond_signal(&_cond);

  86.     }

  87.     //线程执行的任务

  88.     void ThreadRun(ThreadData& td)

  89.     {

  90.         while(true)

  91.         {

  92.             T t;

  93.             {//这个花括号,为了设置lockguard的生命周期的,这样才可以调用析构函数进行解锁

  94.             Lockguard lockguard(&_mutex);

  95.             //方法1)pthread_mutex_lock(&_mutex);//加锁

  96.             while(_q.empty())//如果任务队列是空的,就不用去拿任务了

  97.             {

  98.                 ThreadWait(td);

  99.                 //pthread_cond_wait(&_cond,&mutex);//让线程阻塞,因为没有任务,然后解锁,让其他线程申请到锁,如果队列还是为空的话,仍然会阻塞....

  100.             }

  101.             t=_q.front();//取任务

  102.             _q.pop();

  103.             //1)pthread_mutex_unlock(&_mutex);

  104.             }

  105.  

  106.             //执行任务

  107.             t();

  108.  

  109.             // lg.LogMessage(Debug,"%s handler %s done , result is :%s\n",\

  110.             // td._threadname.c_str(),t.Printtask().c_str(),t.Printresult().c_str());

  111.             //cout<<"handler done"<<t.Printresult()<<endl;

  112.         }

  113.     }

  114.  

  115. //线程池中插入任务

  116.     void Push( T& in)

  117.     {

  118.         Lockguard lockguard(&_mutex);

  119.         _q.push(in);

  120.         ThreadWakeup();//每次插入任务后,唤醒一个线程

  121.     }

  122.  

  123.     ~ThreadPool()

  124.     {

  125.         pthread_mutex_destroy(&_mutex);

  126.         pthread_cond_destroy(&_cond);

  127.     }

  128.     //for debug

  129.     //主线程等待线程

  130.     void Wait()

  131.     {

  132.         for(auto& thread: _threads)

  133.         {

  134.             thread.Join();

  135.         }

  136.     }

  137. private:

  138.     queue<T> _q;//队列,用于存储线程池中线程要执行的任务

  139.     vector<Thread<ThreadData>> _threads;//线程池其实是一个顺序表类型,里面存储的多线程

  140.     int _thread_num;//创建线程的数量

  141.     pthread_mutex_t _mutex; // 锁

  142.     pthread_cond_t _cond; // 条件变量

  143.  

  144.     //懒汉单例

  145.     static pthread_mutex_t _mutex_q;//单例锁

  146.     static ThreadPool* instance;

  147.  

  148. };

  149.  

  150. template<class T>

  151. ThreadPool<T>* ThreadPool<T>::instance=nullptr;

  152. template<class T>

  153. pthread_mutex_t  ThreadPool<T>::_mutex_q=PTHREAD_MUTEX_INITIALIZER;
复制代码
#### Udpserver.hpp



服务端
复制代码
>       1. #pragma once

>  
>       2. #include "nocopy.hpp"
>  
>       3. // #include "Log.hpp"
>  
>       4. #include <iostream>
>  
>       5. #include <string>
>  
>       6. #include <sys/types.h>
>  
>       7. #include <sys/socket.h>
>  
>       8. #include <cerrno>
>  
>       9. #include <cstring>
>  
>       10. #include <unistd.h>
>  
>       11. #include "Comm.hpp"
>  
>       12. #include <netinet/in.h>
>  
>       13. #include <strings.h>
>  
>       14. #include <arpa/inet.h>
>  
>       15. #include <functional>
>  
>       16. #include "ThreadPool.hpp"
>  
>       17. #include <vector>
>  
>       18. #include <pthread.h>
>  
>       19.  
>  
>       20. #include "InetAddr.hpp"
>  
>       21. using namespace std;
>  
>       22.  
>  
>       23. static const string defaultip = "0.0.0.0";
>  
>       24. static const uint16_t defaultport = 8888;
>  
>       25. static const int defaultfd = -1;
>  
>       26. static const int defaultsize = 1024;
>  
>       27.  
>  
>       28. using task_t = function<void()>;
>  
>       29.  
>  
>       30. class UdpServer : public nocopy
>  
>       31. {
>  
>       32. public:
>  
>       33.     UdpServer(uint16_t port = defaultport, string ip = defaultip)
>  
>       34.         : _ip(ip), _port(port), _sockfd(defaultfd)
>  
>       35.     {
>  
>       36.         pthread_mutex_init(&_user_mutex, nullptr);
>  
>       37.     }
>  
>       38.     void Init()
>  
>       39.     {
>  
>       40.         // 创建套接字
>  
>       41.         _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
>  
>       42.         if (_sockfd < 0)
>  
>       43.         {
>  
>       44.             lg.LogMessage(Fatal, "socket error,%d :%s\n", errno, strerror(errno));
>  
>       45.             exit(Socket_Err);
>  
>       46.         }
>  
>       47.         lg.LogMessage(Info, "socket success ,sockfd:%d \n", _sockfd);
>  
>       48.  
>  
>       49.         // 2.绑定,指定网络信息
>  
>       50.         struct sockaddr_in local;      // 创建套接字地址结构体对象
>  
>       51.         bzero(&local, sizeof(local));  // 初始化local
>  
>       52.         local.sin_family = AF_INET;    // 指定协议族
>  
>       53.         local.sin_port = htons(_port); // 指定端口号(htons的功能就是将我们创建的端口号转成网络子节序)
>  
>       54.         // local.sin_addr.s_addr = inet_addr(_ip.c_str());                    // 指定ip,需要传递整形的ip(inet_addr就是将字符串ip地址转为整形,同时也转为网络子节序)
>  
>       55.         local.sin_addr.s_addr = INADDR_ANY;                                // 指定ip,INADDR_ANY表示本机的任意一个ip地址
>  
>       56.         int n = ::bind(_sockfd, (struct sockaddr *)&local, sizeof(local)); // 将套接字地址结构体绑定到套接字
>  
>       57.         if (n != 0)
>  
>       58.         {
>  
>       59.             lg.LogMessage(Fatal, "bind err ,%d:%s", errno, strerror(errno));
>  
>       60.             exit(Bind_Err);
>  
>       61.         }
>  
>       62.  
>  
>       63.         ThreadPool<task_t>::Getinstance()->Start(); // 启动线程池
>  
>       64.     }
>  
>       65.  
>  
>       66.     void AddOnlineUser(InetAddr addr) // 将addr插入到_online_user中
>  
>       67.     {
>  
>       68.         {
>  
>       69.             Lockguard lockguard(&_user_mutex);
>  
>       70.             // cout << "添加用户" << endl;
>  
>       71.             for (size_t i = 0; i < _online_user.size(); i++)
>  
>       72.             {
>  
>       73.                 if (addr == _online_user[i])
>  
>       74.                 {
>  
>       75.                     // cout << "用户已存在" << endl;
>  
>       76.                     return;
>  
>       77.                 }
>  
>       78.             }
>  
>       79.             // cout << "添加用户成功" << endl;
>  
>       80.             _online_user.push_back(addr);
>  
>       81.             lg.LogMessage(Debug, "add user to onlinelist success, %s:%d\n", addr.Ip().c_str(), addr.Port());
>  
>       82.         }
>  
>       83.     }
>  
>       84.  
>  
>       85.     // 服务器路由
>  
>       86.     void Route(int sock, const string &message)
>  
>       87.     {
>  
>       88.         {
>  
>       89.             Lockguard lockguard(&_user_mutex);
>  
>       90.             for (auto &user : _online_user)
>  
>       91.             {
>  
>       92.                 // cout << "发送给client" << endl;
>  
>       93.                 sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr *)&user.GetAddr(), sizeof(user.GetAddr()));
>  
>       94.                 lg.LogMessage(Debug, "send message to client success, %s:%d\n", user.Ip().c_str(), user.Port());
>  
>       95.             }
>  
>       96.         }
>  
>       97.     }
>  
>       98.     void Start()
>  
>       99.     {
>  
>       100.         // 服务器永远是在循环运行
>  
>       101.         char buffer[defaultsize]; // 创建一个缓冲区
>  
>       102.         for (;;)
>  
>       103.         {
>  
>       104.             struct sockaddr_in peer;                                                                      // 创建一个套接字地址空间,用于存储客户端的套接字地址
>  
>       105.             socklen_t len = sizeof(peer);                                                                 // 获取套接字的地址空间大小
>  
>       106.             ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len); // 用于接收来自客户端的数据,并将其存储在buffer中
>  
>       107.             // cout << "判断服务器收到消息没" << buffer << endl;
>  
>       108.             if (n > 0)
>  
>       109.             {
>  
>       110.                 InetAddr addr(peer);
>  
>       111.                 AddOnlineUser(addr);
>  
>       112.                 buffer[n] = 0;
>  
>       113.  
>  
>       114.                 string message = "[";
>  
>       115.                 message += addr.Ip() + ":" + to_string(addr.Port()) + "]" + "#";
>  
>       116.                 message += buffer;
>  
>       117.  
>  
>       118.                 task_t task = std::bind(&UdpServer::Route, this, _sockfd, message);
>  
>       119.                 ThreadPool<task_t>::Getinstance()->Push(task);
>  
>       120.                 // 处理信息
>  
>       121.                 // string response=_OnMessage(buffer);
>  
>       122.                 // sendto(_sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&peer, len); // 向指定的套接字peer进行接收数据
>  
>       123.             }
>  
>       124.         }
>  
>       125.     }
>  
>       126.  
>  
>       127.     ~UdpServer()
>  
>       128.     {
>  
>       129.         pthread_mutex_destroy(&_user_mutex);
>  
>       130.     }
>  
>       131.  
>  
>       132. private:
>  
>       133.     string _ip;
>  
>       134.     uint16_t _port;
>  
>       135.     int _sockfd;
>  
>       136.  
>  
>       137.     vector<InetAddr> _online_user; // 会被多线程同时访问
>  
>       138.     pthread_mutex_t _user_mutex;   // 锁
>  
>       139. };
>  
>  
>  
>  
> ```
>
>
>
> #### main.cc
>
>
>
> 服务器端
>
>
复制代码
  1. #include"Udpserver.hpp"
复制代码
  2. #include"Comm.hpp"

  3. #include<memory>

  4. #include<stdio.h>

  5.  

  6. using namespace std;

  7.  

  8.  

  9. string OnMessageDefault(string request)

  10. {

  11.     return request+"[haha, got you!!]";

  12. }

  13.  

  14.  

  15. string ExecuteCommand(string command)//我们的处理客户端发来的消息,不一定直接返回字符串,我们还可以让客户端输入shell命令,然后执行

  16. {

  17.     cout<<"get a message :"<<command<<endl;

  18.     FILE* fp=popen(command.c_str(),"r");

  19.     if(fp==nullptr)

  20.     {

  21.         return "execute error, reason is uknown";

  22.     }

  23.  

  24.     string response;

  25.     char buffer[1024]={0};

  26.     while(true)

  27.     {

  28.         char* s=fgets(buffer,sizeof(buffer),fp);

  29.         if(!s) break;//如果读取为空就返回

  30.         else response+=buffer;

  31.     }

  32.  

  33.     pclose(fp);

  34.     return response;

  35. }

  36.  

  37. int main(int argc,char* argv[])

  38. {

  39.     if(argc!=2)

  40.     {

  41.         cout<<"Usage:\n     ./udp_echo_server <port>"<<endl;

  42.         return -1;

  43.     }

  44.  

  45.     // string ip=argv[1];

  46.     uint16_t port=stoi(argv[1]);

  47.  

  48.     //unique_ptr<UdpServer> usvr=make_unique<UdpServer>();???

  49.     //UdpServer* usvr=new UdpServer(OnMessageDefault,port);

  50.     //UdpServer* usvr=new UdpServer(ExecuteCommand,port);

  51.     UdpServer* usvr=new UdpServer(port);

  52.     usvr->Init();

  53.     usvr->Start();

  54.     return 0;

  55. }
复制代码
#### UdpClient.cc



客户端
复制代码
>       1. #include "Log.hpp"

>  
>       2. #include <iostream>
>  
>       3. #include <string>
>  
>       4. #include <sys/types.h>
>  
>       5. #include <sys/socket.h>
>  
>       6. #include <cerrno>
>  
>       7. #include <cstring>
>  
>       8. #include <unistd.h>
>  
>       9. #include "Comm.hpp"
>  
>       10. #include <netinet/in.h>
>  
>       11. #include <strings.h>
>  
>       12. #include <arpa/inet.h>
>  
>       13. #include <cerrno>
>  
>       14. #include"thread.hpp"
>  
>       15. #include"InetAddr.hpp"
>  
>       16.  
>  
>       17. using namespace std;
>  
>       18.  
>  
>       19. class ThreadData
>  
>       20. {
>  
>       21.     public:
>  
>       22.         ThreadData(int sock,struct sockaddr_in& server)
>  
>       23.         :_sockfd(sock),
>  
>       24.         _serveraddr(server)
>  
>       25.         {
>  
>       26.  
>  
>       27.         }
>  
>       28.         ~ThreadData()
>  
>       29.         {
>  
>       30.  
>  
>       31.         }
>  
>       32.     public:
>  
>       33.         int _sockfd;
>  
>       34.         InetAddr _serveraddr;
>  
>       35. };
>  
>       36.  
>  
>       37. void RecvRoutine(ThreadData& td)
>  
>       38. {
>  
>       39.     char buffer[4096];
>  
>       40.     while(true)
>  
>       41.     {
>  
>       42.         //收消息
>  
>       43.  
>  
>       44.             struct sockaddr_in temp;
>  
>       45.             socklen_t len = sizeof(temp);
>  
>       46.             size_t n = recvfrom(td._sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &len);
>  
>       47.  
>  
>       48.             if (n > 0)
>  
>       49.             {
>  
>       50.                 buffer[n] = 0;
>  
>       51.                 cerr<<buffer << endl;
>  
>       52.             }
>  
>       53.             else
>  
>       54.                 break;
>  
>       55.     }
>  
>       56. };
>  
>       57.  
>  
>       58. void SendRoutine(ThreadData& td)
>  
>       59. {
>  
>       60.     while(true)
>  
>       61.     {
>  
>       62.         // 我们要发的数据
>  
>       63.         string inbuffer;
>  
>       64.         cout << "Please Enter#:";
>  
>       65.         getline(cin, inbuffer);
>  
>       66.         //cout << inbuffer << endl;
>  
>       67.  
>  
>       68.         // 我们要发给谁?server
>  
>       69.         auto server=td._serveraddr.GetAddr();
>  
>       70.  
>  
>       71.         int n = sendto(td._sockfd, inbuffer.c_str(), inbuffer.size(), 0, (struct sockaddr *)&server, sizeof(server));
>  
>       72.         if(n<=0)
>  
>       73.         cout<<"send error"<<endl;
>  
>       74.     }
>  
>       75. };
>  
>       76.  
>  
>       77. int main(int argc, char *argv[])
>  
>       78. {
>  
>       79.     if (argc != 3)
>  
>       80.     {
>  
>       81.         cout << "Usage:\n     ./udp_echo_client <ip> <port>" << endl;
>  
>       82.         return -1;
>  
>       83.     }
>  
>       84.  
>  
>       85.     string serverip = argv[1];           // 服务器ip
>  
>       86.     uint16_t serverport = atoi(argv[2]); // 服务器端口号
>  
>       87.  
>  
>       88.     // 创建套接字
>  
>       89.     int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
>  
>       90.     if (sockfd < 0)
>  
>       91.     {
>  
>       92.         cerr << "socket error" << strerror(errno) << endl;
>  
>       93.         return 1;
>  
>       94.     }
>  
>       95.  
>  
>       96.     cout << "client create socket success:" << sockfd << endl;
>  
>       97.     // 2.客户端也需要绑定套接字空间,但是,不需要显示的绑定,client会在首次发送数据的时候自动绑定
>  
>       98.     // 服务器的端口号,一定众所周知的,不可以随意改变,client需要port,客户端需要绑定随机端口
>  
>       99.     // 因为客户端非常多,所以客户端需要绑定随机端口
>  
>       100.  
>  
>       101.     // 2.1填充一下server的信息
>  
>       102.     struct sockaddr_in server;
>  
>       103.     memset(&server, 0, sizeof(server));
>  
>       104.     server.sin_family = AF_INET;
>  
>       105.     server.sin_port = htons(serverport);
>  
>       106.     server.sin_addr.s_addr = inet_addr(serverip.c_str());
>  
>       107.  
>  
>       108.     ThreadData td(sockfd,server);
>  
>       109.     Thread<ThreadData>recver("recver",RecvRoutine,td);
>  
>       110.     Thread<ThreadData>sender("sender",SendRoutine,td);
>  
>       111.  
>  
>       112.    recver.Start();
>  
>       113.    sender.Start();
>  
>       114.  
>  
>       115.  
>  
>       116.     recver.Join();//主线程等待子线程
>  
>       117.     sender.Join();
>  
>       118.  
>  
>       119.  
>  
>       120.     close(sockfd);
>  
>       121.     return 0;
>  
>       122. }
>  
>  
>  
>  
> ```
>
>
>
> #### Makefile
>
>
>
> 工程管理,形成客户端和服务端
>
>
复制代码
  1. .PHONY:all
复制代码
  2. all:udp_server udp_client

  3.  

  4. udp_server:main.cc

  5.      g++ -o $@ $^ -std=c++11 -lpthread

  6.  

  7. udp_client:UdpClient.cc

  8.      g++ -o $@ $^ -std=c++11 -lpthread

  9.  

  10. .PHONY:clean

  11. clean:

  12.     rm -f udp_server udp_client
复制代码
#### 运行结果



首先编译形成客户端和服务端
复制代码
>     make
>  
> ```
>
>
>
> 运行服务端
>
>
复制代码
  1. bch@hcss-ecs-6176:~/linux/4_3/UDPsever/udp_echo_server_chat.4.02$ ./udp_server 8888
复制代码
  2. [Info][2024年-5月-24日 15时:28分:36秒][93230]socket success ,sockfd:3

  3. [Info][2024年-5月-24日 15时:28分:36秒][93230]单例创建成功...

  4. [Info][2024年-5月-24日 15时:28分:36秒][93230]thread -1 is created ...

  5. [Info][2024年-5月-24日 15时:28分:36秒][93230]thread -2 is created ...

  6. [Info][2024年-5月-24日 15时:28分:36秒][93230]thread -3 is created ...

  7. [Info][2024年-5月-24日 15时:28分:36秒][93230]thread -4 is created ...

  8. [Info][2024年-5月-24日 15时:28分:36秒][93230]thread -5 is created ...

  9. [Info][2024年-5月-24日 15时:28分:36秒][93230]thread -1 is running...

  10. [Info][2024年-5月-24日 15时:28分:36秒][93230]thread -2 is running...

  11. [Info][2024年-5月-24日 15时:28分:36秒][93230]thread -3 is running...

  12. [Info][2024年-5月-24日 15时:28分:36秒][93230]thread -4 is running...

  13. [Info][2024年-5月-24日 15时:28分:36秒][93230]thread -5 is running...

  14. [Debug][2024年-5月-24日 15时:28分:36秒][93230]no task,thread -3 is sleeping...

  15. [Debug][2024年-5月-24日 15时:28分:36秒][93230]no task,thread -2 is sleeping...

  16. [Debug][2024年-5月-24日 15时:28分:36秒][93230]no task,thread -1 is sleeping...

  17. [Debug][2024年-5月-24日 15时:28分:36秒][93230]no task,thread -5 is sleeping...

  18. [Debug][2024年-5月-24日 15时:28分:36秒][93230]no task,thread -4 is sleeping...
复制代码
启动客户端

🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸

全部评论 (0)

还没有任何评论哟~