Linux第三十九章
发布时间
阅读量:
阅读量
🐶博主主页:[@ᰔᩚ. 一怀明月ꦿ]( "@ᰔᩚ. 一怀明月ꦿ")
❤️🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++,linux
🔥座右铭:“不要等到什么都没有了,才下定决心去做”
🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀
目录
popen
sz和rz
简单的聊天室
Comm.hpp
InetAddr.hpp
Lockguard.hpp
Log.hpp
nocopy.hpp
thread.hpp
ThreadPool.hpp
Udpserver.hpp
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)
还没有任何评论哟~
