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;
};
相关文章:
Socket 编程 UDP
Socket 编程 UDP UDP 网络编程V1 版本 - echo serverV2 版本 - DictServerV3 版本 - 简单聊天室 补充参考内容地址转换函数关于 inet_ntoa UDP 网络编程 声明:下面代码的验证都是用Windows作为客户端的,如果你有两台云服务器可以直接粘贴我在Linux下的客…...
Lua 基础 API与 辅助库函数 中关于创建的方法用法
目录 基础 API 函数1. lua_len(L, index)2. lua_load(L, reader, data, chunkname, mode)3. lua_newstate(allocator, ud)4. lua_newtable(L)5. lua_newthread(L)6. lua_newuserdata(L, size)7. lua_next(L, index) 辅助库函数(luaL_*)8. luaL_len(L, in…...
YOLOv11改进:利用RT-DETR主干网络PPHGNetV2助力轻量化目标检测
这里写自定义目录标题 YOLOv11改进:利用RT-DETR主干网络PPHGNetV2助力轻量化目标检测1. 介绍2. 引言3. 技术背景3.1 YOLOv11概述3.2 RT-DETR与PPHGNetV23.3 相关工作 4. 应用使用场景5. 详细代码实现5.1 环境准备5.2 PPHGNetV2主干网络实现5.3 YOLOv11与PPHGNetV2集…...
centos7.0无法安装php8.2/8.3
在centos安装php8.2报错 configure: error: *** A compiler with support for C17 language features is required. 配置过程检测到你的系统编译器不支持 C17 语言特性,而 PHP 8.2 的编译需要编译器支持 C17 sudo yum update -y sudo yum install centos-releas…...
工业传动核心部件深度剖析:丝杆升降机与气缸的技术特性及选型指南
在工业自动化技术飞速发展的当下,丝杆升降机与气缸作为关键的直线传动部件,广泛应用于各类机械设备中。对于工程师而言,深入了解它们的技术特性、优缺点及适用场景,是实现高效、精准设备设计的重要前提。本文将从技术原理出发&…...
flask 获取各种请求数据:GET form-data x-www-form-urlencoded JSON headers 上传文件
在 Flask 里,能使用多种方法获取不同类型的请求数据,下面详细介绍常见请求数据的获取方式。 获取查询字符串参数(GET 请求) 查询字符串参数一般在 URL 里,以 ?key1value1&key2value2 这种形式存在。可通过 requ…...
c++_2011 NOIP 普及组 (1)
P1307 [NOIP 2011 普及组] 数字反转 P1307 [NOIP 2011 普及组] 数字反转 - 洛谷 # P1307 [NOIP 2011 普及组] 数字反转 ## 题目描述 给定一个整数 $N$,请将该数各个位上数字反转得到一个新数。新数也应满足整数的常见形式,即除非给定的原数为零&…...
信息泄露:网站敏感文件泄漏的隐形危机与防御之道
在网络安全领域,信息泄露常被称为“沉默的杀手”。攻击者无需复杂漏洞,仅通过网站无意暴露的敏感文件(如源码备份、配置文件、版本控制记录),即可获取数据库密码、API密钥甚至服务器权限。本文将深入剖析信息泄…...
C++笔记-多态(包含虚函数,纯虚函数和虚函数表等)
1.多态的概念 多态(polymorphism)的概念:通俗来说,就是多种形态。多态分为编译时多态(静态多态)和运行时多态(动态多态),这里我们重点讲运行时多态,编译时多态(静态多态)和运行时多态(动态多态)。编译时多态(静态多态)主要就是我们前面讲的函…...
2025年- H22-Lc130-206. 反转链表(链表)---java版
1.题目描述 2.思路 使用迭代法 (1)定义一个前指针 (2)然后定义两个变量 curr(head),curr.next。 (3)curr和curr.next交换位置(只要当前指针不为空,执行两两交换) 3.代码实现 /*** Definition for singly-…...
智能家居的OneNet云平台
一、声明 该项目只需要创建一个产品,然后这个产品里面包含几个设备,而不是直接创建几个产品 注意:传输数据使用到了不同的power,还有一定要手机先联网才能使用云平台 二、OneNet云平台创建 (1)Temperatur…...
二、shell脚本--变量与数据类型
1. 变量的定义与使用 定义变量:简单直接 在 Shell 里定义变量相当容易: 基本格式: variable_namevalue关键点 ❗:赋值号 的两边绝对不能有空格!这绝对是初学者最容易踩的坑之一 😨,务必留意!…...
GitHub Actions 和 GitLab CI/CD 流水线设计
以下是关于 GitHub Actions 和 GitLab CI/CD 流水线设计 的基本知识总结: 一、核心概念对比 维度GitHub ActionsGitLab CI/CD配置方式YAML 文件(.github/workflows/*.yml).gitlab-ci.yml执行环境GitHub 托管 Runner / 自托管GitLab 共享 Runner / 自托管市场生态Actions Mar…...
穿越数据森林与网络迷宫:树与图上动态规划实战指南
在 C 算法的浩瀚宇宙中,树与图就像是神秘的迷宫和茂密的森林,充满了未知与挑战。而动态规划则是我们探索其中的神奇罗盘,帮助我们找到最优路径。今天,就让我们一起深入这片神秘领域,揭开树与图上动态规划的神秘面纱&am…...
Java学习手册:Spring 生态其他组件介绍
一、微服务架构相关组件 Spring Cloud 服务注册与发现 : Eureka :由 Netflix 开源,包含 Eureka Server 和 Eureka Client 两部分。Eureka Server 作为服务注册表,接收服务实例的注册请求并管理其信息;Eureka Client 负…...
[android]MT6835 Android 移植brctl指令
说明 android默认brctl不支持showmacs选项,需要移植brctl-utils软件包 移除toybox中brctl编译 mssi/external/toybox/Android.bp 将 toybox_symlinks ["[","acpi","base64","basename","blockdev","br…...
安卓基础(悬浮窗分级菜单和弹窗)
initializeViews() 初始化 把全部的按钮都弄出来 // 主菜单按钮ImageButton mainButton floatingMenuView.findViewById(R.id.main_button);// 二级菜单按钮subButtons new ImageButton[3];subButtons[0] floatingMenuView.findViewById(R.id.sub_button_1);subButtons[1]…...
HTTP基础介绍+OSI七层参考模型+HTTP协议介绍
图片来源于网络 图片来源于网络 浏览器 Chrome:谷歌浏览器,推荐 Safari(WebKit):苹果浏览器,iOS,macOS Firefox:火狐浏览器,开源插件特别多(FireBug) IE:Wi…...
【项目实践】boost 搜索引擎
1. 项目展示 boost搜索引擎具体讲解视频 2. 项目背景 对于boost库,官方是没有提供搜索功能的,我们这个项目就是来为它添加一个站内搜索的功能。 3. 项目环境与技术栈 • 项目环境: ubuntu22.04、vscode • 技术栈: C/C、C11、S…...
接口隔离原则(ISP)
非常好,**接口隔离原则(ISP: Interface Segregation Principle)是 SOLID 五大原则中的第四个,它专门解决“一个接口太臃肿”**导致的麻烦。 我来从以下几个维度详细拆解: 🧠 什么是接口隔离原则࿱…...
Leetcode刷题记录29——矩阵置零
题源:https://leetcode.cn/problems/set-matrix-zeroes/description/?envTypestudy-plan-v2&envIdtop-100-liked 题目描述: 思路一: 💡 解题思路 本题中我们采用如下策略: 第一次遍历整个矩阵,记…...
复刻低成本机械臂 SO-ARM100 组装篇(打螺丝喽)
视频讲解: 复刻低成本机械臂 SO-ARM100 组装篇(打螺丝喽) 组装的视频有很多,参考大佬的《手把手复刻HuggingFace开源神作之Follower机械臂组装,资料已整理》_哔哩哔哩_bilibili,跟着视频做,大体…...
[更新完毕]2025东三省B题深圳杯B题数学建模挑战赛数模思路代码文章教学:LED显示屏颜色转换设计与校正
完整内容请看文章最下面的推广群 已经更新完整的文章代码 基于非线性映射与深度模型的多通道LED显示屏色彩校正 摘要 本研究聚焦于高动态色彩空间下LED显示屏的色彩映射与逐点校正问题,结合非线性回归理论与深度学习模型,构建了一套涵盖BT.2020映射、RG…...
Easy云盘总结篇-登录注册
**说在前面:该项目是跟着B站一位大佬写的,不分享源码,支持项目付费 ** 获取图形验证码 可以看到这里有2两种图形验证码,分为: type0:如上图下面那个,是完成操作后要进行注册的验证码 type1: 如…...
04 基于 STM32 的时钟展示程序
前言 我们经常会看到 各个场合下面有 基于数码管 的时钟程序 比如 在车站, 教室, 办公室 等等 各个场合都有 然后 这里就是做一个 简单的 时钟程序 展示程序 测试用例 每一秒钟更新时间, 然后 迭代更新 天, 时, 分 等等 然后 主流程 基于 天, 时分秒 渲染数码管 #incl…...
音视频开发技术总结报告
音视频开发技术总结报告 一、音视频开发基础 1、音频基础 声音原理 声波特性:频率、振幅、波长人耳听觉范围:20Hz-20kHz声音三要素:音调、音量、音色 数字音频基础 采样率:常见44.1kHz、48kHz、96kHz量化位数:8bit、…...
FastAPI系列13:API的安全防护
API的安全防护 1、HTTPS 强制什么是HTTPS强制如何在FastAPI中实现HTTPS强制 2、CORS跨域资源共享什么是CORS在 FastAPI 中开启 CORS 3、SQL注入防护什么是SQL注入如何在FastAPI中实现SQL注入防护 4、CSRF防护什么是CSRF防护如何在FastAPI中实现CSRF防护 在 FastAPI系列12&…...
每天一道面试题@第五天
1.包装类型的缓存机制了解么? 指部分包装类在创建对象时,会将一定范围内的对象缓存起来,当再次使用相同值创建对象时,优先从缓存中获取,而不是重新创建新对象。【提高性能】【节省内存】 列举几个常见的包装类缓存机…...
Python硬核革命:从微控制器到FPGA的深度开发指南
1. 重新定义硬件开发:Python的颠覆性突破 传统硬件开发长期被C/C++和Verilog/VHDL统治,但Python正通过两条路径改变这一格局: 1.1 微控制器领域的MicroPython革命 完整Python 3.4语法支持,运行在资源受限的MCU上(最低要求:64KB ROM,16KB RAM) 直接内存访问能力,突破…...
WebRTC 服务器之Janus概述和环境搭建
1 概述 Janus 是由 Meetecho 开发的通用 WebRTC 服务器,它为构建 WebRTC 应用程序提供了一个模块化框架。服务器目标:Janus WebRTC 网关被设计为轻量级、通用的 WebRTC 服务器,除了实现以下方法外,它本身不提供任何功能࿱…...
mcp+llm+rag
MCPRAG简介 前言一、MCP是什么?二、MCP工作原理(1. MCP Hosts(主机)(2.MCP Clients(客户端)(3. MCP Servers(服务端)(4. Local Data Sources(本地数据源&…...
Seata RM的事务提交与回滚源码解析
文章目录 前言一、RM提交事务二、RM回滚事务2.1、undo校验逻辑2.2、执行回滚逻辑 总结RM 的事务提交与回滚行为说明(基于 Seata AT 模式)1. 提交阶段(Phase Two Commit)2. 回滚阶段(Phase Two Rollback) 前…...
Ubuntu 24.04 完整Docker安装指南:从零配置到实战命令大全
Ubuntu 24.04 完整Docker安装指南:从零配置到实战命令大全 文章目录 Ubuntu 24.04 完整Docker安装指南:从零配置到实战命令大全1. 安装 Docker2. 配置 Docker 镜像加速器2.1 配置 Docker 镜像源2.2 重启 Docker 服务 3. Docker 常用命令3.1 Docker 常用命…...
设计模式简述(十七)备忘录模式
备忘录模式 描述组件使用 描述 备忘录模式用于将对象的状态进行保存为备忘录,以便在需要时可以从备忘录会对象状态;其核心点在于备忘录对象及其管理者是独立于原有对象之外的。 常用于需要回退、撤销功能的场景。 组件 原有对象(包含自身…...
【ICMP协议深度解析】从网络诊断到安全实践
目录 前言技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键报文类型说明协议版本对比 二、实战演示环境配置要求核心实验实现实验1:标准ping流程实验2:traceroute路径发现实验3:自定义ICMP…...
《应用开发突围指南:敏捷开发的实战精髓》
如何在应用开发中精准且深入地应用敏捷开发方法呢?让我们一同深入探索。 敏捷开发,绝非仅仅是一种开发流程,更是一种蕴含深刻智慧的理念与思维方式。它与传统开发模式有着本质的区别,传统开发模式如同严谨的线性旅程,…...
【Mytais系列】SqlSession
MyBatis 的 SqlSession 是框架的核心接口之一,它是应用程序与 MyBatis 交互的顶层 API,用于执行 SQL 命令、管理事务和访问数据库。以下是关于 SqlSession 的详细说明: 1. 核心功能 (1) 执行 SQL 操作 增删改查:通过方法如 sele…...
【掌握 DDL】:SQL 中的数据库与表管理
掌握 DDL:SQL 中的数据库与表管理 掌握 DDL:SQL 中的数据库与表管理数据库 DDL创建数据库查看数据库查看所有数据库查看数据库创建语句 进入数据库删除数据库备份数据库备份恢复 查看数据库连接深入理解数据库创建与删除数据库字符集与校验规则 表 DLL创…...
第43周:GAN总结
目录 摘要 Abstract 计算机视觉中的分类 架构变体 损失变体 时间序列中的GAN 连续型GAN 离散型GAN 总结 摘要 本周总结了GAN的变形,主要从图像处理和时间序列生成两部分入手,分别找出了其中比较经典的几种GAN变种模型,简单分析了…...
安卓基础(MediaProjection)
1. Display 类 作用:代表显示设备(手机屏幕、外接显示器)常用方法: display.getRotation() // 获取屏幕方向(横屏/竖屏) display.getRefreshRate() // 获取屏幕刷新率(如&…...
Android Compose 物联网(IoT)UI 组件库封装指南
Android Compose 物联网封装组件 在物联网(IoT)应用开发中,使用Jetpack Compose可以创建现代化、响应式的用户界面。以下是一些针对物联网场景的Compose封装组件思路和实现方法: 常用物联网组件封装 1. 设备状态指示器 Composable fun DeviceStatusI…...
实用在线工具箱OmniTools
简介 OmniTools 是一个自托管的网络应用,提供多种在线工具,旨在简化日常任务。它包含了一系列独立的、小型但实用的工具,涵盖了文件处理、文本操作、网络请求、系统监控等多个方面。 OmniTools 的设计理念是简单、易用、可定制,方…...
【AI大模型学习路线】第一阶段之大模型开发基础——第三章(大模型实操与API调用)单轮对话与多轮对话调用。
【AI大模型学习路线】第一阶段之大模型开发基础——第三章(大模型实操与API调用)单轮对话与多轮对话调用? 【AI大模型学习路线】第一阶段之大模型开发基础——第三章(大模型实操与API调用)单轮对话与多轮对话调用&…...
数字化转型进阶:26页华为数字化转型实践分享【附全文阅读】
本文分享了华为数字化转型的实践经验和体会。华为通过数字化变革,致力于在客户服务、供应链、产品管理等方面提高效率,并把数字世界带入每个组织,构建万物互联的智能世界。华为的数字化转型愿景是成为行业标杆,通过推进数字化战略、构建面向业务数字化转型的IT组织阵型、坚…...
Go语言的优势与应用场景 -《Go语言实战指南》
一、 Go语言的五大核心优势 1. 语法简洁,开发高效 Go语言借鉴了C语言的表达方式,但去掉了多余复杂的特性(如继承、多态、异常处理等),语法风格清晰明了,极大地降低了学习成本: • 无需头文件…...
3D人物关系图开发实战:Three.js实现自动旋转可视化图谱(附完整代码)
3D人物关系图开发实战:Three.js实现自动旋转可视化图谱 效果核心解析场景初始化自动旋转控制器节点创建(带图片和标签)关系连线动画循环数据格式说明 代码 效果 本文将带您使用Three.js实现一个带自动旋转功能的3D人物关系图谱,核…...
文件操作-
1. 为什么使⽤⽂件? 如果没有⽂件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运⾏程序,是看不到上次程序的数据的,如果要将数据进⾏持久化…...
硬件零基础入门(尚硅谷)
1 一个碳原子有一个自由电子。所以能够导电。 金刚石四个都是都弄成共价键了,所以没有自由电子不能自由电子。 2 新的电子进来,因为互斥电荷进行了定向运动,产生了能量。两边电子平衡就停止了。所以电池的负极有电子。 电荷就是质子和电…...
【Ai零件】高德开放平台MCP的API-key注册
前言 基本操作文档,为n8n等平台,调用高德MCP服务做准备,本文记录其API-Key的生成步骤。 操作步骤 高德开发平台官网:https://lbs.amap.com/ 完成后,进入控制台界面: 创建新应用 进入【应用管理】,点击页…...
安卓基础(startActivityForResult和onActivityResult)
onActivityResult 方法有三个参数: requestCode:启动 Activity 时传入的请求码,用于区分不同的启动请求。resultCode:返回结果的状态码,通常为 RESULT_OK 或 RESULT_CANCELED。data:一个 Intent 对象&…...