linux socket编程之udp(实现客户端和服务端消息的发送和接收)
目录
一.创建socket套接字(服务器端)
二.bind将prot与端口号进行绑定(服务器端)
2.1填充sockaddr_in结构
2.2bind绑定端口
三.直接通信(服务器端)
3.1接收客户端发送的消息
3.2给客户端发送消息
四.客户端通信
4.1创建socket套接字
4.2客户端bind问题
4.3直接通信即可
4.3.1构建目标主机的socket信息
4.3.2给服务端发送消息
4.3.3.接收服务端发送过来的消息
五.效果展示
5.1使用127.0.0.1本地环回测试
5.2使用公网ip测试
六.代码演示
6.1UdpServer.hpp
6.2UdpClient.cc
6.3InetAddr.hpp
6.4LockGuard.hpp
6.5Log.hpp
6.6main.cc
6.7makefile
一.创建socket套接字(服务器端)
int socket(int domain, int type, int protocol);
domain:选择你要使用的网络层协议 一般是ipv4,也就是AF_INET
type:选择你要使用的应用层协议,这里我们选择udp,也就是SOCK_DGRAM
protocol:这里我们先设置成0
成功返回文件描述符,失败返回-1
//1.创建socket套接字
_socketfd = socket(AF_INET,SOCK_DGRAM,0);
if(_socketfd < 0)
{LOG(FATAL, "SOCKET ERROR, %s, %d\n", strerror(errno), errno);exit(SOCKET_ERROR);
}
二.bind将prot与端口号进行绑定(服务器端)
2.1填充sockaddr_in结构
uint16_t htons(uint16_t hostshort);//将端口号从主机序列转成网络序列
in_addr_t inet_addr(const char *cp);//将ip从主机序列转成网络序列 + 字符串风格ip转成点分十进制ip
uint16_t ntohs(uint16_t netshort);//将端口号从网络序列转成主机序列
char *inet_ntoa(struct in_addr in);//将ip从网络序列转成主机序列 + 点分十进制ip转成字符串风格ip

网络通信:struct sockaddr_in
本地通信:sockaddr_un
16位地址类型表明了他们是网络通信还是本地通信
16位地址类型:sin_family
16位端口号:sin_port
32位ip地址:sin_addr.s_addr
//填充sockaddr_in结构
struct sockaddr_in local;
local.sin_family = AF_INET;//表明是网络通信
local.sin_port = htons(_prot);//将主机序列转成网络序列
local.sin_addr.s_addr = inet_addr("0.0.0.0");//将字符串类型的点分十进制ip转成四字节ip,并转成网络序列
2.2bind绑定端口
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd:要绑定的socket套接字的文件描述符
struct sockaddr *:包含ip地址+端口号的结构体(类型不一样需要进行强转)
socklen_t addrlen:sockaddr_in结构体的大小
//bind绑定端口号
int n = bind(_socketfd,(sockaddr *)&local,sizeof(local));//需要将sockaddr_in强转成sockaddr
if(n < 0)
{LOG(FATAL, "BIND ERROR, %s, %d\n", strerror(errno), errno);exit(BIND_ERROR);
}
三.直接通信(服务器端)
3.1接收客户端发送的消息
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
sockfd:套接字的文件描述符
buf:缓存区
len:缓存区的大小,单位是字节
flags:暂时设置为0
src_addr:数据来自于哪
addrlen:struct sockaddr结构体的大小
//接收客户端发送来的消息
char buffer[1024];
sockaddr_in peer;
socklen_t len = sizeof(peer);
int n = recvfrom(_socketfd,buffer,sizeof(buffer) - 1,0,(sockaddr*)&peer,&len);
3.2给客户端发送消息
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd:套接字的文件描述符
buf:缓存区
len:缓存区的大小,单位是字节
flags:设置为0
src_addr:要将数据发送给谁
addrlen:struct sockaddr结构体的大小
//处理客户端发送的消息,并且将结果返回给客户端
buffer[n] = 0;
sendto(_socketfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);
四.客户端通信
4.1创建socket套接字
// 1. 创建socket
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{std::cerr << "socket error" << std::endl;
}
4.2客户端bind问题
客户端不需要显示的bind,os会自动帮你绑定端口号!!!
试想一下,你的手机上有抖音和微信两个客户端小程序,如果抖音客户端bind了8080这个端口,微信也想要bind8080这个端口,那么这时候就会出现一个问题,一个端口号被两个进程竞争!!!结果就是,抖音和微信不可能同时启动。
所以解决方法就是:udp client首次发送数据的时候,OS会自己自动随机的给client进行bind
4.3直接通信即可
4.3.1构建目标主机的socket信息
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());
4.3.2给服务端发送消息
//给服务端发送消息
std::cout << "Please Enter# ";
std::getline(std::cin, message);
sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));
4.3.3.接收服务端发送过来的消息
//接收服务端发送过来的消息
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
char buffer[1024];
ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len);
if(n > 0)
{buffer[n] = 0;std::cout << "server echo# " << buffer << std::endl;
}
五.效果展示
5.1使用127.0.0.1本地环回测试

5.2使用公网ip测试

六.代码演示
6.1UdpServer.hpp
#pragma once#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Log.hpp"
#include "InetAddr.hpp"enum
{SOCKET_ERROR = 1,//创建套接字失败BIND_ERROR,//bind绑定端口失败USAGE_ERROR//启动udp服务失败
};int default_socketfd = -1;
class UdpServer
{
public:UdpServer(uint16_t prot): _socketfd(default_socketfd),_prot(prot){} ~UdpServer(){}void Init(){//1.创建socket套接字_socketfd = socket(AF_INET,SOCK_DGRAM,0);if(_socketfd < 0){LOG(FATAL, "SOCKET ERROR, %s, %d\n", strerror(errno), errno);exit(SOCKET_ERROR);}LOG(INFO, "socket create success, sockfd: %d\n", _socketfd);//2.bind 将socket套接字和端口号进行绑定//填充sockaddr_in结构struct sockaddr_in local;local.sin_family = AF_INET;//表明是网络通信local.sin_port = htons(_prot);//将主机序列转成网络序列local.sin_addr.s_addr = inet_addr("0.0.0.0");//将字符串类型的点分十进制ip转成四字节ip,并转成网络序列//local.sin_addr.s_addr = INADDR_ANY;//bind绑定端口号int n = bind(_socketfd,(sockaddr *)&local,sizeof(local));//需要将sockaddr_in强转成sockaddrif(n < 0){LOG(FATAL, "BIND ERROR, %s, %d\n", strerror(errno), errno);exit(BIND_ERROR);}LOG(INFO, "socket bind success\n");}void Stat(){//3.直接开始通信while(true){//接收客户端发送来的消息char buffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer); ssize_t n = recvfrom(_socketfd,buffer,sizeof(buffer) - 1,0,(struct sockaddr*)&peer,&len); if(n > 0){InetAddr addr(peer);LOG(DEBUG, "get message from [%s:%d]: %s\n", addr.Ip().c_str(), addr.Port(), buffer);//处理客户端发送的消息,并且将结果返回给客户端buffer[n] = 0;sendto(_socketfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);}}}private:uint16_t _prot;int _socketfd;
};
6.2UdpClient.cc
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>void Usage(std::string proc)
{std::cout << "Usage:\n\t" << proc << " serverip serverport\n"<< std::endl;
}// ./udpclient serverip serverport
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}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 << "socket error" << std::endl;}// 2. client要不要bind?一定要,client也要有自己的IP和PORT。要不要显式[和server一样用bind函数]的bind?不能!不建议!!// a. 如何bind呢?udp client首次发送数据的时候,OS会自己自动随机的给client进行bind --- 为什么?防止client port冲突。要bind,必然要和port关联!// b. 什么时候bind呢?首次发送数据的时候// 构建目标主机的socket信息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());std::string message;// 2. 直接通信即可while(true){//给服务端发送消息std::cout << "Please Enter# ";std::getline(std::cin, message);std::cout<<message<<std::endl;sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));//接收服务端发送过来的消息struct sockaddr_in peer;socklen_t len = sizeof(peer);char buffer[1024];ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len);if(n > 0){buffer[n] = 0;std::cout << "server echo# " << buffer << std::endl;}}return 0;
}
6.3InetAddr.hpp
#pragma once#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>class InetAddr
{
private:void GetAddress(std::string *ip, uint16_t *port){*port = ntohs(_addr.sin_port);*ip = inet_ntoa(_addr.sin_addr);}public:InetAddr(const struct sockaddr_in &addr) : _addr(addr){GetAddress(&_ip, &_port);}std::string Ip(){return _ip;}uint16_t Port(){return _port;}~InetAddr(){}private:struct sockaddr_in _addr;std::string _ip;uint16_t _port;
};
6.4LockGuard.hpp
#include <iostream>
#include <pthread.h>class LockGuard
{
public:LockGuard(pthread_mutex_t *mutex):_mutex(mutex){pthread_mutex_lock(_mutex); // 构造加锁}~LockGuard(){pthread_mutex_unlock(_mutex);// 析构释放锁}
private:pthread_mutex_t *_mutex;
};
6.5Log.hpp
#pragma once
#include <cstdio>
#include <iostream>
#include <string>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdarg.h>
#include <fstream>#include "LockGuard.hpp"bool IsSave = false;//是否向文件中写入
const std::string logname = "log.txt";//日志信息写入的文件路径// 日志是有等级的
enum Level
{DEBUG = 0,INFO,WARNING,ERROR,FATAL
};// 将日志的登记由整形转换为字符串
std::string LevelToString(int level)
{switch (level){case DEBUG:return "Debug";case INFO:return "Info";case WARNING:return "Warning";case ERROR:return "Error";case FATAL:return "Fatal";default:return "Unknown";}
}// 获取时间
std::string GetTimeString()
{time_t curr_time = time(nullptr);struct tm *format_time = localtime(&curr_time);if (format_time == nullptr)return "None"; // 没有获取成功char time_buffer[1024];snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",format_time->tm_year + 1900, // 这里的year是减去1900之后的值,需要加上1900format_time->tm_mon + 1, // 这里的mon是介于0-11之间的,需要加上1format_time->tm_mday,format_time->tm_hour,format_time->tm_min,format_time->tm_sec);return time_buffer;
}//将日志信息写入到文件中
void SaveFile(const std::string &filename, const std::string &message)
{std::ofstream out(filename, std::ios::app);if (!out.is_open()){return;}out << message;out.close();
}pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;//定义锁 支持多线程
// 日志是有格式的
// 日志等级 时间 代码所在的文件名/行数 日志的内容
void LogMessage(std::string filename, int line, bool issave, int level, const char *format, ...)
{std::string levelstr = LevelToString(level);std::string timestr = GetTimeString();pid_t selfid = getpid();char buffer[1024];va_list arg;//定义一个void* 指针va_start(arg, format);//初始化指针,将指针指向可变参数列表开始的位置vsnprintf(buffer, sizeof(buffer), format, arg);//将可变参数列表写入到buffer中va_end(arg);//将指针置空std::string message = "[" + timestr + "]" + "[" + levelstr + "]" +"[" + std::to_string(selfid) + "]" +"[" + filename + "]" + "[" + std::to_string(line) + "] " + buffer + "\n";LockGuard lockguard(&lock);if (!issave){std::cout << message;//将日志信息打印到显示器中}else{SaveFile(logname, message);//将日志信息写入到文件}
}// C99新特性__VA_ARGS__
#define LOG(level, format, ...) do{ LogMessage(__FILE__, __LINE__,IsSave,level, format, ##__VA_ARGS__); }while(0)
#define EnableFile() do{ IsSave = true; }while(0)
#define EnableScreen() do{ IsSave = false; }while(0)
6.6main.cc
#include <iostream>
#include <memory>
#include "UdpServer.hpp"void Usage(std::string proc)
{std::cout << "Usage:\n\t" << proc << " local_port\n" << std::endl;
}// ./udpserver port
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(USAGE_ERROR);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>(port);usvr->Init();usvr->Stat();return 0;
}
6.7makefile
.PHONY:all
all:udpserver udpclient
udpclient:UdpClient.ccg++ -o udpclient UdpClient.cc -std=c++14
udpserver:main.ccg++ -o udpserver main.cc -std=c++14
.PHONY:clean
clean:rm -f udpserver
相关文章:
linux socket编程之udp(实现客户端和服务端消息的发送和接收)
目录 一.创建socket套接字(服务器端) 二.bind将prot与端口号进行绑定(服务器端) 2.1填充sockaddr_in结构 2.2bind绑定端口 三.直接通信(服务器端) 3.1接收客户端发送的消息 3.2给客户端发送消息 四.客户端通信 4.1创建socket套接字 4.2客户端bind问题 4.3直接通信即可…...
SAP HANA使用命令行快速导出导入
楔子 今天折腾了接近一下午,就为了使用SAP HANA自带的命令行工具来导出数据备份。 SAP HANA(后续简称Hana)是内存数据库,性能这一方面上还真没怕过谁。 由于SAP HANA提供了Hana Studio这个桌面工具来方便运维和DBA使用…...
goland做验证码识别时报“undefined: gosseract.NewClient”
gosseract 应该有 和 c 相关的配置库因此需要安装 cgo 并且启用 CGO_ENABLED 在cmd下面输入这个 go env -w CGO_ENABLED1 接着输入 go env 验证是否设置成功 解决了这个问题后 “undefined: gosseract.NewClient” 又出现了 # runtime/cgo …...
计算机网络 实验四 静态路由的配置与应用
一、实验目的 掌握路由器基础工作原理及静态路由协议机制熟练使用华为ENSP网络模拟器进行拓扑设计与设备配置建立系统化的网络故障排除思维通过实践验证静态路由在中小型网络中的部署优势 二、实验环境 硬件配置:标准PC终端软件工具:华为企业网络模拟…...
Vue自定义指令-防抖节流
Vue2版本 // 防抖 // <el-button v-debounce"[reset,click,300]" ></el-button> // <el-button v-debounce"[reset]" ></el-button> Vue.directive(debounce, { inserted: function (el, binding) { let [fn, event "cl…...
[每周一更]-(第140期):sync.Pool 使用详解:性能优化的利器
文章目录 一、什么是 sync.Pool?二、sync.Pool 的基本作用三、sync.Pool 的主要方法四、sync.Pool 的内部工作原理五、sync.Pool 适用场景六、使用示例示例 1:基本使用输出示例:示例 2:并发使用 七、一个基于 sync.Pool 的 **Benc…...
3.QT-信号和槽|自定义槽函数|自定义信号}自定义的语法}带参数的信号和槽(C++)
信号和槽 Linux信号 Signal 系统内部的通知机制. 进程间通信的方式. 信号源:谁发的信号.信号的类型:哪种类别的信号信号的处理方式:注册信号处理函数,在信号被触发的时候自动调用执行. Qt中的信号和Linux中的信号,虽…...
健康养生之道
在快节奏的现代生活中,健康养生不再是中老年人的专属话题,越来越多的人开始意识到,合理的养生方式是保持良好身体状态和生活质量的关键。 饮食养生是健康的基石。遵循 “食物多样、谷类为主” 的原则,保证每天摄入足够的蔬菜、…...
Spark-SQL核心编程3
数据加载与保存 通用方式: SparkSQL 提供了通用的保存数据和数据加载的方式。这里的通用指的是使用相同的API,根据不同的参数读取和保存不同格式的数据,SparkSQL 默认读取和保存的文件格式为parquet 数据加载方法: spark.read.lo…...
TVM计算图分割--Collage
1 背景 为满足高效部署的需要,整合大量优化的tensor代数库和运行时做为后端成为必要之举。现在的深度学习后端可以分为两类:1)算子库(operator kernel libraries),为每个DL算子单独提供高效地低阶kernel实现。这些库一般也支持算…...
elementUI中MessageBox.confirm()默认不聚焦问题处理
在项目中使用elementUI的MessageBox.confirm()出现了默认不聚焦的问题,默认确认按钮是浅色的,需要点击一下才会变成正常。面对这种问题,创建新组件,实现聚焦。替换默认的MessageBox.confirm() 解决 创建components/MessageBoxCo…...
【刷题Day20】TCP和UDP(浅)
TCP 和 UDP 有什么区别? TCP提供了可靠、面向连接的传输,适用于需要数据完整性和顺序的场景。 UDP提供了更轻量、面向报文的传输,适用于实时性要求高的场景。 特性TCPUDP连接方式面向连接无连接可靠性提供可靠性,保证数据按顺序…...
sql server 预估索引大小
使用deepseek工具预估如下: 问题: 如果建立一个数据类型是datetime的索引,需要多大的空间? 回答: 如果建立一个数据类型是 datetime 的索引,索引的大小取决于以下因素: 索引键的大小&#…...
利用 i2c 快速从 Interface 生成 Class
利用 i2c 快速从 Interface 生成 Class(支持 TS & ArkTS) 在日常 TypeScript 或 ArkTS 开发中,需要根据 interface 定义手动实现对应的 class,这既重复又容易出错。分享一个命令行工具 —— interface2class,简称…...
MCGS昆仑通太屏笔记
4.3寸:4013ef/e1 7寸:7032kw 特点: 如果是使用组态屏进行调试使用,选择com1如果是实际项目使用,选择com2 操作步骤: 先创建设备窗口,再创建用户界面 在设备窗口界面,依次设置如下…...
服务治理-搭建Nacos注册中心
运行nacos.sql文件。 将准备好的nacos目录和nacos.tar包上传。 192.168.59.101是我的虚拟机ip,8848是我们设置的访问端口号。...
网络--socket编程(2)
Socket 编程 TCP TCP 网络程序 和刚才 UDP 类似 . 实现一个简单的英译汉的功能 TCP socket API 详解 下面介绍程序中用到的 socket API, 这些函数都在 sys/socket.h 中。 socket(): • socket() 打开一个网络通讯端口 , 如果成功的话 , 就像 open() 一样返回一个…...
【FreeRTOS进阶】优先级翻转现象详解及解决方案
【FreeRTOS进阶】优先级翻转现象详解及解决方案 接下来我们聊聊优先级翻转这个经典问题。这个问题在实时系统中经常出现,尤其是在任务较多的场景下,而且问题定位起来比较麻烦。 什么是优先级翻转? 优先级翻转的核心定义很简单:…...
结合建筑业务讲述TOGAF标准处理哪种架构
TOGAF标准处理哪种架构 内容介绍业务架构业务策略,治理,组织和关键业务流程数据架构组织的逻辑和物理数据资产以及数据管理资源的结构应用架构待部署的各个应用程序,它们之间的交互以及与组织核心业务流程的关系的蓝图技术架构支持业务&#…...
C++入门小馆: 深入string类(一)
嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的pa…...
NHANES指标推荐:WWI
文章题目:Weight-adjusted waist circumference index with hepatic steatosis and fibrosis in adult females: a cross-sectional, nationally representative study (NHANES 2017-2020) DOI:10.1186/s12876-025-03706-4 中文标题:体重调整…...
2025.04.18|【Map】地图绘图技巧全解
Add circles Add circles on a Leaflet map Change tile Several background tiles are offered by leaflet. Learn how to load them, and check the possibilities. 文章目录 Add circlesChange tile 2025.04.18【Map】| 地图绘图技巧全解1. 准备工作2. 地理区域着色图&…...
PR第一课
目录 1.新建 2.PR内部设置 3.导入素材 4.关于素材窗口 5.关于编辑窗口 6.序列的创建 7.视频、图片、音乐 7.1 带有透明通道的素材 8.导出作品 8.1 打开方法 8.2 导出时,需要修改的参数 1.新建 2.PR内部设置 随意点开 编辑->首选项 中的任意内容&a…...
C# 预定义类型全解析
在 C# 编程中,预定义类型是基础且重要的概念。下面我们来详细了解 C# 的预定义类型。 预定义类型概述 C# 提供了 16 种预定义类型,包含 13 种简单类型和 3 种非简单类型。所有预定义类型的名称都由全小写字母组成。 预定义简单类型 预定义简单类型表…...
@EnableAsync+@Async源码学习笔记之六
接上文,我们本文分析 AsyncExecutionAspectSupport 的源码: package org.springframework.aop.interceptor;import java.lang.reflect.Method; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFu…...
Java CMS和G1垃圾回收器
举个真带劲的例子:把JVM内存比作你家的祖传旱厕 想象你有个祖传旱厕,分三个坑: 新坑区(年轻代):刚拉的屎热乎着(新对象)陈年坑(老年代):风干的屎…...
Vue+Notification 自定义消息通知组件 支持数据分页 实时更新
效果图: message.vue 消息组件 子组件 <template><div class"custom-notification"><div class"content"><span click"gotoMessageList(currentMessage.split()[1])">{{ currentMessage.split()[0] }}</…...
不规则曲面上两点距离求取
背景 在CT中求皮肤上两点间的弧长。由于人体表面并不是规则的曲面,不可能用圆的弧长求取方法来计算出两点间的弧长。 而在不规则的曲面上求两点的距离,都可以用类似测地线距离求取的方式来求取(积分),而转化为搜索路…...
Redis面试问题缓存相关详解
Redis面试问题缓存相关详解 一、缓存三兄弟(穿透、击穿、雪崩) 1. 穿透 问题描述: 缓存穿透是指查询一个数据库中不存在的数据,由于缓存不会保存这样的数据,每次都会穿透到数据库,导致数据库压力增大。例…...
性能比拼: Elixir vs Go
本内容是对知名性能评测博主 Anton Putra Elixir vs Go (Golang) Performance (Latency - Throughput - Saturation - Availability) 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准 对比 Elixir 和 Go 简介 许多人长期以来一直要求我对比 Elixir 和 Go。在本视频…...
