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.mzph.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-…