网络6 -- udp_socket 实现 echo服务器

avatar
作者
猴君
阅读量:0

目录

1.server 服务端

1.1.完整代码展示:

1.2.代码解析:

1.2.1 给服务端创建套接字

1.2.2 绑定套接字

1.2.3 服务端接受数据并返回

2.客户端:

2.1 完整代码展示:

2.2 代码解析

 2.2.1 客户端使用手则:

2.2.2 创建客户端套接字

2.2.3 关于客户端是否绑定问题

2.2.4 客户端传输信息给服务端

3.主函数

3.1完整代码

3.2代码解析

3.2.1  回调函数

3.2.2 功能拓展

4.log(日志代码)

5.makefile代码:


1.server 服务端

1.1.完整代码展示:

#pragma once  #include <iostream> #include <string> #include <strings.h> #include <cstring> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <functional> #include "Log.hpp"  // using func_t = std::function<std::string(const std::string&)>; typedef std::function<std::string(const std::string&)> func_t;  Log lg;  enum{     SOCKET_ERR=1,     BIND_ERR };  uint16_t defaultport = 8080; std::string defaultip = "0.0.0.0"; const int size = 1024;  class UdpServer{ public:     UdpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip):sockfd_(0), port_(port), ip_(ip),isrunning_(false)     {}     void Init()     {         // 1. 创建udp socket         sockfd_ = socket(AF_INET, SOCK_DGRAM, 0); // PF_INET         if(sockfd_ < 0)         {             lg(Fatal, "socket create error, sockfd: %d", sockfd_);             exit(SOCKET_ERR);         }         lg(Info, "socket create success, sockfd: %d", sockfd_);         // 2. bind socket         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()); //1. string -> uint32_t 2. uint32_t必须是网络序列的 // ??         // local.sin_addr.s_addr = htonl(INADDR_ANY);          if(bind(sockfd_, (const struct sockaddr *)&local, sizeof(local)) < 0)         {             lg(Fatal, "bind error, errno: %d, err string: %s", errno, strerror(errno));             exit(BIND_ERR);         }         lg(Info, "bind success, errno: %d, err string: %s", errno, strerror(errno));     }     void Run(func_t func) // 对代码进行分层     {         isrunning_ = true;         char inbuffer[size];         while(isrunning_)         {             struct sockaddr_in client;             socklen_t len = sizeof(client);             ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);             if(n < 0)             {                 lg(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));                 continue;             }             inbuffer[n] = 0;             std::string info = inbuffer;             std::string echo_string = func(info);             sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (const sockaddr*)&client, len);         }     }     ~UdpServer()     {         if(sockfd_>0) close(sockfd_);     } private:     int sockfd_;     // 网路文件描述符     std::string ip_; // 任意地址bind 0     uint16_t port_;  // 表明服务器进程的端口号     bool isrunning_; };

1.2.代码解析:

1.2.1 给服务端创建套接字

// 1. 创建udp socket         sockfd_ = socket(AF_INET, SOCK_DGRAM, 0); // PF_INET         if(sockfd_ < 0)         {             lg(Fatal, "socket create error, sockfd: %d", sockfd_);             exit(SOCKET_ERR);         }         lg(Info, "socket create success, sockfd: %d", sockfd_);         // 2. bind socket         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()); //1. string -> uint32_t 2. uint32_t必须是网络序列的 // ??         // local.sin_addr.s_addr = htonl(INADDR_ANY);          

创建套接字时,我们要将字符型的port和ip转化为网络序列 

参考如下:

整数ip和字符串ip之间的相互转化()_uint ip转string-CSDN博客

inet_addr()原理-CSDN博客

1.2.2 绑定套接字

if(bind(sockfd_, (const struct sockaddr *)&local, sizeof(local)) < 0)         {             lg(Fatal, "bind error, errno: %d, err string: %s", errno, strerror(errno));             exit(BIND_ERR);         }         lg(Info, "bind success, errno: %d, err string: %s", errno, strerror(errno));     }

绑定套接字应注意:ip和port可能绑定失败:

参考如下:

关于bind绑定时出现 Permission denied_bind: permission denied-CSDN博客

云服务器不允许绑定(bind)公网ip_bind主机-CSDN博客

1.2.3 服务端接受数据并返回

 void Run(func_t func) // 对代码进行分层     {         isrunning_ = true;         char inbuffer[size];         while(isrunning_)         {             struct sockaddr_in client;             socklen_t len = sizeof(client);             ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);             if(n < 0)             {                 lg(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));                 continue;             }             inbuffer[n] = 0;             std::string info = inbuffer;             std::string echo_string = func(info);             sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (const sockaddr*)&client, len);         }     }     ~UdpServer()     {         if(sockfd_>0) close(sockfd_);     }

         使用 func() 函数回调,从main()函数中获取数据

2.客户端:

2.1 完整代码展示:

#include <iostream> #include <cstdlib> #include <unistd.h> #include <strings.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>  using namespace std;  void Usage(std::string proc) {     std::cout << "\n\rUsage: " << proc << " serverip serverport\n"               << std::endl; }  // ./udpclient serverip serverport int main(int argc, char *argv[]) {     if (argc != 3)     {         Usage(argv[0]);         exit(0);     }     std::string serverip = argv[1];     uint16_t serverport = std::stoi(argv[2]);      struct sockaddr_in server;     bzero(&server, sizeof(server));     server.sin_family = AF_INET;     server.sin_port = htons(serverport); //?     server.sin_addr.s_addr = inet_addr(serverip.c_str());     socklen_t len = sizeof(server);      int sockfd = socket(AF_INET, SOCK_DGRAM, 0);     if (sockfd < 0)     {         cout << "socker error" << endl;         return 1;     }      // client 要bind吗?要!只不过不需要用户显示的bind!一般有OS自由随机选择!     // 一个端口号只能被一个进程bind,对server是如此,对于client,也是如此!     // 其实client的port是多少,其实不重要,只要能保证主机上的唯一性就可以!     // 系统什么时候给我bind呢?首次发送数据的时候      string message;     char buffer[1024];     while (true)     {         cout << "Please Enter@ ";         getline(cin, message);          // std::cout << message << std::endl;         // 1. 数据 2. 给谁发         sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, len);                  struct sockaddr_in temp;         socklen_t len = sizeof(temp);          ssize_t s = recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len);         if(s > 0)         {             buffer[s] = 0;             cout << buffer << endl;         }     }      close(sockfd);     return 0; }

2.2 代码解析

 2.2.1 客户端使用手则:

void Usage(std::string proc) {     std::cout << "\n\rUsage: " << proc << " serverip serverport\n"               << std::endl; }  // ./udpclient serverip serverport int main(int argc, char *argv[]) {     if (argc != 3)     {         Usage(argv[0]);         exit(0);     }     std::string serverip = argv[1];     uint16_t serverport = std::stoi(argv[2]);

2.2.2 创建客户端套接字

 struct sockaddr_in server;     bzero(&server, sizeof(server));     server.sin_family = AF_INET;     server.sin_port = htons(serverport); //?     server.sin_addr.s_addr = inet_addr(serverip.c_str());     socklen_t len = sizeof(server);      int sockfd = socket(AF_INET, SOCK_DGRAM, 0);     if (sockfd < 0)     {         cout << "socker error" << endl;         return 1;     } 

2.2.3 关于客户端是否绑定问题

        (1)client 要bind吗?要!只不过不需要用户显示的bind!一般有OS自由随机选择!
        (2)一个端口号只能被一个进程bind,对server是如此,对于client,也是如此!

        (3)其实client的port是多少,其实不重要,只要能保证主机上的唯一性就可以!
        (4)系统什么时候给我bind呢?首次发送数据的时候

参考如下:

           客户端需要bind吗?(端口号绑定的理解)_客戶端bind-CSDN博客

2.2.4 客户端传输信息给服务端

 string message;     char buffer[1024];     while (true)     {         cout << "Please Enter@ ";         getline(cin, message);          // std::cout << message << std::endl;         // 1. 数据 2. 给谁发         sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, len);                  struct sockaddr_in temp;         socklen_t len = sizeof(temp);          ssize_t s = recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len);         if(s > 0)         {             buffer[s] = 0;             cout << buffer << endl;         }     }      close(sockfd);     return 0; }

3.主函数

3.1完整代码

#include "UdpServer.hpp" #include <memory> #include <cstdio>  // "120.78.126.148" 点分十进制字符串风格的IP地址  void Usage(std::string proc) {     std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl; }  std::string Handler(const std::string &str) {     std::string res = "Server get a message: ";     res += str;     std::cout << res << std::endl;      // pid_t id = fork();     // if(id == 0)     // {     //     // ls -a -l -> "ls" "-a" "-l"     //     // exec*();     // }     return res; }  std::string ExcuteCommand(const std::string &cmd) {     // SafeCheck(cmd);      FILE *fp = popen(cmd.c_str(), "r");     if(nullptr == fp)     {         perror("popen");         return "error";     }     std::string result;     char buffer[4096];     while(true)     {         char *ok = fgets(buffer, sizeof(buffer), fp);         if(ok == nullptr) break;         result += buffer;     }     pclose(fp);      return result; }  // ./udpserver port int main(int argc, char *argv[]) {     if(argc != 2)     {         Usage(argv[0]);         exit(0);     }      uint16_t port = std::stoi(argv[1]);      std::unique_ptr<UdpServer> svr(new UdpServer(port));      svr->Init(/**/);     svr->Run(Handler);      return 0; }

3.2代码解析

3.2.1  回调函数

std::string Handler(const std::string &str) {     std::string res = "Server get a message: ";     res += str;     std::cout << res << std::endl;      // pid_t id = fork();     // if(id == 0)     // {     //     // ls -a -l -> "ls" "-a" "-l"     //     // exec*();     // }     return res; }

服务端接受数据返回时的回调函数fun():Handler

3.2.2 功能拓展

std::string ExcuteCommand(const std::string &cmd) {     // SafeCheck(cmd);      FILE *fp = popen(cmd.c_str(), "r");     if(nullptr == fp)     {         perror("popen");         return "error";     }     std::string result;     char buffer[4096];     while(true)     {         char *ok = fgets(buffer, sizeof(buffer), fp);         if(ok == nullptr) break;         result += buffer;     }     pclose(fp);      return result; }

远程执行命令!!!

参考如下:

popen()-CSDN博客


4.log(日志代码)

#pragma once  #include <iostream> #include <time.h> #include <stdarg.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h>  #define SIZE 1024  #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 LogFile "log.txt"  class Log { public:     Log()     {         printMethod = Screen;         path = "./log/";     }     void Enable(int method)     {         printMethod = method;     }     std::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 logmessage(int level, const char *format, ...)     // {     //     time_t t = time(nullptr);     //     struct tm *ctime = localtime(&t);     //     char leftbuffer[SIZE];     //     snprintf(leftbuffer, sizeof(leftbuffer), "[%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];     //     vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);     //     // va_end(s);      //     // 格式:默认部分+自定义部分     //     char logtxt[SIZE * 2];     //     snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);      //     // printf("%s", logtxt); // 暂时打印     //     printLog(level, logtxt);     // }     void printLog(int level, const std::string &logtxt)     {         switch (printMethod)         {         case Screen:             std::cout << logtxt << std::endl;             break;         case Onefile:             printOneFile(LogFile, logtxt);             break;         case Classfile:             printClassFile(level, logtxt);             break;         default:             break;         }     }     void printOneFile(const std::string &logname, const std::string &logtxt)     {         std::string _logname = path + logname;         int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"         if (fd < 0)             return;         write(fd, logtxt.c_str(), logtxt.size());         close(fd);     }     void printClassFile(int level, const std::string &logtxt)     {         std::string filename = LogFile;         filename += ".";         filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"         printOneFile(filename, logtxt);     }      ~Log()     {     }     void operator()(int level, const char *format, ...)     {         time_t t = time(nullptr);         struct tm *ctime = localtime(&t);         char leftbuffer[SIZE];         snprintf(leftbuffer, sizeof(leftbuffer), "[%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];         vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);         va_end(s);          // 格式:默认部分+自定义部分         char logtxt[SIZE * 2];         snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);          // printf("%s", logtxt); // 暂时打印         printLog(level, logtxt);     }  private:     int printMethod;     std::string path; };  // int sum(int n, ...) // { //     va_list s; // char* //     va_start(s, n);  //     int sum = 0; //     while(n) //     { //         sum += va_arg(s, int); // printf("hello %d, hello %s, hello %c, hello %d,", 1, "hello", 'c', 123); //         n--; //     }  //     va_end(s); //s = NULL //     return sum; // }

5.makefile代码:

.PHONY:all all:udpserver udpclient  udpserver:Main.cc 	g++ -o $@ $^ -std=c++11 udpclient:UdpClient.cc 	g++ -o $@ $^ -std=c++11   .PHONY:clean clean: 	rm -f udpserver udpclient

    广告一刻

    为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!