阅读量:0
目录
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博客
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; }
远程执行命令!!!
参考如下:
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