Socket 編程 UDP

Socket 編程 UDP

  • UDP 網絡編程
    • V1 版本 - echo server
    • V2 版本 - DictServer
    • V3 版本 - 簡單聊天室
  • 補充參考內容
    • 地址轉換函數
    • 關于 inet_ntoa

UDP 網絡編程

  • 聲明:下面代碼的驗證都是用Windows作為客戶端的,如果你有兩臺云服務器可以直接粘貼我在Linux下的客戶端代碼。這里我的客戶端使用Windows寫的如果用發現你的Windows無法連接到服務器就要去修改你的云服務器的安全組,這里就不詳細贅述了,大家感興趣可以自行搜索

V1 版本 - echo server

  • 代碼用到的log.hpp在我的專欄系統部分可以找到這里就不貼了
//UdpServer.hpp
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <memory>
#include "log.hpp"
using namespace LogModule;
using func_t = std::function<std::string(std::string message)>;
class UdpServe
{
public:UdpServe(uint16_t port, func_t func): _port(port),_func(func){}~UdpServe() {}void Start(){_isrunning = true;while (_isrunning){// 1.接受消息sockaddr_in peer; // 客戶端socklen_t len = sizeof(peer);char buffer[1024];ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&peer, &len);if (n > 0){buffer[n] = 0;std::string peer_ip=inet_ntoa(peer.sin_addr);int peer_port=ntohs(peer.sin_port);std::string ret = _func(std::string(buffer));LOG(LogLevel::INFO)<<"[" << peer_ip << ":" << peer_port<< "]# " << ret;}//2.發消息std::string sermes="serve say@"+std::string(buffer);//std::cout<<sermes<<std::endl;ssize_t n2=sendto(_sockfd,sermes.c_str(),sermes.size(),0,(sockaddr*)&peer,len);}}void Init(){// 1.創建我們的sockfd// 前兩個參數就規定了我們用的就是udpint n = socket(AF_INET, SOCK_DGRAM, 0);if (n < 0){LOG(LogLevel::FATAL) << "sock error";exit(2);}_sockfd=n;LOG(LogLevel::INFO) << "sock success";// 創建成功了// 進行綁定sockaddr_in local;// 首先進行清零bzero(&local, sizeof(local));// 填充字段local.sin_family = AF_INET;// 把本地轉為網絡序列local.sin_port = htons(_port);// 這里就是不顯示綁定而是綁定該太機器上任意的ip地址local.sin_addr.s_addr = INADDR_ANY;int ret = bind(_sockfd, (sockaddr *)&local, sizeof(local));if (ret < 0){LOG(LogLevel::FATAL) << "bind failsure";exit(2);}LOG(LogLevel::INFO) << "bind success";}private:int _sockfd;// std::string _ip;//服務器端一臺機器上可能有多個ip我們最好的就是不顯示綁定我們的ip地址uint16_t _port;bool _isrunning;func_t _func;
};
//UdpServe.cc
#include"UdpServe.hpp"
#include<memory>
std::string defaultfunc(std::string messges)
{return messges;
}int main(int argc,char*argv[])
{if(argc!=2){std::cerr << "Usage: " << argv[0] << " port" << std::endl;return 1;}ENABLE_CONSOLE_LOG_STRATEGY();uint16_t port=std::stoi(argv[1]);std::unique_ptr<UdpServe> udpserveptr=std::make_unique<UdpServe>(port,defaultfunc);udpserveptr->Init();udpserveptr->Start();return 0;
}
//UdpClient.cc Linux
#include <iostream>
#include <string>
#include <functional>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <memory>
#include "log.hpp"
using namespace LogModule;int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << " server_ip server_port" << std::endl;return 1;}ENABLE_CONSOLE_LOG_STRATEGY();std::string serve_ip = argv[1];int serve_port = std::stoi(argv[2]);int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){LOG(LogLevel::FATAL) << "socket failsure";exit(2);}// 客戶端不用顯示綁定在我們第一次發消息的時候就os會自動幫我們綁定客戶端機器的ip,端口號隨機生成sockaddr_in serve;bzero(&serve, sizeof(serve));socklen_t len = sizeof(serve);serve.sin_port = htons(serve_port);serve.sin_family = AF_INET;serve.sin_addr.s_addr = inet_addr(serve_ip.c_str());while (true){std::string mesage;std::cout << "Please Enter:" << std::endl;// 從標準輸入流讀取mesagegetline(std::cin, mesage);int n = sendto(sockfd, mesage.c_str(), mesage.size(), 0, (sockaddr *)&serve, len);(void)n;// 收消息char buffer[1024];sockaddr_in peer;bzero(&peer, sizeof(peer));socklen_t peerlen=sizeof(peer);int m= recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(sockaddr*)&peer,&peerlen);if(m>0){buffer[m]=0;std::cout<<buffer<<std::endl;}}return 0;
}

驗證效果:
在這里插入圖片描述

//windows作為客戶端
#include <iostream>
#include <cstdio>
#include <thread>
#include <string>
#include <cstdlib>
#include <WinSock2.h>
#include <Windows.h>
#include <io.h>
#include <fcntl.h>#pragma warning(disable : 4996)
#pragma comment(lib, "ws2_32.lib")std::string serverip = ""; // 填寫你的云服務器 ip
uint16_t serverport =;              // 填寫你的云服務開放的端口號// 設置控制臺編碼為 UTF-8
void SetConsoleToUTF8() {SetConsoleOutputCP(65001);          // 設置控制臺輸出編碼為 UTF-8SetConsoleCP(65001);               // 設置控制臺輸入編碼為 UTF-8_setmode(_fileno(stdout), _O_U8TEXT);  // 設置寬字符輸出模式_setmode(_fileno(stdin), _O_U8TEXT);    // 設置寬字符輸入模式
}int main() {SetConsoleToUTF8();  // 必須在其他操作前調用WSADATA wsd;WSAStartup(MAKEWORD(2, 2), &wsd);struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);server.sin_addr.s_addr = inet_addr(serverip.c_str());SOCKET sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd == INVALID_SOCKET) {std::wcout << L"socket error: " << WSAGetLastError() << std::endl;return 1;}std::wstring wmessage;char buffer[1024];while (true) {std::wcout << L"Please Enter@ ";std::getline(std::wcin, wmessage);if (wmessage.empty()) continue;// 寬字符轉 UTF-8int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wmessage[0], (int)wmessage.size(), NULL, 0, NULL, NULL);std::string message(size_needed, 0);WideCharToMultiByte(CP_UTF8, 0, &wmessage[0], (int)wmessage.size(), &message[0], size_needed, NULL, NULL);sendto(sockfd, message.c_str(), (int)message.size(), 0,(struct sockaddr*)&server, sizeof(server));struct sockaddr_in temp;int len = sizeof(temp);int s = recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len);if (s > 0) {buffer[s] = 0;// UTF-8 轉寬字符顯示int wsize_needed = MultiByteToWideChar(CP_UTF8, 0, buffer, s, NULL, 0);std::wstring wbuffer(wsize_needed, 0);MultiByteToWideChar(CP_UTF8, 0, buffer, s, &wbuffer[0], wsize_needed);std::wcout << wbuffer << std::endl;}}closesocket(sockfd);WSACleanup();return 0;
}

V2 版本 - DictServer

  • 代碼用到的其余的模塊在我的專欄系統部分可以找到代碼這里就不貼了,只貼了服務端代碼和客戶端代碼
//UdpServe.hpp
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <memory>
#include "log.hpp"
#include "InetAddr.hpp"
using namespace LogModule;
using func_t = std::function<std::string(std::string message, InetAddr peer)>;
class UdpServe
{
public:UdpServe(uint16_t port, func_t func): _port(port),_func(func){}~UdpServe() {}void Start(){_isrunning = true;while (_isrunning){// 1.接受消息sockaddr_in peer; // 客戶端socklen_t len = sizeof(peer);char buffer[1024];ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&peer, &len);std::string ret;if (n > 0){buffer[n] = 0;// std::string peer_ip=inet_ntoa(peer.sin_addr);// int peer_port=ntohs(peer.sin_port);InetAddr client(peer);ret = _func(std::string(buffer), client);// LOG(LogLevel::INFO)<<"[" << peer_ip << ":" << peer_port<< "]# " << ret;}// 2.發消息// std::string sermes="serve say@"+std::string(buffer);// std::cout<<sermes<<std::endl;ssize_t n2 = sendto(_sockfd, ret.c_str(), ret.size(), 0, (sockaddr *)&peer, len);}}void Init(){// 1.創建我們的sockfd// 前兩個參數就規定了我們用的就是udpint n = socket(AF_INET, SOCK_DGRAM, 0);if (n < 0){LOG(LogLevel::FATAL) << "sock error";exit(2);}_sockfd = n;LOG(LogLevel::INFO) << "sock success";// 創建成功了// 進行綁定sockaddr_in local;// 首先進行清零bzero(&local, sizeof(local));// 填充字段local.sin_family = AF_INET;// 把本地轉為網絡序列local.sin_port = htons(_port);// 這里就是不顯示綁定而是綁定該太機器上任意的ip地址local.sin_addr.s_addr = INADDR_ANY;int ret = bind(_sockfd, (sockaddr *)&local, sizeof(local));if (ret < 0){LOG(LogLevel::FATAL) << "bind failsure";exit(2);}LOG(LogLevel::INFO) << "bind success";}private:int _sockfd;// std::string _ip;//服務器端一臺機器上可能有多個ip我們最好的就是不顯示綁定我們的ip地址uint16_t _port;bool _isrunning;func_t _func;
};
//UdpServe.cc
#include "UdpServe.hpp"
#include "dict.hpp"
#include <memory>
#include "InetAddr.hpp"
std::string defaultfunc(std::string messges)
{return messges;
}int main(int argc, char *argv[])
{if (argc != 2){std::cerr << "Usage: " << argv[0] << " port" << std::endl;return 1;}ENABLE_CONSOLE_LOG_STRATEGY();uint16_t port = std::stoi(argv[1]);// 字典Dict dict;dict.LoadDict();std::unique_ptr<UdpServe> udpserveptr = std::make_unique<UdpServe>(port, [&dict](std::string mess, InetAddr peer) -> std::string {return dict.Translate(mess,peer);});udpserveptr->Init();udpserveptr->Start();return 0;
}
//UdpClient.cc Linux
#include <iostream>
#include <string>
#include <functional>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <memory>
#include "log.hpp"
using namespace LogModule;int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << " server_ip server_port" << std::endl;return 1;}ENABLE_CONSOLE_LOG_STRATEGY();std::string serve_ip = argv[1];int serve_port = std::stoi(argv[2]);int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){LOG(LogLevel::FATAL) << "socket failsure";exit(2);}// 客戶端不用顯示綁定在我們第一次發消息的時候就os會自動幫我們綁定客戶端機器的ip,端口號隨機生成sockaddr_in serve;bzero(&serve, sizeof(serve));socklen_t len = sizeof(serve);serve.sin_port = htons(serve_port);serve.sin_family = AF_INET;serve.sin_addr.s_addr = inet_addr(serve_ip.c_str());while (true){std::string mesage;std::cout << "Please Enter:" << std::endl;// 從標準輸入流讀取mesagegetline(std::cin, mesage);int n = sendto(sockfd, mesage.c_str(), mesage.size(), 0, (sockaddr *)&serve, len);(void)n;// 收消息char buffer[1024];sockaddr_in peer;bzero(&peer, sizeof(peer));socklen_t peerlen=sizeof(peer);int m= recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(sockaddr*)&peer,&peerlen);if(m>0){buffer[m]=0;std::cout<<buffer<<std::endl;}}return 0;
}
//InetAddr.hpp
#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string>
class InetAddr
{public:InetAddr(sockaddr_in &sock):_sockaddrin(sock){_ip=inet_ntoa(_sockaddrin.sin_addr);_port=ntohs(_sockaddrin.sin_port);}~InetAddr(){}std::string Ip(){return _ip;}uint16_t Port(){return _port;}private:sockaddr_in _sockaddrin;std::string _ip;uint16_t _port;};
//windows作為客戶端
#include <iostream>
#include <cstdio>
#include <thread>
#include <string>
#include <cstdlib>
#include <WinSock2.h>
#include <Windows.h>
#include <io.h>
#include <fcntl.h>#pragma warning(disable : 4996)
#pragma comment(lib, "ws2_32.lib")std::string serverip = ""; // 填寫你的云服務器 ip
uint16_t serverport =;              // 填寫你的云服務開放的端口號// 設置控制臺編碼為 UTF-8
void SetConsoleToUTF8() {SetConsoleOutputCP(65001);          // 設置控制臺輸出編碼為 UTF-8SetConsoleCP(65001);               // 設置控制臺輸入編碼為 UTF-8_setmode(_fileno(stdout), _O_U8TEXT);  // 設置寬字符輸出模式_setmode(_fileno(stdin), _O_U8TEXT);    // 設置寬字符輸入模式
}int main() {SetConsoleToUTF8();  // 必須在其他操作前調用WSADATA wsd;WSAStartup(MAKEWORD(2, 2), &wsd);struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);server.sin_addr.s_addr = inet_addr(serverip.c_str());SOCKET sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd == INVALID_SOCKET) {std::wcout << L"socket error: " << WSAGetLastError() << std::endl;return 1;}std::wstring wmessage;char buffer[1024];while (true) {std::wcout << L"Please Enter@ ";std::getline(std::wcin, wmessage);if (wmessage.empty()) continue;// 寬字符轉 UTF-8int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wmessage[0], (int)wmessage.size(), NULL, 0, NULL, NULL);std::string message(size_needed, 0);WideCharToMultiByte(CP_UTF8, 0, &wmessage[0], (int)wmessage.size(), &message[0], size_needed, NULL, NULL);sendto(sockfd, message.c_str(), (int)message.size(), 0,(struct sockaddr*)&server, sizeof(server));struct sockaddr_in temp;int len = sizeof(temp);int s = recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len);if (s > 0) {buffer[s] = 0;// UTF-8 轉寬字符顯示int wsize_needed = MultiByteToWideChar(CP_UTF8, 0, buffer, s, NULL, 0);std::wstring wbuffer(wsize_needed, 0);MultiByteToWideChar(CP_UTF8, 0, buffer, s, &wbuffer[0], wsize_needed);std::wcout << wbuffer << std::endl;}}closesocket(sockfd);WSACleanup();return 0;
}

驗證效果:
在這里插入圖片描述

V3 版本 - 簡單聊天室

//linux udpserve.hpp
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <memory>
#include "log.hpp"
#include "InetAddr.hpp"
using namespace LogModule;
using func_t1 = std::function<void(int sockfd,std::string message, InetAddr peer)>;
class UdpServe
{
public:UdpServe(uint16_t port, func_t1 func): _port(port),_func(func){}~UdpServe() {}void Start(){_isrunning = true;while (_isrunning){// 1.接受消息sockaddr_in peer; // 客戶端socklen_t len = sizeof(peer);char buffer[1024];ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&peer, &len);std::string ret;if (n > 0){buffer[n] = 0;// std::string peer_ip=inet_ntoa(peer.sin_addr);// int peer_port=ntohs(peer.sin_port);InetAddr client(peer);_func(_sockfd,std::string(buffer), client);// LOG(LogLevel::INFO)<<"[" << peer_ip << ":" << peer_port<< "]# " << ret;}// 2.發消息// std::string sermes="serve say@"+std::string(buffer);// std::cout<<sermes<<std::endl;//ssize_t n2 = sendto(_sockfd, ret.c_str(), ret.size(), 0, (sockaddr *)&peer, len);}}void Init(){// 1.創建我們的sockfd// 前兩個參數就規定了我們用的就是udpint n = socket(AF_INET, SOCK_DGRAM, 0);if (n < 0){LOG(LogLevel::FATAL) << "sock error";exit(2);}_sockfd = n;LOG(LogLevel::INFO) << "sock success";// 創建成功了// 進行綁定sockaddr_in local;// 首先進行清零bzero(&local, sizeof(local));// 填充字段local.sin_family = AF_INET;// 把本地轉為網絡序列local.sin_port = htons(_port);// 這里就是不顯示綁定而是綁定該太機器上任意的ip地址local.sin_addr.s_addr = INADDR_ANY;int ret = bind(_sockfd, (sockaddr *)&local, sizeof(local));if (ret < 0){LOG(LogLevel::FATAL) << "bind failsure";exit(2);}LOG(LogLevel::INFO) << "bind success";}private:int _sockfd;// std::string _ip;//服務器端一臺機器上可能有多個ip我們最好的就是不顯示綁定我們的ip地址uint16_t _port;bool _isrunning;func_t1 _func;
};//linux udpserve.cc
#include "UdpServe.hpp"
#include <memory>
#include "InetAddr.hpp"
#include "Route.hpp"
#include "PthreadPool.hpp"
#include <functional>
using funcpopl = std::function<void()>;
using namespace PthreadModlue;
std::string defaultfunc(std::string messges)
{return messges;
}int main(int argc, char *argv[])
{if (argc != 2){std::cerr << "Usage: " << argv[0] << " port" << std::endl;return 1;}ENABLE_CONSOLE_LOG_STRATEGY();uint16_t port = std::stoi(argv[1]);// 1.創建route對象提供吧消息轉發給所有的在線用戶Route r;// 2.創建線程池auto pt = PthreadPoolModule::PthreadPool<funcpopl>::GetInstance();pt->Start();std::unique_ptr<UdpServe> udpserveptr = std::make_unique<UdpServe>(port, [&r, &pt](int sockfd, std::string message, InetAddr peer){funcpopl fun=std::bind(&Route::RouteMessage,&r,sockfd,message,peer);pt->Enqueue(fun);});udpserveptr->Init();udpserveptr->Start();return 0;
}//udpclient.cc linux
#include <iostream>
#include <string>
#include <functional>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <memory>
#include "log.hpp"
#include "pthread.hpp"
using namespace LogModule;
using namespace PthreadModlue;
std::string serve_ip;
int serve_port;
int sockfd;
socklen_t len;
sockaddr_in serve;
pthread_t reid;
void recive()
{while (true){char buffer[1024];sockaddr_in peer;bzero(&peer, sizeof(peer));socklen_t peerlen = sizeof(peer);int m = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&peer, &peerlen);if (m > 0){buffer[m] = 0;std::cout << buffer << std::endl;}}
}
void sends()
{const std::string online = "inline";sendto(sockfd, online.c_str(), online.size(), 0, (struct sockaddr *)&serve, sizeof(serve));while (true){std::string mesage;std::cout << "Please Enter:" << std::endl;// 從標準輸入流讀取mesagegetline(std::cin, mesage);int n = sendto(sockfd, mesage.c_str(), mesage.size(), 0, (sockaddr *)&serve, len);(void)n;if (mesage == std::string("QUIT")){pthread_cancel(reid);break;}}
}int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << " server_ip server_port" << std::endl;return 1;}ENABLE_CONSOLE_LOG_STRATEGY();serve_ip = argv[1];serve_port = std::stoi(argv[2]);sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){LOG(LogLevel::FATAL) << "socket failsure";exit(2);}// 客戶端不用顯示綁定在我們第一次發消息的時候就os會自動幫我們綁定客戶端機器的ip,端口號隨機生成bzero(&serve, sizeof(serve));len = sizeof(serve);serve.sin_port = htons(serve_port);serve.sin_family = AF_INET;serve.sin_addr.s_addr = inet_addr(serve_ip.c_str());Pthread p1(recive);Pthread p2(sends);p1.Start();p2.Start();reid = p1.Ip();p1.Join();p2.Join();return 0;
}
//window udpclient.cc
#include <iostream>
#include <string>
#include <winsock2.h>
#include <process.h>
#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable : 4996)
#define BUFFER_SIZE 1024SOCKET sockfd;
sockaddr_in server_addr;
bool running = true;// 接收線程函數
unsigned __stdcall RecvThread(void* param) {char buffer[BUFFER_SIZE];int server_len = sizeof(server_addr);while (running) {int n = recvfrom(sockfd, buffer, BUFFER_SIZE - 1, 0,(sockaddr*)&server_addr, &server_len);if (n > 0) {buffer[n] = '\0';std::cout << "\n[Server] " << buffer << std::endl;std::cout << "Enter message: ";}}return 0;
}// 發送線程函數
unsigned __stdcall SendThread(void* param) {const std::string online = "online";sendto(sockfd, online.c_str(), online.size(), 0,(sockaddr*)&server_addr, sizeof(server_addr));while (running) {std::string message;std::cout << "Enter message: ";std::getline(std::cin, message);if (message == "QUIT") {running = false;break;}sendto(sockfd, message.c_str(), message.size(), 0,(sockaddr*)&server_addr, sizeof(server_addr));}// 清理closesocket(sockfd);WSACleanup();return 0;
}int main() {// 初始化WinsockWSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {std::cerr << "WSAStartup failed\n";return 1;}// 創建套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd == INVALID_SOCKET) {std::cerr << "Socket creation failed: " << WSAGetLastError() << "\n";WSACleanup();return 1;}// 硬編碼服務器信息const char* SERVER_IP = "116.205.122.71";const unsigned short SERVER_PORT = 8080;// 配置服務器地址memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);// 驗證地址有效性if (server_addr.sin_addr.s_addr == INADDR_NONE) {std::cerr << "Invalid server IP format!\n";closesocket(sockfd);WSACleanup();return 1;}std::cout << "=== Connecting to " << SERVER_IP << ":" << SERVER_PORT << " ===\n";// 創建線程HANDLE hRecv = (HANDLE)_beginthreadex(nullptr, 0, RecvThread, nullptr, 0, nullptr);HANDLE hSend = (HANDLE)_beginthreadex(nullptr, 0, SendThread, nullptr, 0, nullptr);// 等待線程結束WaitForSingleObject(hRecv, INFINITE);WaitForSingleObject(hSend, INFINITE);CloseHandle(hRecv);CloseHandle(hSend);return 0;
}

效果顯示:
在這里插入圖片描述

補充參考內容

地址轉換函數

字符串轉 in_addr 的函數:
在這里插入圖片描述
in_addr 轉字符串的函數:
在這里插入圖片描述
其中 inet_pton 和 inet_ntop 不僅可以轉換 IPv4 的 in_addr,還可以轉換 IPv6 的
in6_addr,因此函數接口是 void *addrptr。
代碼示例:

在這里插入圖片描述

關于 inet_ntoa

inet_ntoa 這個函數返回了一個 char*, 很顯然是這個函數自己在內部為我們申請了一塊
內存來保存 ip 的結果. 那么是否需要調用者手動釋放呢?
在這里插入圖片描述
man 手冊上說, inet_ntoa 函數, 是把這個返回結果放到了靜態存儲區. 這個時候不需要
我們手動進行釋放. 那么問題來了, 如果我們調用多次這個函數, 會有什么樣的效果呢? 參見如下代碼:
在這里插入圖片描述
運行結果如下:
在這里插入圖片描述
因為 inet_ntoa 把結果放到自己內部的一個靜態存儲區, 這樣第二次調用時的結果會覆
蓋掉上一次的結果

  • 思考: 如果有多個線程調用 inet_ntoa, 是否會出現異常情況呢?
  • 在 APUE 中, 明確提出 inet_ntoa 不是線程安全的函數;
  • 但是在 centos7 上測試, 并沒有出現問題, 可能內部的實現加了互斥鎖;
  • 在多線程環境下, 推薦使用 inet_ntop, 這個函數由調用者提供一個緩沖區保存結果, 可以規避線程安全問題

因此我們上面的InetAddr類就可以改寫了

#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include<string.h>
class InetAddr
{
public://網絡轉主機InetAddr(sockaddr_in &sock): _addr(sock){//_ip=inet_ntoa(_sockaddrin.sin_addr);_port = ntohs(_addr.sin_port);char ipbuffer[64];inet_ntop(AF_INET, &_addr.sin_addr, ipbuffer, sizeof(_addr));_ip = ipbuffer;}//主機轉網絡InetAddr(const std::string &ip, uint16_t port) : _ip(ip), _port(port){// 主機轉網絡memset(&_addr, 0, sizeof(_addr));_addr.sin_family = AF_INET;inet_pton(AF_INET, _ip.c_str(), &_addr.sin_addr);_addr.sin_port = htons(_port);}InetAddr(uint16_t port) : _port(port), _ip("0"){// 主機轉網絡memset(&_addr, 0, sizeof(_addr));_addr.sin_family = AF_INET;_addr.sin_addr.s_addr = INADDR_ANY;_addr.sin_port = htons(_port);}~InetAddr(){}std::string Ip(){return _ip;}uint16_t Port(){return _port;}bool operator==(const InetAddr &peer){return _ip == peer._ip && _port == peer._port;}std::string StringAddr(){return _ip + ":" + std::to_string(_port);}socklen_t NetAddrLen(){return sizeof(_addr);}const struct sockaddr_in &NetAddr() { return _addr; }private:sockaddr_in _addr;std::string _ip;uint16_t _port;
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/81508.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/81508.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/81508.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

c++ 二級指針 vs 指針引用

二級指針 vs 指針引用&#xff1a;深入對比與分析 在C中&#xff0c;二級指針和指針引用都可以用于修改外部指針&#xff0c;但它們在語法、安全性和使用場景上有重要區別。下面我將從多個維度進行詳細對比。 1. 基本概念 1.1 二級指針 (Pointer to Pointer) int a 10; in…

【Hive入門】Hive與Spark SQL深度集成:通過Spark ThriftServer高效查詢Hive表

目錄 引言 1 Spark ThriftServer架構解析 1.1 核心組件與工作原理 1.2 與傳統HiveServer2的對比 2 Spark ThriftServer部署指南 2.1 環境準備與啟動流程 2.1.1 前置條件檢查 2.1.2 服務啟動流程 2.2 高可用部署方案 2.2.1 基于ZooKeeper的HA架構 3 性能優化實戰 3.…

[面試]SoC驗證工程師面試常見問題(二)

SoC驗證工程師面試常見問題(二) 摘要:面試SoC驗證工程師時,SystemVerilog (SV) 和 UVM (Universal Verification Methodology) 是核心技能,而AXI總線是現代SoC中最常見的接口協議之一,因此也是必考點。以下是可能被問到的問題及優質答案的詳細列表: 一、 System…

vue3 css模擬語音通話不同語音、正在加載等的效果

實現效果如下&#xff1a; 在不同的時間&#xff0c;顯示不一樣的效果&#xff08;大小是一樣的&#xff0c;截圖時尺寸發生了變化&#xff09; 具體實現代碼如下&#xff1a; <script setup> import {ref} from "vue";const max_hight ref(40px) const min…

KeyPresser 一款自動化按鍵工具

1. 簡介 KeyPresser 是一款自動化按鍵工具,它可以與窗口交互,并支持后臺運行, 無需保持被控窗口在前臺運行。用戶可以選擇要操作的目標窗口,并通過勾選復選框來控制要發送哪些按鍵消息。可以從組合框中選擇所需的按鍵,并在編輯框中輸入時間間隔以控制按鍵發送之間的延遲。程…

ai之paddleOCR 識別PDF python312和paddle版本沖突 GLIBCXX_3.4.30

這里寫自定義目錄標題 問題一**解決方案****方法 1&#xff1a;使用符號鏈接將系統庫鏈接到 Conda 環境** **補充說明****驗證修復結果** 問題二&#xff1a;**問題根源****解決方案****1. 確認 TensorRT 安裝狀態****2. 安裝 TensorRT 并配置環境變量****3. 驗證 TensorRT 與 …

【RabbitMQ】 RabbitMQ快速上手

文章目錄 一、RabbitMQ 核心概念1.1 Producer和Consumer2.2 Connection和Channel2.3 Virtual host2.4 Queue2.5 Exchange2.6 RabbitMQ工作流程 二、AMQP協議三 、web界面操作4.1 用戶相關操作4.2 虛擬主機相關操作 四、RabbitMQ快速入門4.1 引入依賴4.2 編寫生產者代碼4.2.1 創…

Beatoven AI 自動生成音樂

Beatoven AI 自動生成音樂 文章目錄 Beatoven AI 自動生成音樂一、源代碼二、準備工作1. 安裝 Python 環境2. 安裝依賴庫 三、配置 API 密鑰四、運行腳本示例一&#xff1a;使用默認參數示例二&#xff1a;生成一段電影預告片風格音樂&#xff08;30秒&#xff09; 五、生成結果…

筆試專題(十四)

文章目錄 mari和shiny題解代碼 體操隊形題解代碼 二叉樹中的最大路徑和題解代碼 mari和shiny 題目鏈接 題解 1. 可以用多狀態的線性dp 2. 細節處理&#xff1a;使用long long 存儲個數 3. 空間優化&#xff1a;只需要考慮等于’s’&#xff0c;‘sh’&#xff0c;shy’的情況…

LeetCode —— 94. 二叉樹的中序遍歷

&#x1f636;?&#x1f32b;?&#x1f636;?&#x1f32b;?&#x1f636;?&#x1f32b;?&#x1f636;?&#x1f32b;?Take your time ! &#x1f636;?&#x1f32b;?&#x1f636;?&#x1f32b;?&#x1f636;?&#x1f32b;?&#x1f636;?&#x1f32b;?…

conda相關操作

安裝torch 直接使用conda install torch1.12.0會報錯&#xff0c;因為 Conda 通常使用 pytorch 作為包名&#xff08;而非 torch&#xff09; 正確使用方法&#xff1a; conda install pytorch1.12.0 -c pytorch使用 pip 安裝 pip install torch1.12.0在 Conda 中查看可安裝…

【Java面試筆記:進階】26.如何監控和診斷JVM堆內和堆外內存使用?

監控和診斷JVM內存使用是優化性能和解決內存問題的關鍵。 1.JVM內存監控與診斷方法 1.圖形化工具 JConsole:提供圖形化界面,可直接連接到Java進程,查看內存使用情況。VisualVM:功能強大的圖形化工具,但注意從Oracle JDK 9開始不再包含在JDK安裝包中。Java Mission Contr…

AVIOContext 再學習

這個目前階段用的不多&#xff0c;暫時不要花費太多精力。 url 的格式不同&#xff0c;使用的傳輸層協議也不同。這塊看代碼還沒看到自己想的這樣。 目前看的信息是&#xff1a;avformatContext 的 io_open 回調函數 在默認情況下叫 io_open_default&#xff0c;在解復用的 av…

在Java項目中實現本地語音識別與熱點檢測,并集成阿里云智能語音服務

引言 隨著語音交互技術的發展&#xff0c;如何高效地處理用戶的語音輸入成為許多應用的重要課題。本文將詳細介紹如何在一個Java項目中同時實現&#xff1a; 基于Vosk的本地語音識別&#xff1a;無需調用云端API即可完成語音到文本的轉換。本地熱點語音內容識別&#xff1a;對…

第15章 對API的身份驗證和授權

第15章 對API的身份驗證和授權 在構建RESTful API時,確保只有經過身份驗證和授權的用戶才能訪問特定資源是至關重要的。身份驗證是確認用戶身份的過程,而授權則是決定用戶是否有權訪問特定資源的過程。在本章中,我們將詳細探討如何在ASP.NET Core Web API中實現身份驗證和授…

asp.net客戶管理系統批量客戶信息上傳系統客戶跟單系統crm

# crm-150708 客戶管理系統批量客戶信息上傳系統客戶跟單系統 # 開發背景 本軟件是給鄭州某企業管理咨詢公司開發的客戶管理系統軟件 # 功能 1、導入客戶數據到系統 2、批量將不同的客戶分配給不同的業務員跟進 3、可以對客戶數據根據緊急程度標記不同的顏色&#xff0c…

深入理解現代JavaScript:從ES6+語法到Fetch API

引言 JavaScript作為Web開發的基石語言&#xff0c;近年來經歷了翻天覆地的變化。ES6(ECMAScript 2015)的發布帶來了革命性的新特性&#xff0c;而現代瀏覽器提供的API也讓前端開發變得更加強大和高效。本文將深入探討ES6核心語法、DOM操作優化技巧以及使用Fetch API進行異步請…

仙盟創夢IDE-智能編程,C#判斷數組中是否存在key

一、net4 net core版本 使用LINQ的Contains方法 string[] array { "apple", "banana", "cherry" };string key "banana";bool exists array.Contains(key);if (exists){Console.WriteLine($"數組中存在鍵 {key}");}else…

360驅動大師v2.0(含網卡版)驅動工具軟件下載及安裝教程

1.軟件名稱&#xff1a;360驅動大師 2.軟件版本&#xff1a;2.0 3.軟件大小&#xff1a;218 MB 4.安裝環境&#xff1a;win7/win10/win11 5.下載地址&#xff1a; https://www.kdocs.cn/l/cdZMwizD2ZL1?RL1MvMTM%3D 提示&#xff1a;先轉存后下載&#xff0c;防止資源丟失&…

2025年- H22-Lc130-206. 反轉鏈表(鏈表)---java版

1.題目描述 2.思路 使用迭代法 (1)定義一個前指針 (2)然后定義兩個變量 curr&#xff08;head&#xff09;&#xff0c;curr.next。 (3)curr和curr.next交換位置&#xff08;只要當前指針不為空&#xff0c;執行兩兩交換&#xff09; 3.代碼實現 /*** Definition for singly-…