网络 UDP协议(C++|代码通过udp协议实现客户端与服务端之间的通信)

2023-05-16

这里写目录标题

  • udp通信编程
    • 各端的操作流程:
      • 服务端操作流程:
      • 客户端操作流程:第2,3步与服务端不同
    • socket接口介绍
      • udp客户服务端代码实现
  • 推荐阅读

socket套接字编程就是在网络程序中编写代码,通过一套套的接字接口实现网络程序的编写。

udp通信编程

udp是无连接、不可靠、面向数据报的传输层协议。
特点:支持多对多连接交互通信。尽最大努力交付,不保证可靠性,但速度最快
在这里插入图片描述

各端的操作流程:

服务端操作流程:

  1. 创建套接字端口:在内核中创建socket结构体,关联进程与网卡之间的联系
  2. 为套接字绑定地址信息:网络通信中的数据都必须带有源端IP、源端端口、对端IP、对端端口、协议。在内核创建的socket结构体中描述IP地址端口以及协议,(必须主动绑定,告诉客户端自己的地址信息,如果不绑定客户端就不知道该发往哪个服务端了)为了告诉操作系统发往哪个IP地址,哪个端口的数据是交给我来处理的
  3. 接收数据:告诉操作系统发往哪个地址和端口的数据应该交给我处理,操作系统一旦接收到发往这个地址和端口的数据,就会将这条数据放到对应的socket的接收缓冲区中,然后服务端从对应的socket的接收缓冲区中取出数据。
  4. 发送数据:将数据写入内核中的socket发送缓冲区中,操作系统选择合适的时候将数据封装发送出去
  5. 关闭套接字:释放资源

在这里插入图片描述

客户端操作流程:第2,3步与服务端不同

  1. 创建套接字:在内核中创建socket结构体,关联进程与网卡之间的联系
  2. 为套接字绑定地址信息:描述在内核中创建的socket结构体的源端地址信息;发送的数据中源端地址信息就是绑定的地址信息(不推荐主动绑定地址,降低端口冲突的概率,从而确保数据发送的安全性)
  3. 发送数据:将数据放到socket的发送缓冲区中,操作系统选择合适时候封装数据并发送数据。若socket发送数据的时候还没绑定地址,则操作系统会选择合适的地址进行绑定。
  4. 接收数据:将数据写入内核中的socket发送缓冲区中,操作系统选择合适的时候将数据封装发送出去
  5. 关闭套接字:释放资源

socket接口介绍

1、创建套接字int socket(int domain, int type, int protocol) 参数内容(domian:地址域(本地通信-AF_LOCAL、IPv4-AF_INET、IPv6-AF_INET6等)确定本次socket通信使用哪种协议版本的地址结构,不同的协议版本有不同的地址结构;type:套接字类型(流式套接字-SOCK_STREAM、数据报套接字-SOCK_DGRAM等);protocol:协议类型(TCP-IPPROTO_TCP、UDP-IPPROTO_UDP) ,默认为0-流式默认TCP,数据报默认UDP)
返回值:文件描述符-非负整数, 套接字所有其他接口的操作句柄,失败返回-1

2、为套接字绑定地址信息int bind(int sockfd, struct sockaddr *addr, socklen_t len)参数内容(sockfd:创建套接字返回的操作句柄;addr:要绑定的地址信息;len:要绑定的地址信息长度)
中间的参数结构体有很多种

struct sockaddr
{
	sa_family_t sa_family;
	char sa_data[14];
};

struct sockaddr_in
{
	sa_family sin_family;//地址域
	in_port_t sin_port;//端口号
	struct in_addr sin_addr;//IP地址
};

struct sockaddr_in6
{
	sa_family sin_family;//地址域
	in_port_t sin6_port;//端口号
	uint32_t sin6_flowinfo;
	struct in6_addr, sin6_addr;//IP地址
	unit32_t sin6_scope_id;
}

struct sockaddr_un
{
	__SOCKADDR_COMMON (sun_);
	char sun_path[108];
};

在这里插入图片描述

不同的地址结构,有统一的一个信息:前两个字节是地址域类型,bind可以绑定不同的地址结构,为了实现接口统一,因此用户定义地址结构的时候,需要定义自己需要的地址结构,例如IPv4就使用struct sockaddr_in,但是进行绑定的时候,统一类型强转成为sockaddr*类型
简单解析bind接口的实现

bind(fd,addr, len)
{
	if(addr->sa_family==AF_INET)
	{
		//绑定IPv4地址信息,这个结构体按照sockaddr_in进行解析
	}
	else if (addr->sa_family==AF_INET6)
	{
		//绑定IPv6地址信息,这个结构体按照sockaddr_in6进行解析
	}
	else if ...
}

3、接收数据,接收发送者地址便于回复ssize_t recvfrom(int sockfd, char *buf, int len, int flag, struct sockaddr *peer_addr, socklen *addrlen) 参数内容(sockfd:创建套接字返回的操作句柄;buf:一块缓冲区,用于接收从接收缓冲区中取出数据;len:想要接收的数据长度;flag:操作选项标志,默认为0,表示阻塞操作;peer_addr:发送方的地址信息;addrlen:想要获取的地址信息长度以及返回实际长度)
返回值:成功返回实际接收到的数据字节长度,失败返回-1

4、发送数据ssize_t sendto(int sockfd, char *data, int len, int flag, struct sickaddr *peer_addr, socklen_t addrlen) 参数内容(socket:socket操作句柄;data:要发送的数据地址;len:要发送数据长度;flag:默认为0,表示阻塞操作;peer_addr:接收方的地址信息;addrlen:地址信息长度)
返回值:成功返回实际发送的数据字节长度,失败返回-1

5、关闭套接字int close(int fd)

网络字节序的转换接口
uint32_t htonl(uint32_t hostlong) 主机字节序到网络字节序的转换
uint16_t htons(uint16_t hostshort)
uint32_t ntohl(uint32_t netlong)网络字节序到主机字节序的转换
uint16_t ntohs(uint16_t netshort)

in_addr_t inet_addr(const char *cp)将字符串的点分十进制IP地址转换成为网络字节序的整数IP地址
char *inet_ntoa(struct in_addr in)将网络字节序的整数IP地址转化为字符串点分十进制IP地址
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size)将网络字节序的整数IP地址转化为字符串IP地址-兼容IPv4和IPv6
int inet_pton(int af, const char *src, void *dst)将字符串的IP地址转换成为网络字节序的整数IP地址-兼容IPv4和IPv6

udp客户服务端代码实现

使用c++封装UdpSocket类,实例化的每一个对象都是一个udp通信套接字,并且能够通过成员接口实现udp通信流程
udpsocket.hpp

//udpsocket.hpp
#include <cstdio>
#include <string>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
using namespace std;
class UdpSocket
{
	public:
		UdpSocket()
			:_sockfd(-1)
		{}
		//创建套接字
		bool Socket()
		{
			_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
			if (_sockfd < 0)
			{
				perror("socket error");
				return false;
			}
			return true;
		}
		//为套接字绑定地址信息
		bool Bind(const string &ip, uint16_t port)
		{
			//定义IPv4地址结构体
			struct sockaddr_in addr;
			//地址信息赋值
			addr.sin_family = AF_INET;
			addr.sin_port = htons(port);//将主机字节序短整型型数据转化为网络字节序数据
			addr.sin_addr.s_addr = inet_addr(ip.c_str());//将字符串IP地址转化为网络字节序IP地址
			socklen_t len = sizeof(struct sockaddr_in);
			int ret = bind(_sockfd, (struct sockaddr*)&addr, len);
			if (ret < 0)
			{
				perror("bind error");
				return false;
			}
			return true;
		}
		//接收数据,获取发送端地址信息
		bool Recv(string *buf, string *ip=NULL, uint16_t *port=NULL)
		{
			struct sockaddr_in peer_addr;//用于接收发送端的地址信息
			socklen_t len = sizeof(struct sockaddr_in);
			char tmp[4096] = {0};
			int ret = recvfrom(_sockfd, tmp, 4096, 0, (struct sockaddr*)&peer_addr, &len);
			if (ret < 0)
			{
				perror("recvfrom error");
				return false;
			}
			buf->assign(tmp, ret);//assign从指定字符串中截取指定长度的数据到buf中
			if (port != NULL)
			{
				*port = ntohs(peer_addr.sin_port);//网络字节序到主机字节序的转换
			}
			if (ip != NULL)
			{
				*ip = inet_ntoa(peer_addr.sin_addr);//网络字节序到字符串IP地址的转换
			}
			return true;
		}
		//发送数据
		bool Send(const string &data, string &ip, const uint16_t port)
		{
			struct sockaddr_in addr;
			addr.sin_family = AF_INET;
			addr.sin_port = htons(port);
			addr.sin_addr.s_addr = inet_addr(ip.c_str());
			socklen_t len = sizeof(struct sockaddr_in);
			int ret = sendto(_sockfd, data.c_str(), data.size(), 0, (struct sockaddr*)&addr, len);
			if (ret < 0)
			{
				perror("sendto error");
				return false;
			}
			return true;
		}
		//关闭套接字
		bool Close()
		{
			if (_sockfd > 0)
			{
				close(_sockfd);
				_sockfd = -1;
			}
			return true;
		}
	private:
		int _sockfd;
};


udp_srv.cpp

#include <iostream>
#include <string>
#include "udpsocket.hpp"
using namespace std;

#define CHECK_RET(q) if((q)==false){return false;}
int main(int argc, char *argv[])
{
	//运行时有三个参数 udp_src 192.168.73.29 4096
	if (argc != 3)
	{
		cout << "Usage: ./udp_srv ip prot\n";
		return -1;
	}
	uint16_t port = stoi(argv[2]);
	string ip = argv[1];
	UdpSocket srv_sock;
	//创建套接字
	CHECK_RET(srv_sock.Socket());
	//绑定地址信息
	CHECK_RET(srv_sock.Bind(ip, port));
	while(1)
	{
		//接收数据
		string buf;
		string peer_ip;
		uint16_t peer_port;
		CHECK_RET(srv_sock.Recv(&buf, &peer_ip, &peer_port));
		cout << "client["<<peer_ip<<":"<<peer_port<<"] say: " << buf << endl;
		//发送数据
		buf.clear();
		cout << "server say: ";
		cin >> buf;
		CHECK_RET(srv_sock.Send(buf, peer_ip, peer_port));
	}
	//关闭套接字
	srv_sock.Close();
	return 0;
}


udp_cli.cpp

#include <iostream>
#include <string>
#include "udpsocket.hpp"
using namespace std;

#define CHECK_RET(q) if((q)==false){return -1;}
 main(int argc, char *argv[])
{
	if (argc != 3)
	{
		cout << "Usage: ./udpcli ip port\n";
		return -1;
	}
	string srv_ip = argv[1];
	uint16_t srv_port = stoi(argv[2]);

	UdpSocket cli_sock;
	//创建套接字
	CHECK_RET(cli_sock.Socket());
	//绑定数据(不推荐)
	while(1)
	{
		//发送数据
		cout << "client say:";
		string buf;
		cin >> buf;
		CHECK_RET(cli_sock.Send(buf, srv_ip, srv_port));
		//接收数据
		buf.clear();
		CHECK_RET(cli_sock.Recv(&buf));
		cout << "server say: " << buf << endl;
	}
	//关闭套接字
	cli_sock.Close();
	return 0;
}

makefile

all:udp_srv udp_cli
udp_srv:udp_srv.cpp
	g++ -std=c++11 $^ -o $@
udp_cli:udp_cli.cpp
	g++ -std=c++11 $^ -o $@

运行结果:
通过ifconfig命令查看网卡信息
在这里插入图片描述
先运行服务端(ip地址必须为网卡信息中的IP地址)
在这里插入图片描述
再运行客户端,并对指定服务端发送数据
在这里插入图片描述
服务端收到数据并回复
在这里插入图片描述
客户端也收到数据
在这里插入图片描述
netstat-查看网络状态信息
-a表示查看所有网络信息
-n表示查看原始ip地址信息
-p表示查看网络状态对应的进程
-t表示查看tcp的信息
-u表示查看udp的信息

推荐阅读

网络 TCP协议(C++代码|通过tcp协议实现客户端与服务端之间的通信)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

网络 UDP协议(C++|代码通过udp协议实现客户端与服务端之间的通信) 的相关文章

  • Ubtuntu+C语言实现网络通信附源代码

    下面这个案例是我用C在ubtuntu上面写的网络编程案例 2 网络编程 xff08 1 xff09 OSI七层模型理想化 应用层 xff1a app xff0c 应用程序 表示层 xff1a 对数据进行加工 会话层 xff1a 建立会话 x
  • Jetson Nano的GPIO口学习

    1 配置GPIO库 https github com NVIDIA jetson gpio 1 安装pip工具 sudo apt get update sudo apt get install python3 pip sudo apt ge
  • 22.11.22 TCP与UDP 客户端与服务器 协议搭建

    ubuntu 64 ubuntu yuyu yu 11 cat Tcp Cli c 客户端 include lt stdio h gt include lt sys types h gt include lt sys socket h gt
  • cmake交叉编译配置

    cmake交叉编译配置 很多时候 xff0c 我们在开发的时候是面对嵌入式平台 xff0c 因此由于资源的限制需要用到相关的交叉编译 即在你host宿主机上要生成target目标机的程序 里面牵扯到相关头文件的切换和编译器的选择以及环境变量
  • OS——gcc、g++、gdb、vim、vs code的基本使用

    文章目录 g 43 43 的使用gdb的使用vim的使用vscode的使用vs code的安装vs code中C 43 43 的编译运行配置 如果想要学习如何在CentOS 7中安装配置gcc g 43 43 gdb zhs和oh my z
  • make和cmake

    编程人员已经使用CMake和Make很长一段时间了 当你加入一家大公司或者开始在一个具有大量代码的工程上开展工作时 xff0c 你需要注意所有的构建 你需要看到处跳转的 CMakeLists txt 文件 你应该会在终端使用 cmake 和
  • ubuntu自带python与anaconda python环境的切换

    ubuntu的python可分为三大类 xff1a 1 ubuntu自带的python环境 一般安装在 usr bin 中python2和python3可以共存 2 anaconda自带的base环境 3 在anaconda中创建的虚拟py
  • 详细介绍如何在ubuntu20.04中安装ROS系统,以及安装过程中出现的常见错误的解决方法,填坑!!!

    本篇文章写于2020 10 xff0c 经过很多小伙伴的验证 xff0c 文章所介绍的步骤是可以正常完成安装的 xff0c 现在是2021 10 xff0c 经过近期的探索 xff0c 我将安装步骤进行了进一步的优化 xff0c 使安装变得
  • VScode进行python开发出现 No module named “XXX“的解决方法

    VScode进行python开发出现 No module named 34 XXX 34 的解决方法 最近从pycharm转向vscode的时候 xff0c 遇到了如下问题 span class token keyword import s
  • CM3寄存器简介

    Cortex M3基础 寄存器组 通用目的寄存器组R0 R7 也被称为低组寄存器 xff0c 所有指令都能访问字长32位 通用目的寄存器组R8 R12 高组寄存器 32位寄存器 复位后的初始值不可预料 堆栈指针R13 CM3中共有两个堆栈指
  • 基于亚博K210开发板的学习之旅(一)

    本文参考亚博智能官方K210开源课程 五月份购买了亚博的K210开发板 xff0c 但由于课程压力就搁置了 xff0c 最近暑假得空又恰逢电赛清单里有这个 芯片 xff0c 就抽空学习一下 xff0c 特写下这些 xff0c 以作记录 按照
  • STM32标准库通用软件模拟IIC

    STM32标准库通用软件模拟IIC 继上次通用可移植的矩阵键盘之后 xff0c 便继续寻找着下一个能够拿来只需改改引脚就可以使用的通用方案 恰好最近在研究PCA9685 xff0c 这是一片能够产生最多十六路PWM信号的芯片 xff0c 通
  • STM32F103控制PCA9685产生16路PWM波控制SG90舵机

    STM32控制PCA9685产生16路PWM波控制SG90舵机 如果你能点开这篇文章 xff0c 说明你已经知道PCA9685是多么强大 xff0c NXP公司原本做这片芯片是为了提供给LED使用 xff0c 在其官方文档里也能看到所有PW
  • 从源代码来看HAL库函数(一) HAL基础函数

    从源代码来看HAL库函数 xff08 一 xff09 HAL基础函数 全局变量 IO uint32 t uwtick 这个变量充当了一个运行时长计数的作用 xff0c 每发生一次SysTick中断就会加一 xff0c 直至溢出 xff0c
  • 使用TCP+串口与板子进行网络通信

    最近做了一个嵌入式的project xff0c 大概要求就是做一个client端 xff0c 一个sensor端 xff0c 两者通过TCP UDP进行通信 xff0c 然后在client端输入不同的命令sensor需做出不同的处理 xff
  • 毕业论文格式(图片题注引用,表格,公式格式)

    本科毕业论文差不多写完了 xff0c 记录一下一些格式 xff0c 以后写作可能会用到 xff0c 就可以翻起来看看 首先 xff0c 如果可以找到一篇格式符合要求的word文档的话 xff0c 最简单的方法就是在这个文档的基础上进行内容的
  • 图像处理——相位恢复(GS,TIE,改进型角谱迭代法)(已更新代码)

    利用GS xff0c TIE xff0c 改进型角谱迭代算法进行相位恢复 角谱传播理论 角谱传播理论可以翻阅傅里叶光学的书 xff0c 就能找到定量分析的计算公式 xff0c 可以分析某个平面的角谱垂直传播到另外一个平面的角谱 xff0c
  • 串口应用:遵循uart协议,发送多个字节的数据(状态机)

    上一节中 xff0c 我们遵循uart协议 xff0c 它发送一次只能发送6 7 8位数据 xff0c 我们不能随意更改位数 xff08 虽然在代码上可行 xff09 xff0c 不然就不遵循uart协议了 xff0c 会造成接收端无法接收
  • 数码管动态显示Verilog实现(参考小梅哥教程)(视觉暂留)

    一个数码管有八个引脚 xff0c 控制八段二极管的亮灭 xff0c 用以显示需要的数字 当有N个数码管时 xff0c 一个一个控制的话需要N x 8 个引脚 xff0c 消耗资源较多 因此可以利用动态显示的方案通过人眼的视觉暂留特性达到静态
  • 彻底理解DDS(信号发生器)的fpga实现(verilog设计代码)

    DDS xff08 Direct Digital Synthesis xff09 是一种把一系列数字信号通过D A转换器转换成模拟信号的数字合成技术 它有查表法和计算法两种基本合成方法 在这里主要记录DDS查表法的fpga实现 查表法 xf

随机推荐

  • HDMI/DVI

    一 基础知识 1 历史 早期在FPGA芯片上实现HDMI控制显示是使用HDMI发送芯片 xff0c eg xff1a ADV7513 sil9022 xff0c CH7301等 用之前VGA控制中输出的RGB信号 行场同步信号和使能信号输入
  • HDMI/DVI____TMDS编码

    一 编码步骤 xff1a 基本方法 xff1a 取第一位数据为初值 xff0c 接下来输入的每一位与前一导出的位 xff08 根据判断条件 xff09 进行异或XOR或者同或XNOR xff08 最小化传输 xff0c 减少0 1翻转 xf
  • HDMI/DVI____串行发送器

    一 功能 xff1a 把10bit数据转化为串行数据在一个时钟周期全部输出 xff08 先输出高位 xff0c 再输出低位 xff09 二 框图 二 思路 对于TMDS编码器 xff0c 在每一个输入时钟周期 xff0c 输入一次数据到TM
  • keil添加新文件.c.h

    文章目录 添加文件到组中1 双击组名称2 点击快捷键 添加头文件路径 h1 点击魔术棒快捷键2 头文件加 添加文件到组中 1 双击组名称 双击组名称 xff0c 打开弹窗 xff0c 然后选择相应的组中的新文件 xff0c 在点击ADD 2
  • QT常用控件(二)——自定义控件封装

    引言 Qt已经提供了很多的基础控件供开发使用 xff0c 而Qt原生的控件有时候并不能满足我们的需求 xff0c 特别是在工业的运用上 xff0c 比如我们需要一个日期时间的选择器 xff0c Qt虽然已经提供了原生的QDateTime控件
  • STM32之串口通信USART模块学习(1)

    一 通信接口 通信的目的 xff1a 将一个设备的数据传送到另一个设备 xff0c 扩展硬件系统通信协议 xff1a 制定通信的规则 xff0c 通信双方按照协议规则进行数据收发 单端信号通信的双方必须要共地 xff0c 因为都是对GND的
  • 2019电赛总结(一)

    2019电赛总结 xff08 一 xff09 文章目录 2019电赛总结 xff08 一 xff09 4 那之前5 电赛初期6 电赛中期7 电赛强化练习8 电赛预热阶段8月初9 那以后 4 那之前 2019电赛总结 序 xff09 5 电赛
  • 统计从键盘输入的一行字符中小写字母,大写字母,数字字符和其它字符的个数。

    统计从键盘输入的一行字符中小写字母 xff0c 大写字母 xff0c 数字字符和其它字符的个数 C语言实现 vs 2019 span class token macro property span class token directive
  • c语言求1~10的阶乘和

    求1 43 2 43 3 43 43 10 的和 span class token macro property span class token directive keyword include span span class toke
  • C和Cpp区别

    1 输入 xff0c 输出不同 xff08 out xff0c put xff09 c语言 xff1a include lt stdio h gt scanf 34 d 34 amp a printf 34 a 61 d n 34 a cp
  • C++实现基于顺序搜索的动态分区分配算法

    目录 1 需求分析 2 代码实现 3 测试用例 4 总结与收获 1 需求分析 动态分区分配又称为可变分区分配 xff0c 他是根据进程的实际需要 xff0c 动态地为之分配内存空间 在实现动态分区分配时 xff0c 将涉及到分区分配中所有的
  • C语言实现TCP编程

    C语言实现TCP编程 1 主机字节序和网络字节序2 套接字的地址结构IP地址转化的方法 3 TCP的网络接口4 TCP服务器端的编程流程5 TCP客户端的编程流程6 运行结果 1 主机字节序和网络字节序 主机字节序 xff1a 不同的芯片
  • QT---用户登录注册案例实现

    用户登录 注册 span class token macro property span class token directive hash span span class token directive keyword include
  • C++中list详解

    list详解 list的介绍list函数说明成员类型构造函数元素访问迭代器容量修改器操作 vector和list区别总结vector和list的使用场景 仿写END xff01 96 在这里插入代码片 96 list的介绍 list是序列容
  • sip response 摘要认证

    详解摘要认证 1 什么是摘要认证 摘要认证与基础认证的工作原理很相似 xff0c 用户先发出一个没有认证证书的请求 xff0c Web服务器回复一个带有WWW Authenticate头的响应 xff0c 指明访问所请求的资源需要证书 但是
  • Prim算法实现最小生成树

    Prim算法实现最小生成树 1 最小生成树是什么2 最小生成树的用途3 Prim算法描述4 Prim算法演示最小生成树过程5 Prim算法实现END 1 最小生成树是什么 对连通图进行遍历 过程中所经过的边和顶点的组合可看做是一棵普通树 通
  • 哈夫曼树,哈夫曼编码及应用——(代码实现)

    哈夫曼树 xff0c 哈夫曼编码及应用 1 哈夫曼树1 1 什么是哈夫曼树 2 如何构造哈夫曼树 xff08 哈夫曼算法 xff09 2 1 举例实现哈夫曼树2 1 1手动实现具体步骤2 1 2代码实现具体步骤 3 哈夫曼编码3 1 什么是
  • 二叉排序树详解及实现

    二叉排序树详解及实现 1 什么是二叉排序树2 二叉排序树的数据结构2 1二叉排序树的节点类型2 2二叉排序树中插入某个元素2 3 二叉排序树中按值查找元素2 4 找排序二叉树中的最小值2 5返回排序二叉树中ptr中序遍历的后续节点2 6 寻
  • 平衡二叉树的一系列操作:删除、插入(在二叉排序树中插入新结点后,如何保持平衡)、调整平衡等等等

    平衡二叉树的插入 xff08 在二叉排序树中插入新结点后 xff0c 如何保持平衡 xff09 1 平衡二叉树的定义2 平衡二叉树的插入 xff08 调整最小不平衡子树A xff09 2 1LL xff08 在A的左孩子的左子树中插入导致不
  • 网络 UDP协议(C++|代码通过udp协议实现客户端与服务端之间的通信)

    这里写目录标题 udp通信编程各端的操作流程 xff1a 服务端操作流程 xff1a 客户端操作流程 xff1a 第2 3步与服务端不同 socket接口介绍udp客户服务端代码实现 推荐阅读 socket套接字编程就是在网络程序中编写代码