尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

UDP套接字编程

发布时间:2026/9/25 16:53:30

资讯中心
01
ARTICLE

UDP套接字编程

UDP套接字编程
目录1. 基础知识点2. 网络字节序列3. socket编程接口3.1 socket3.2 bind3.3 recvfrom 接收消息3.4 sendto 发送消息4. 简单的应用1. 基础知识点我们所有的网络通信的行为本质都是应用层进程间通信只不过之前我们学习的是通过管道通信现在是通过网络通信。对双方而言1.先把数据能到达自己的机器(ip地址) 2.找到指定的进程(port:端口号)。ip地址用来表示互联网中唯一的一台主机。端口号用来表示该指定的机器中进程的唯一性。{ip,port}可以表示互联网中唯一的一个进程称为套接字socket。如何理解 port端口号是一个16位数字标识当前主机上唯一的网络进程。网络进程回合port进行绑定关联。为什么不用进程pid它也可以标识一个进程但因为PID是动态变化的所以网络协议规定每个提供网络服务的软件必须提前绑定一个固定的端口号。每一个进程都有pid但不一定都有端口号。所以使用 socket 的好处是让其他模块进程管理和网络进行解耦。port专门用来进行网络通信。一个端口号与一个进程相关联一个端口号不可以和多个进程关联一个进程可以和多个端口号关联然后操作系统可以通过 hash 快速地通过端口号找到对应的进程。认识TCP协议此处我们先对TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识; 后面我们再详细讨论TCP的一些细节问题.传输层协议有连接可靠传输面向字节流认识UDP协议此处我们也是对UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识; 后面再详细讨论.传输层协议无连接不可靠传输面向数据报这可靠和不可靠都是中性词。“可靠”就是“发出去的数据对方百分之百确定收到了且顺序完全正确”“不可靠”就是“尽力发但到不到、到得全不全、乱没乱序我不负责”。TCP为了保证数据万无一失内置了一套极其严苛的“确认-重传”机制。UDP不提供TCP那一套任何保证错误也不会非常多适合对数据可以可靠性要求不高的场景。事实上UDP反而用的多TCP这么靠谱干嘛还要UDP这是因为“可靠”是有巨大代价的这个代价就是时间延迟和资源性能,要做更多的工作更复杂。2. 网络字节序列内存中的多字节数据相对于内存地址有大端和小端之分磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分网络数据流同样有大端小端之分。那么如何定义网络数据流的地址呢发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出。接收主机把从网络上接到的字节依次保存在接收缓冲区中也是按内存地址从低到高的顺序保存。因此网络数据流的地址应这样规定先发出的数据是低地址后发出的数据是高地址。TCP/IP协议规定网络数据流应采用大端字节序即低地址高字节。不管这台主机是大端机还是小端机都会按照这个TCP/IP规定的网络字节序来发送/接收数据。如果当前发送主机是小端就需要先将数据转成大端否则就忽略直接发送即可。举个例子比如说数据是4字节的 0x aa bb cc dd大端存储内存中就是aa bb cc dd低地址就是aa需要先发送 aa 小端存储就是 dd cc bb aa需要先发送 dd。但 TCP/IP 协议规定 我们要以 aa bb cc dd 的方式发送即先发送 aa大端可读性比较好。为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。h:host主机n:net网络l:long32位s:short 16位。#include arpa/inet.h uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);3. socket编程接口网络编程的时候socket是有很多类别的我们重点放在第二点。unix socket域间socket -同一台机器上文件路径命名管道本机内部进行通信。网络socketipport网络通信也可以本地通信。原始socket应用层不使用 TCP/UDP直接绕过传输层 访问网络层和数据链路层。通常用来编写一些网络工具网络编程的时候又有不同的应用场景理论上而言我们应该给每种场景都设计一套编程接口但设计者想用同一套接口完成所以提出了struct sockaddr类型大部分人网络编程接口要求传入 struct sockaddr* 类型了内部可以通过判断struct sockaddr 的前两个字节然后强转为struct sockaddr_in 或 struct sockaddr_un也就 struct sockaddr 是基类。为什么不使用 void*因为当设计这些接口时C语言不支持 void*。3.1 socket创建 socket 文件描述符指向套接字对象 (TCP/UDP, 客户端 服务器)成功返回一个文件描述符错误返回-1错误吗被设置。#include sys/types.h #include sys/socket.h int socket(int domain, int type, int protocol);domain 标识设置成哪种通信AF_UNIX 域间通信AF_INET 网络通信是一个宏是2。type 标识套接字类型一般经常使用的两种SOCK_STREAM 流式套接 通常时TCP 套接字SOCK_DGRAM 面向数据报无连接UDP使用。SOCK_RAW 底层原始套接。第三个参数表示你是使用TCP协议还是UDP协议在网络通信中前两个参数固定后第三个参数缺省为0即可。3.2 bind绑定端口号 (TCP/UDP, 服务器)第一个参数是 socket 返回的文件描述符第二个实际传入的是struct sockaddr_in也是就是网络具体信息内容如下图所示第三个参数表示传入结构体的长度。绑定成功返回0失败返回-1。#include netinet/in.h #include arpa/inet.h int bind(int socket, const struct sockaddr *address, socklen_t address_len);sockaddr_in 有一个成员是 sin_family 这里看不出来因为使用了##可以把位于它两侧的符号合成一个符号它允许宏定义从分离的文本片段创建标识符例如#define Add_TO_SUM(num, value) sum##num value; ... ADD_TO_SUM(5, 10);最后变成 sum5 10。这样的连接必须产生要合成合法的标识符否则其结果就是未定义的。所以 struct sockaddr_in 中有 sin_family 这个成员传入sin_然后拼接 family类型是 sa_family_t 也就是无符号短整型作用是指定这个地址结构使用的是哪种地址族对于 IPv4 来说它必须被设成 AF_INET。因为套接字 API 用的是通用地址结构 struct sockaddr它本身不知道具体是 IPv4、IPv6 还是 Unix 域。所以每个具体结构如 sockaddr_in的第一个字段都用来打标签告诉内核实际类型。通过上面看struct in_addr 的定义发现是一个uint32_t 类型。inet_addr可以将字符串类型的 IP 地址 转化为 4 字节 IP 地址并且变成网络序列。inet_ntoa将4字节网络序列的 IP 地址转化为字符串。#include arpa/inet.h in_addr_t inet_addr(const char *cp); char* inet_ntoa(struct in_addr in);3.3 recvfrom 接收消息ssize_t recvfrom(int sockfd,void *buf,size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen);返回值是实际收到多少字节buf 指向用户缓冲区len是期望收到多少字节sockfd 就是之前创建的套接字从哪个套接字的接收队列里取数据。flags表示收数据的模式0指阻塞式收消息。后两个是输入输出型参数src_addr 表示这条数据是谁发来的addrlen 调用前表示告诉内核 src_addr 缓冲区有多大调用后内核写回实际地址结构的长度因为的第五个参数声明为 struct sockaddr*它不知道你实际传入的是哪种地址结构可能是struct sockaddr_in 也可能是 struct sockaddr_un。所以需要 addrlen 来传递长度信息。是为了让通用代码能处理多种地址类型、检测截断、以及精确地回发数据。如果世界上只有 IPv4 一种地址那确实没啥用但套接字 API 要支持 IPv4/IPv6/Unix 域等就必须有它默认情况下UDP 的 recvfrom 就是“来着不拒”。只要数据报的目标 IP 和端口匹配你的套接字不管来源是谁任何源 IP、任何源端口内核都会把它放进该套接字的接收队列。recvfrom 只是从队列里取一个出来并把来源地址填到 src_addr 里告诉你。并给我们对方也就是 client 的socket 信息ip和port)。3.4 sendto 发送消息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阻塞式。dest_addr 指向目标地址的结构体指针因为发送消息前要先知道服务端ip和addr并且套接字 API 类型是 IPv4/IPv6/Unix 接收方区分的自己不用在这里填写自己的信息。len 是结构体的实际大小。成功返回实际发送字节数失败返回 -1。每个套接字socket都有自己独立的发送缓冲区和接收缓冲区。阻塞Blocking描述的是当某个操作无法立即完成时程序是“停下来等”还是“立刻返回一个错误”。netstat -anup a:all n:num u:udp p:process。 Foreign Address 0.0.0.0:*表示接收任何客户端的请求。n的作用是把能显示成数字的都显示成数字例如以数字方式显示出localhost的ip地址。服务器提供服务需要提供自己的 ip和端口号。客户端一定也需要绑定自己的套接字和网络信息但是不需要显示绑定bindIP 也不用写内核会自动帮你填。client会在首次发送数据的时候会自动进行绑定不用手写。client 也需要端口但我们希望它绑定随机端口。自己也可以绑定但是非常不推荐因为有很多客户端程序当使用不同服务时可能因为要使用同一个端口发生冲突。但服务端的端口号一般是众所周知不改变的127.0.0.1本地回环它代表“本机自己”通常用来进行网络csclient,server在一台机器的测试。数据不离开本机整个127.0.0.0/8网段都是回环前 8 位是固定的网络号剩下的 24 位可以随意变化只能进行本地通信。所以进程间本地通信也可以使用网络通信127.0.0.0/8。可以发现客户端已经自动进行了绑定端口号。我们服务端使用云服务器不能绑定云服务器提供的公网IP因为云服务器的公网 IP 根本不在你的服务器网卡上而在云服务商的网关上我们的的服务器只拥有一个内网 IP可以通过ip addr查看内网 ip因为 bind只能绑定“你这台机器网卡上真实配置的 IP”。云服务器的公网 IP 不在你的网卡上所以内核会直接拒绝。如果是真的linux 环境可以直接 bind IP但我们强烈不推荐给服务器 bind 固定的IP因为如果绑定了且有两张网卡就只能收到一个 socket 的信息所以更推荐服务端本地任意 IP绑定的方式只要端口相同都会给绑定这个端口的进程//ip的动态绑定 local.sin_addr.s_addr INADDR_ANY;再次运行可以发现 IP 地址变成了 0.0.0.0表示所有 IP 都可以。UDP 是无连接的服务器 bind 到0.0.0.0:8888 后没有固定对端所以Foreign Address 是 0.0.0.0:*表示可以接收任意IP和端口。云服务器大部分的端口号是被腾讯云阿里云拦截的。我们要能够进行数据的发送需要开发指定的多个端口需要开发指定的多个端口 808081818888。怎么开放云服务器的网络后端防火墙安全组中开放。0.0.0.0 允许所有地址ALL表示是 UDPTCP 所有端口。4. 简单的应用26/9 · 哈哈/linux_Tencent - 码云 - 开源中国一共有三个udp程序。1. windows 充当 client实现简单的 echo server服务器收到什么就返回什么。windows端socket接口与linux端差别不会很大接口几乎完全一样因为TCP/IP 协议是在内核中的所以 windows linux 都是类似的。windows代码#include iostream #include cstdio #include string #include cstdlib #include WinSock2.h #include Windows.h #pragma warning(disable:4996)//禁掉这个错误 #pragma comment(lib, ws2_32.lib)//使用这个库 uint16_t serverport 8888;//自己选端口 std::string serverip 0.0.0.0;//自己的云服务器ip int main() { //差别1 WSADATA wsd;//windows 套接字 WSAStartup(MAKEWORD(2, 2), wsd);//选择2.2版本名称。固定写法初始化库 struct sockaddr_in server; memset(server, 0, sizeof(server)); server.sin_family AF_INET;//与linux相同 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 SOCKET_ERROR) { std::cout socker error std::endl; return 1; } std::string message; char buffer[1024]; while (true) { std::cout Please Enter ; std::getline(std::cin, message); sendto(sockfd, message.c_str(), (int)message.size(), 0, (struct sockaddr*)server, sizeof(server));//c中struct 可以省略像class struct sockaddr_in temp; int len sizeof(temp); int s recvfrom(sockfd, buffer, 1023, 0, (sockaddr*) temp, len); if (s 0) { buffer[s] 0; std::cout server echo# buffer std::endl; } } //差别2 closesocket(sockfd); WSACleanup();//固定写法清理库 return 0; }2.客户端输入命令服务端将处理结果返回。使用了popen函数。#include stdio.h FILE* popen(const char* command, const char* type); int pclose(FILE* stream);底层自动帮我们创建管道fork()为我们创建子进程让子进程程序替换执行命令command结果返回到FILE*中type表示将来对返回值文件的读取方式。失败返回NULL。如果命令执行有结果也可以访问文件查看。pclose 关闭文件流。std::bind 的参数分两种命运bind 里写的命运是否出现在最终签名具体值_sockfd、buffer立即固定被吃掉❌ 不出现占位符_1、_2留个坑等调用时填✅ 出现clear只会清理client的屏幕因为控制序列只有直接送到终端才会生效而 ls 会直接把结果文件名列表写到stdout这里 stdout 是 popen 创建的管道不是终端3.简单的聊天室udp 是全双工的可以一个线程读一个线程写不会互相干扰。任务是一个类对象函数也是对象所以可以任务可以是仿函数也可以是函数。每有一条消息进程池中的进程就会竞争式的转发消息。云服务器如何将文件运输到本地:命令全称方向用途szsendZMODEM远程 → 本地把远程服务器上的文件下载到本地rzreceiveZMODEM本地 → 远程把本地文件上传到远程服务器记忆sz是“送出去”rz是“收进来”都是从远程服务器的视角。scp 是Secure Copy的缩写一个基于SSH 协议的加密文件传输命令。它可以在本地和远程主机之间或者两台远程主机之间复制文件/目录。因为走 SSH 通道所以传输是加密的scp [选项] 源路径目标路径本地路径直接写如/home/file.txt远程路径写成用户名主机IP:路径如wdz106.55.106.178:/home本篇结束
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。