当前位置: 首页 > news >正文

16.udp_socket

一.概念回顾

建议先学上篇博客,再向下学习,上篇博客的链接如下:

https://blog.csdn.net/weixin_60668256/article/details/154657323?fromshare=blogdetail&sharetype=blogdetail&sharerId=154657323&sharerefer=PC&sharesource=weixin_60668256&sharefrom=from_link

二.server封装

1.makefile实现

.PHONY:all
all:server_udp client_udpserver_udp:UdpServerMain.ccg++ -o $@ $^ -std=c++17
client_udp:UdpClientMain.ccg++ -o $@ $^ -std=c++17.PHONY:clean
clean:rm -f server_udp client_udp

2.UdpServer的InitServer()实现

a.创建套接字

返回值为一个文件描述符

void InitServer(){// 1.创建套接字_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "socket: " << strerror(errno);Die(1);}LOG(LogLevel::INFO) << "socket success, sockfd is : " << _sockfd;}

b.bind套接字

void InitServer(){// 1.创建套接字_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "socket: " << strerror(errno);Die(1);}LOG(LogLevel::INFO) << "socket success, sockfd is : " << _sockfd;// 2.填充网络信息,再进行bindstruct sockaddr_in local;bzero(&local,sizeof(local));local.sin_family = AF_INET;local.sin_port = ::htons(_port); //要被发送给对方的,即要发送到网络中local.sin_addr.s_addr = ::inet_addr(_ip.c_str());//1.string ip -> 4bytes  2.newwork order// 2.1 bindint n = ::bind(_sockfd,CONV(&local),sizeof(local));}

bind的本质就是将我们对应的_sockfd设置进入内核中

这些往后都是套路

3.Start函数的实现

void Start(){_isrunning = true;while(true){char inbuffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = ::recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,CONV(&peer),&len);if(n > 0){//1.消息内容 && 2.谁给我发的inbuffer[n] = 0;LOG(LogLevel::DEBUG) << "client say@ " << inbuffer;std::string echo_string = "echo# ";echo_string += inbuffer;::sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,CONV(&peer),sizeof(peer));}}}

三.client实现

对于我们的client,我们就不进行封装了,我们就直接进行对应的实现即可(有时间的可以尝试实现封装)

#include "UdpClient.hpp"
#include <iostream>
#include <cstring>
#include <string.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Common.hpp"// ./client_udp serverip serverport
int main(int argc,char* argv[])
{if(argc != 3){std::cerr << "Usage: " << argv[0] << " serverip serverport" << std::endl;Die(USAGE_ERR);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);//1.创建socketint sockfd = ::socket(AF_INET,SOCK_DGRAM,0);if (sockfd < 0){std::cerr << "scoket error" << std::endl;Die(SOCKET_ERR);}//1.1填充server信息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());//2.client donewhile(true){std::cout << "please Enter# " << std::endl;std::string message;std::getline(std::cin,message);// client 不需要进行bind吗? socket <->socket// client 必须要有自己的ip和端口!但是客户端,不需要自己显示调用bind!!// 而是,客户端首次sendto消息的时候,由OS自动进行bind// 1.如何理解client自动随机bind端口号?    一个端口号,由OS自动进行bind(客户端不需要被人找到)// 2.如何理解server需要显示的bind?        服务器端口号必须稳定,所以这里不能由OS随机bindint n = ::sendto(sockfd,message.c_str(),message.size(),0,CONV(&server),sizeof(server));(void)n;struct sockaddr_in temp;socklen_t len = sizeof(temp);char buffer[1024];n = ::recvfrom(sockfd,buffer,sizeof(buffer)-1,0,CONV(&temp),&len);if(n > 0){buffer[n] = 0;std::cout << buffer << std::endl;}}return 0;
}

所以这里的客户端由我们对应的OS自动进行随机bind(不用被别人知道)

四.测试

打印对应的发送消息的客户端的IP + 端口

客户端进行修改:

#ifndef __UDP_SERVER_HPP__
#define __UDP_SERVER_HPP__#include <iostream>
#include <string>
#include <memory>
#include <cstring>
#include <errno.h>
#include <strings.h>#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "Common.hpp"
#include "Log.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:UdpServer(const std::string & ip = gdefaultip,uint16_t port = gdefaultport):_sockfd(gsockfd),_ip(ip),_port(port),_isrunning(false){}void InitServer(){// 1.创建套接字_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "socket: " << strerror(errno);Die(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success, sockfd is : " << _sockfd;// 2.填充网络信息,再进行bindstruct sockaddr_in local;bzero(&local,sizeof(local));local.sin_family = AF_INET;local.sin_port = ::htons(_port); //要被发送给对方的,即要发送到网络中local.sin_addr.s_addr = ::inet_addr(_ip.c_str());//1.string ip -> 4bytes  2.newwork order// 2.1 bind :: 设置进入内核中int n = ::bind(_sockfd,CONV(&local),sizeof(local));if(n < 0){LOG(LogLevel::FATAL) << "bind: " << strerror(errno);Die(BIND_ERR);}LOG(LogLevel::INFO) << "bind success";}void Start(){_isrunning = true;while(true){char inbuffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = ::recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,CONV(&peer),&len);if(n > 0){//1.消息内容 && 2.谁给我发的uint16_t clientport = ::ntohs(peer.sin_port);std::string clientip = ::inet_ntoa(peer.sin_addr);inbuffer[n] = 0;std::string clientinfo = clientip + " : " + std::to_string(clientport) + " # " + inbuffer;LOG(LogLevel::DEBUG) << clientinfo;std::string echo_string = "echo# ";echo_string += inbuffer;::sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,CONV(&peer),sizeof(peer));}}}~UdpServer(){}private:int _sockfd;uint16_t _port;  //服务器的端口std::string _ip; //服务器的IPbool _isrunning; //服务器运行状态
};#endif

五.本地通信改成网络通信

"ServerMain.cc"#include "UdpServer.hpp"// ./server_udp localip localport
int main(int argc,char* argv[])
{if(argc != 3){std::cerr << "Usage: " << argv[0] << " localip localport" << std::endl;Die(USAGE_ERR);}std::string ip = argv[1];uint16_t port = std::stoi(argv[2]);ENABLE_CONSOLE_LOG();std::unique_ptr<UdpServer> svr_uptr = std::make_unique<UdpServer>(ip,port);svr_uptr->InitServer();svr_uptr->Start();return 0;
}

所以,我们的服务器根本就不需要ip,只要一个port来标识唯一性就行了

"Server.hpp"#ifndef __UDP_SERVER_HPP__
#define __UDP_SERVER_HPP__#include <iostream>
#include <string>
#include <memory>
#include <cstring>
#include <errno.h>
#include <strings.h>#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "Common.hpp"
#include "Log.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:UdpServer(uint16_t port = gdefaultport):_sockfd(gsockfd),_port(port),_isrunning(false){}void InitServer(){// 1.创建套接字_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "socket: " << strerror(errno);Die(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success, sockfd is : " << _sockfd;// 2.填充网络信息,再进行bindstruct sockaddr_in local;bzero(&local,sizeof(local));local.sin_family = AF_INET;local.sin_port = ::htons(_port); //要被发送给对方的,即要发送到网络中local.sin_addr.s_addr = INADDR_ANY;// 2.1 bind :: 设置进入内核中int n = ::bind(_sockfd,CONV(&local),sizeof(local));if(n < 0){LOG(LogLevel::FATAL) << "bind: " << strerror(errno);Die(BIND_ERR);}LOG(LogLevel::INFO) << "bind success";}void Start(){_isrunning = true;while(true){char inbuffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = ::recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,CONV(&peer),&len);if(n > 0){//1.消息内容 && 2.谁给我发的uint16_t clientport = ::ntohs(peer.sin_port);std::string clientip = ::inet_ntoa(peer.sin_addr);inbuffer[n] = 0;std::string clientinfo = clientip + " : " + std::to_string(clientport) + " # " + inbuffer;LOG(LogLevel::DEBUG) << clientinfo;std::string echo_string = "echo# ";echo_string += inbuffer;::sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,CONV(&peer),sizeof(peer));}}}~UdpServer(){}private:int _sockfd;uint16_t _port;  //服务器的端口bool _isrunning; //服务器运行状态
};#endif
"Main.cc"#include "UdpServer.hpp"// ./server_udp localport
int main(int argc,char* argv[])
{if(argc != 2){std::cerr << "Usage: " << argv[0] << " localport" << std::endl;Die(USAGE_ERR);}uint16_t port = std::stoi(argv[1]);ENABLE_CONSOLE_LOG();std::unique_ptr<UdpServer> svr_uptr = std::make_unique<UdpServer>(port);svr_uptr->InitServer();svr_uptr->Start();return 0;
}

六.InetAddr封装 以及 代码的修改

1.InetAddr的封装

后续我们的网络转主机,就使用这个

#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 PortNetToHost(){_port = ::ntohs(_net_addr.sin_port);}void IpNetToHost(){char ipbuffer[64];const char* ip = ::inet_ntop(AF_INET,&_net_addr.sin_addr,ipbuffer,sizeof(ipbuffer));_ip = ipbuffer;(void)ip;}
public:InetAddr(){}InetAddr(const struct sockaddr_in& addr):_net_addr(addr){PortNetToHost();IpNetToHost();}InetAddr(uint16_t port):_port(port){_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;
};

2.代码整体修改

#ifndef __UDP_SERVER_HPP__
#define __UDP_SERVER_HPP__#include <iostream>
#include <string>
#include <memory>
#include <cstring>
#include <errno.h>
#include <strings.h>#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "InetAddr.hpp"
#include "Common.hpp"
#include "Log.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:UdpServer(uint16_t port = gdefaultport):_sockfd(gsockfd),_addr(port),_isrunning(false){}void InitServer(){// 1.创建套接字_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "socket: " << strerror(errno);Die(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success, sockfd is : " << _sockfd;// 2.1 bind :: 设置进入内核中int n = ::bind(_sockfd,_addr.NetAddr(),_addr.NetAddrLen());if(n < 0){LOG(LogLevel::FATAL) << "bind: " << strerror(errno);Die(BIND_ERR);}LOG(LogLevel::INFO) << "bind success";}void Start(){_isrunning = true;while(true){char inbuffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = ::recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,CONV(&peer),&len);if(n > 0){//1.消息内容 && 2.谁给我发的InetAddr cli(peer);inbuffer[n] = 0;std::string clientinfo = cli.Ip() + " : " + std::to_string(cli.Port()) + " # " + inbuffer;LOG(LogLevel::DEBUG) << clientinfo;std::string echo_string = "echo# ";echo_string += inbuffer;::sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,CONV(&peer),sizeof(peer));}}_isrunning = false;}~UdpServer(){if(_sockfd > gsockfd){::close(gsockfd);}}private:int _sockfd;InetAddr _addr;bool _isrunning; //服务器运行状态
};#endif
http://www.dtcms.com/a/596896.html

相关文章:

  • OpenAI 新推 GPT-5-Codex-Mini:一款针对开发者的轻量级编码助手
  • GPTs智能体案例解析(小红书文案、流程图设计与编辑)
  • 广告宣传网站免费行情软件网站大全
  • 机器学习过拟合和正则化
  • 【SpringBoot】35 核心功能 - 高级特性- Spring Boot 中的Profile 环境配置详解
  • 富利建设集团有限公司网站网络维护怎么做
  • 【VSCode】【Clangd】Win下的基于LLVM/Clangd+Clangd插件+MINGW+CMake的VSCode配置C/C++开发环境的详细教程
  • Java大厂面试真题:从Spring Boot到AI微服务的三轮技术拷问(二)
  • openEuler入门学习教程,从入门到精通,云计算与 Linux 操作系统概述(1)
  • 3.2.STM32-LED闪烁LED流水灯蜂鸣器
  • 4-ARM-PEG-Methoxy(2),化学特性、纯化策略与表征方法
  • 4-ARM-PEG-DSPE(2),多功能PEG脂类偶联分子及反应原理
  • php做的网站安全吗宣传推广方案怎么写
  • 建设网站的发布与推广分销商城的服务商
  • Linux:文件 mmap 读写流程简析
  • Ros1 Noetic(本地)和Ros2 Humble(docker)之间相互通信及设置初始位姿
  • 使用 Docker Compose 部署 Redis 单节点 和 主从架构
  • 群晖NAS上使用最新版WordPress安装部署个人的博客或网站
  • 在Visio中保存PDF时去除空白区域
  • AI学习路线图2025:从入门到进阶的完整指南
  • 电气工程师求职问答-初级篇
  • Learn Git Branching
  • 凡科快图网站中医院网站源码
  • 太原网站快速排名提升河北婚庆网站建设定制
  • [Spring 注解详解]为何 @Service 不仅仅是 @Component?
  • 前端高频面试题之Vue(初、中级篇)
  • 谷歌云发布 Axion Arm 处理器与 TPU v5p,加速 AI 基础设施闭环
  • STM32H743-ARM例程43-SD_IAP_FPGA
  • 甘肃做网站找谁网上帮人卖东西的平台
  • 数据分析笔记01:数据分析概述