久综合色-久综合网-玖草影视-玖草资源在线-亚洲黄色片子-亚洲黄色片在线观看

Hello! 歡迎來到小浪云!


Linux__之__基于UDP的Socket編程網絡通信


前言

本文旨在通過Linux系統接口實現網絡通信,幫助我們更好地掌握socket套接字的使用。通過學習socket網絡通信,我們將發現網絡通信的本質不過是套路。接下來,讓我們直接進入代碼編寫部分。

  1. 事先準備

今天我們將模擬實現一個echo demo,即客戶端向服務器發送信息,服務器接收并回顯這些信息。為了提高代碼的可讀性和調試性,我們將使用日志信息。我將帶領大家手動編寫日志代碼,并將其應用于echo demo中。在日志中,如果需要訪問臨界資源,我們需要進行加鎖和解鎖操作。這里我將引導大家基于Linux系統調用封裝鎖,使得鎖的使用更加便捷。

1.1 Mutex.hpp

想要封裝鎖,我們首先需要了解鎖的概念。簡而言之,鎖是原子性的操作,用于保護在多線程環境下共享資源的安全。鎖的定義有兩種方式:一種是使用宏進行全局初始化,無需手動釋放,由操作系統自動釋放;另一種是局部定義并使用init進行初始化。我們將使用init初始化方法,第一個參數是鎖,第二個參數為鎖的屬性,默認為nullptr。銷毀時使用destroy系統調用。我們將這些操作封裝在一個LockGuard類中,利用對象的特性,離開局部作用域時自動釋放,進一步簡化鎖的使用。

#pragma once #include <iostream> #include <pthread.h> namespace LockMoudle {     class Mutex {     public:         Mutex(const Mutex&) = delete;         const Mutex& operator=(const Mutex&) = delete;         Mutex() {             int n = ::pthread_mutex_init(&_lock, nullptr);             (void)n;         }         ~Mutex() {             int n = ::pthread_mutex_destroy(&_lock);             (void)n;         }         void Lock() {             //加鎖             int n = pthread_mutex_lock(&_lock);             (void)n;         }         //獲取鎖         pthread_mutex_t *LockPtr() {             return &_lock;         }         //解鎖         void Unlock() {             int n = ::pthread_mutex_unlock(&_lock);             (void)n;         }     private:         pthread_mutex_t _lock;     };     class LockGuard {     public:         LockGuard(Mutex &mtx)         :_mtx(mtx)         {             _mtx.Lock();         }         ~LockGuard() {             _mtx.Unlock();         }     private:         Mutex &_mtx;     }; }

1.2 Log.hpp

在日志類中,如果使用文件策略,為了防止多線程并發訪問和創建多個文件,我們需要進行加鎖,確保一次只有一個線程訪問。

首先明確日志策略,是刷新到文件緩沖區還是命令行緩沖區。我們定義基類,使用子類繼承基類的虛方法實現多態,并使用內部類創建日志消息,然后調用外部類的策略方法進行打印。

Linux__之__基于UDP的Socket編程網絡通信Linux__之__基于UDP的Socket編程網絡通信

#pragma once #include <iostream> #include <cstdio> #include <string> #include <fstream> #include <sstream> #include <memory> #include <filesystem> //c++17 #include <unistd.h> #include <time.h> #include "Mutex.hpp" namespace LogModule {     using namespace LockMoudle;     //獲取當前系統時間     std::string CurrentTime() {         time_t time_stamp = ::time(nullptr);         struct tm curr;         localtime_r(&time_stamp, &curr); //時間戳, 獲取可讀性較強的時間信息         char buffer[1024];         snprintf(buffer, sizeof(buffer), "%4d-%02d-%02d %02d:%02d:%02d",             curr.tm_year + 1900,             curr.tm_mon + 1,             curr.tm_mday,             curr.tm_hour,             curr.tm_min,             curr.tm_sec);         return buffer;     }     //構成: 1. 構建日志字符串 2.刷新落盤     //落盤策略(screen, file)     //1.日志文件的默認路徑和文件名     const std::string defaultlogpath = "./log/";     const std::string defaultlogname = "log.txt";     //2.日志等級     enum class LogLevel {         DEBUG = 1,         INFO,         WARNING,         ERROR,         FATAL     };     //枚舉類型轉字符串     std::string Level2String(LogLevel level) {         switch (level) {         case LogLevel::DEBUG:             return "DEBUG";         case LogLevel::INFO:             return "INFO";         case LogLevel::WARNING:             return "WARNING";         case LogLevel::ERROR:             return "ERROR";         case LogLevel::FATAL:             return "FATAL";         default:             return "None";         }     }     //3.刷新策略     class LogStrategy {     public:         virtual ~LogStrategy() = default; //虛析構函數,多態,能夠正確調用對象進行析構, 編譯器自動生成         virtual void SyncLog(const std::string &message) = 0;//純虛函數,子類必須手動實現     };     //3.1控制臺策略     class ConsoleLogStrategy : public LogStrategy {     public:         ConsoleLogStrategy() {}         ~ConsoleLogStrategy() {}         //向控制臺打印日志信息message         void SyncLog(const std::string &message) {             LockGuard lockguard(_lock);             std::cout << message << std::endl;         }     private:         Mutex _lock;     };     //3.2文件策略     class FileLogStrategy : public LogStrategy {     public:         FileLogStrategy(const std::string &path = defaultlogpath, const std::string &name = defaultlogname)         : _path(path), _name(name) {             _file.open(_path + _name, std::ios::app);         }         ~FileLogStrategy() {             if(_file.is_open()) {                 _file.close();             }         }         //向文件中寫入日志信息message         void SyncLog(const std::string &message) {             LockGuard lockguard(_lock);             _file << message << std::endl;             _file.flush();         }     private:         std::string _path;         std::string _name;         std::ofstream _file;         Mutex _lock;     };     //4.日志記錄器     class Logger {     public:         Logger() : _strategy(nullptr) {}         void EnableConsolelog() {             _strategy = std::make_shared<ConsoleLogStrategy>();         }         void EnableFileLog() {             _strategy = std::make_shared<FileLogStrategy>();         }         ~Logger(){}         //一條完整的信息[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16] + 日志的可變部分(         class LogMessage {         public:             LogMessage(LogLevel level, const std::string &filename, int line, Logger &logger)             : _currtime(CurrentTime())             , _level(level)             , _pid(::getpid())             , _filename(filename)             , _line(line)             , _logger(logger)             {}             //重載operator<<, 記錄日志信息             template<typename T>             LogMessage& operator<<(const T &data) {                 std::ostringstream oss;                 oss << data;                 _loginfo += oss.str();                 return *this;             }             //同步日志信息             ~LogMessage() {                 std::ostringstream oss;                 oss << "[" << _currtime << "] [" << Level2String(_level) << "] [" << _pid << "] [" << _filename << "] [" << _line << "] " << _loginfo;                 _logger.SyncLog(oss.str());             }         private:             std::string _currtime; //當前日志的時間             LogLevel _level; //日志等級             pid_t _pid; //進程pid             std::string _filename; //源文件             int _line; //行號             Logger &_logger; //策略             std::string _loginfo; //日志信息         };         //重載operator(), 故意的拷貝         LogMessage operator()(LogLevel level, const std::string &filename, int line) {             return LogMessage(level, filename, line, *this);         }     private:         std::shared_ptr<LogStrategy> _strategy;     };     Logger logger;     #define LOG(Level) logger(Level, __FILE__, __LINE__)     #define ENABLE_CONSOLE_LOG() logger.EnableConsolelog()     #define ENABLE_FILE_LOG() logger.EnableFileLog() }
  1. 編寫Echo demo代碼

2.1 udpServer.hpp 和 UdpServer.cc

這里我們使用套接字進行通信,套接字可以簡單理解為一個文件流。創建套接字后填寫網絡信息,并與內核綁定。由于我們使用的是云服務器,默認不需要綁定IP,因此我們只需綁定端口號,從命令行獲取。

#include "UdpServer.hpp" int main(int argc, char *argv[]) {     if(argc != 2) {         std::cerr << "Usage: " << argv[0] << " <port>" << std::endl;         Die(USAGE_ERR);     }     uint16_t port = static_cast<uint16_t>(std::atoi(argv[1]));     std::unique_ptr<UdpServer> svr_uptr = std::make_unique<UdpServer>(port);     svr_uptr->InitServer();     svr_uptr->Start();     return 0; }
#pragma once #include <iostream> #include <string> #include <memory> #include <cstring> #include <cerrno> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include "InetAddr.hpp" #include "Log.hpp" #include "Common.hpp" using namespace LogModule; const static int gsockfd = -1; //const static std::string gdefaultip = "127.0.0.1" //表示本地主機 const static uint16_t gdefaultport = 8080; class UdpServer { public:     //命令行輸入ip + 端口號進行綁定, 虛擬機無需綁定ip, 只需指定端口號進行綁定即可     UdpServer(uint16_t port = gdefaultport)         : _sockfd(gsockfd)         , _addr(port)         , _isrunning(false)         {}     //都是套路     void InitServer() {         //1.創建套接字         _sockfd = ::socket(AF_INET, SOCK_DGRAM, 0); //指定網絡通信模式. 面向數據包, 標記為設置為0         if(_sockfd < 0) {             LOG(LogLevel::ERROR) << "socket error: " << strerror(errno);             Die(SOCKET_ERR);         }         //2.綁定套接字         if(::bind(_sockfd, _addr.Netaddr(), _addr.NetAddrlen()) < 0) {             LOG(LogLevel::ERROR) << "bind error: " << strerror(errno);             Die(BIND_ERR);         }         _isrunning = true;     }     void Start() {         char inbuffer[1024];         struct sockaddr_in peer;         socklen_t peerlen = sizeof(peer);         while(_isrunning) {             memset(inbuffer, 0, sizeof(inbuffer));             int n = ::recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&peer, &peerlen);             if(n < 0) {                 LOG(LogLevel::ERROR) << "recvfrom error: " << strerror(errno);                 continue;             }             InetAddr cli(peer);             inbuffer[n] = 0;             std::string clientinfo = cli.Ip() + ":" + std::to_string(cli.Port()) + '#' + inbuffer;             LOG(LogLevel::DEBUG) << "recvfrom client: " << clientinfo;             //回顯信息             n = ::sendto(_sockfd, inbuffer, n, 0, (struct sockaddr*)&peer, peerlen);             if(n < 0) {                 LOG(LogLevel::ERROR) << "sendto error: " << strerror(errno);             }         }     }     ~UdpServer() {         if(_sockfd != gsockfd)             ::close(_sockfd);     } private:     int _sockfd;     InetAddr _addr;     bool _isrunning; };

2.2 IntAddr.hpp 和 Commm.hpp

這里對IntAddr進行了封裝,IntAddr包含了網絡信息。網絡通信中,我們需要對InetAddr進行強轉,實現c語言版本的多態。

#pragma once #include <iostream> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include "Common.hpp" class InetAddr { private:     void PortNet2Host() {         _port = ::ntohs(_net_addr.sin_port);     }     void IpNet2Host() {         char ipbuffer[64];         const char *ip = ::inet_ntop(AF_INET,&_net_addr.sin_addr,ipbuffer, sizeof(ipbuffer));         (void)ip;     } public:     InetAddr(){}     //如果傳進來的是一個sockaddr_in, 網絡轉主機     InetAddr(const struct sockaddr_in &addr) : _net_addr(addr) {         PortNet2Host();         IpNet2Host();     }     //如果傳進來的是端口號, 就轉化為網絡, 服務器不需要自己綁定ip     InetAddr(uint16_t port) : _port(port), _ip("") {         _net_addr.sin_family = AF_INET;         _net_addr.sin_port = htons(_port);         _net_addr.sin_addr.s_addr = INADDR_ANY;     }     struct sockaddr* Netaddr() {return CONV(&_net_addr); }     socklen_t NetAddrlen() {return sizeof(_net_addr); }     std::string Ip() {return _ip; }     uint16_t Port() {return _port; }     ~InetAddr(){} private:     struct sockaddr_in _net_addr;     std::string _ip;     uint16_t _port; };

Comman.hpp

#pragma once #include<iostream> #define Die(code) do {exit(code); } while(0) #define CONV(v) (struct sockaddr *)(v) enum{     USAGE_ERR = 1,     SOCKET_ERR,     BIND_ERR };

2.3 Client.cc

客戶端通過標準輸入獲取信息并發送到服務器,然后接收并打印服務器回顯的內容。

#include "UdpClient.hpp" #include "Common.hpp" #include <iostream> #include <cstring> #include <string> #include <cstdlib> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main(int argc, char *argv[]) {     if(argc != 3) {         std::cerr << "Usage: " << argv[0] << " <ip> <port>" << std::endl;         Die(USAGE_ERR);     }     std::string ip = argv[1];     uint16_t port = static_cast<uint16_t>(std::atoi(argv[2]));     UdpClient client(ip, port);     client.InitClient();     char buffer[1024];     while(true) {         std::cout << "請輸入要發送的信息: ";         std::cin.getline(buffer, sizeof(buffer));         if(strcmp(buffer, "quit") == 0) {             break;         }         client.Send(buffer);         int n = client.Recv(buffer, sizeof(buffer) - 1);         if(n > 0) {             buffer[n] = 0;             std::cout << "服務器回顯: " << buffer << std::endl;         }     }     return 0; }
  1. 運行結果

Linux__之__基于UDP的Socket編程網絡通信

相關閱讀

主站蜘蛛池模板: 欧美亚洲日本一区二区三区浪人 | 美女三级毛片 | 久久九九精品一区二区 | 日本久久一区二区 | 成年人午夜影院 | 国产成人一区二区三区影院免费 | 国产午夜亚洲精品一区网站 | 欧美精品一二区 | 97久久精品一区二区三区 | 美美女高清毛片视频黄的一免费 | 怡红院美国十次成人影院 | 99久久精品国产9999高清 | 黑人边吃奶边扎下面激情视频 | 日本一区二区三区在线 视频 | 日本激情视频在线观看 | 欧美日韩国产亚洲综合不卡 | 中文国产成人精品少久久 | 狠狠色综合久久丁香婷婷 | 久久香蕉精品视频 | 波多野结衣一区在线观看 | 99精品国产高清一区二区三区香蕉 | 99在线国产| 蜜桃欧美性大片 | 国产精品推荐 | 国产日韩精品一区二区 | 国产精品久久免费观看 | 欧美毛片性视频区 | 在线欧美视频 | 91情侣高清精品国产 | 视频二区国产 | 国产精品久久视频 | 国产成人精品免费视频 | 成人午夜兔费观看网站 | 波多野一区二区三区在线 | 国产自制一区 | 国产精品亚洲片夜色在线 | 中文 日本 免费 高清 | 美国黑人特大一级毛片 | 成人久久18免费游戏网站 | 在线亚洲日产一区二区 | 亚洲一级毛片欧美一级说乱 |