九色91_成人精品一区二区三区中文字幕_国产精品久久久久一区二区三区_欧美精品久久_国产精品99久久久久久久vr_www.国产视频

Hello! 歡迎來到小浪云!


Linux__之__基于UDP的Socket編程網(wǎng)絡(luò)通信


avatar
小浪云 2025-04-18 24

前言

本文旨在通過Linux系統(tǒng)接口實現(xiàn)網(wǎng)絡(luò)通信,幫助我們更好地掌握socket套接字的使用。通過學(xué)習(xí)socket網(wǎng)絡(luò)通信,我們將發(fā)現(xiàn)網(wǎng)絡(luò)通信的本質(zhì)不過是套路。接下來,讓我們直接進入代碼編寫部分。

  1. 事先準(zhǔn)備

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

1.1 Mutex.hpp

想要封裝鎖,我們首先需要了解鎖的概念。簡而言之,鎖是原子性的操作,用于保護在多線程環(huán)境下共享資源的安全。鎖的定義有兩種方式:一種是使用宏進行全局初始化,無需手動釋放,由操作系統(tǒng)自動釋放;另一種是局部定義并使用init進行初始化。我們將使用init初始化方法,第一個參數(shù)是鎖,第二個參數(shù)為鎖的屬性,默認(rèn)為nullptr。銷毀時使用destroy系統(tǒng)調(diào)用。我們將這些操作封裝在一個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

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

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

Linux__之__基于UDP的Socket編程網(wǎng)絡(luò)通信Linux__之__基于UDP的Socket編程網(wǎng)絡(luò)通信

#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;     //獲取當(dāng)前系統(tǒng)時間     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;     }     //構(gòu)成: 1. 構(gòu)建日志字符串 2.刷新落盤     //落盤策略(screen, file)     //1.日志文件的默認(rèn)路徑和文件名     const std::string defaultlogpath = "./log/";     const std::string defaultlogname = "log.txt";     //2.日志等級     enum class LogLevel {         DEBUG = 1,         INFO,         WARNING,         ERROR,         FATAL     };     //枚舉類型轉(zhuǎn)字符串     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; //虛析構(gòu)函數(shù),多態(tài),能夠正確調(diào)用對象進行析構(gòu), 編譯器自動生成         virtual void SyncLog(const std::string &message) = 0;//純虛函數(shù),子類必須手動實現(xiàn)     };     //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; //當(dāng)前日志的時間             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

這里我們使用套接字進行通信,套接字可以簡單理解為一個文件流。創(chuàng)建套接字后填寫網(wǎng)絡(luò)信息,并與內(nèi)核綁定。由于我們使用的是云服務(wù)器,默認(rèn)不需要綁定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.創(chuàng)建套接字         _sockfd = ::socket(AF_INET, SOCK_DGRAM, 0); //指定網(wǎng)絡(luò)通信模式. 面向數(shù)據(jù)包, 標(biāo)記為設(shè)置為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包含了網(wǎng)絡(luò)信息。網(wǎng)絡(luò)通信中,我們需要對InetAddr進行強轉(zhuǎn),實現(xiàn)c語言版本的多態(tài)。

#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, 網(wǎng)絡(luò)轉(zhuǎn)主機     InetAddr(const struct sockaddr_in &addr) : _net_addr(addr) {         PortNet2Host();         IpNet2Host();     }     //如果傳進來的是端口號, 就轉(zhuǎn)化為網(wǎng)絡(luò), 服務(wù)器不需要自己綁定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

客戶端通過標(biāo)準(zhǔn)輸入獲取信息并發(fā)送到服務(wù)器,然后接收并打印服務(wù)器回顯的內(nèi)容。

#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 << "請輸入要發(fā)送的信息: ";         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 << "服務(wù)器回顯: " << buffer << std::endl;         }     }     return 0; }
  1. 運行結(jié)果

Linux__之__基于UDP的Socket編程網(wǎng)絡(luò)通信

相關(guān)閱讀

主站蜘蛛池模板: 国产精品一区二区在线免费观看 | h视频在线观看免费 | 国产一区二区在线视频 | 成人av一区二区三区 | 亚洲iv一区二区三区 | 欧美一区二区在线观看 | 久久国产精品网 | 亚洲视频二区 | 国产免费又色又爽又黄在线观看 | 99精品国自产在线 | 国产精品久久久99 | 亚洲精品一区二区二区 | 久久久精品一区 | 久久久久久久99 | 国产精品久久av | 日韩精品一区二区三区视频播放 | 亚洲国产成人av好男人在线观看 | 91免费版在线 | 日韩高清av | 亚洲协和影视 | 青青草av在线播放 | 日韩欧美久久 | 精品一区二区三区在线视频 | 日韩免费高清视频 | 国产精品久久久久久久久久免费 | 播放一级黄色片 | 久久一区二区视频 | 久久久久久国产精品免费 | 欧美日韩成人网 | 殴美成人在线视频 | av大片| 欧美精品一区在线 | 久久久久九九九女人毛片 | 国产日韩一区二区 | 国产精品久久久久久久久久久免费看 | 欧美日韩在线观看一区 | 欧美一区二区免费 | 日本 欧美 三级 高清 视频 | 亚洲国产一 | 天堂视频中文在线 | 国产中文在线 |