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

Linux编程——网络编程(UDP)

 网络编程——UDP(用户数据报)

udp特征:

1.数据传输不可靠(丢包率);

2.低延迟;

3.网络开销小;

4.无连接        //无连接 意味着在通信双方开始传输数据之前,不需要预先建立一条专用的通信通道(即连接)

数据报特征:

1.数据与数据之间有边界

2.发送的次数和接收的次数需要对应(保持一致)

3.如果发送太快,就会丢包        //原因:硬盘读的快写的慢

操作流程

头文件:#include <sys/types.h>       
#include <sys/socket.h>

              #include <netinet/ip.h>        /* bind() */

              #include <arpa/inet.h>        /* inet_addr() */

服务端:

socket() ==》bind() ==》recvfrom() ==》sendto()

!!!伪代码逻辑

//1.创建一个UDP套接字
socket(AF_INET, SOCK_DGRAM, 0);// 2. 构建服务器地址 (Server Address Setup)
//    (服务器的IP和Port)
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVER_PORT); // 服务器端口,如8080
inet_addr("ser.ip"); // 服务器IP,如"192.168.1.100"//3.将该套接字绑定到服务器的IP和某个端口(如 8080)
bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); //4.阻塞等待,直到有客户端发来数据。cli_addr 会被填充为客户端的地址信息
recvfrom(sockfd, buf, buf_size, 0, (struct sockaddr*)&cli_addr, &cli_addr_len);//5.处理完请求后,使用 recvfrom 得到的客户端地址 cli_addr,将回复数据发送回去
sendto(sockfd, response_buf, response_len, 0, (struct sockaddr*)&cli_addr, cli_addr_len);

1、socket() 创建通信节点(打开网络设备)

原型:        int socket(int domain, int type, int protocol);

功能:        创建一个通信端点,并返回一个文件描述符(socket descriptor)

参数:        int domain: 指定协议族(Protocol Family)

                   int type: 指定通信语义(套接字类型)

                   int protocol: 通常设置为 0,表示根据 domain 和 type 自动选择默认协议。例如,(AF_INET, SOCK_STREAM) 会自动选择 TCP。

返回值:      成功 返回非负整数文件描述符

                     失败 返回-1,并设置errno

指定协议族:

  • AF_INET: IPv4 协议

  • AF_INET6: IPv6 协议

  • AF_UNIX 或 AF_LOCAL: 本地进程间通信

指定通信定义:

  • SOCK_STREAM: 提供面向连接的、可靠的字节流(TCP)

  • SOCK_DGRAM: 提供无连接的、不可靠的数据报服务(UDP)


2、bind() 给套接字,绑定地址和端口号( ip+port )

原型:        int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

功能:        将一个本地协议地址(IP地址 + 端口号)分配给由 socket() 创建的套接字。服务器程序通常需要调用此函数来将其套接字与一个众所周知的端口号绑定,以便客户端能够连接到服务器

参数:         int sockfdsocket() 函数返回的套接字描述符

                     const struct sockaddr *addr: 指向一个地址结构体的指针,该结构体包含了要绑定的IP和端口号

                      socklen_t addrlen: 第二个参数 addr 所指向结构体的长度(以字节为单位),通常使用 sizeof(struct sockaddr_in)

返回值:      成功 返回0;

                    失败 返回-1,并设置errno

参数二:指向地址结构体的指针:

  • 对于IPv4 (AF_INET),使用 struct sockaddr_in

  • 对于IPv6 (AF_INET6),使用 struct sockaddr_in6

bind伪代码示例
struct sockaddr_in ser,cli;ser.sin_family = AF_INET;//host to net shortser.sin_port = htons(50000);ser.sin_addr.s_addr = inet_addr("192.168.1.200");  int ret = bind(sockfd,(SA)&ser,sizeof(ser));if(-1 == ret){perror("bind fail");return 1;}

3、recvfrom() 从客户端接收数据(阻塞等待客户端请求)

原型:        ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

功能:        从一个(特别是无连接的,如UDP)套接字接收数据,并捕获发送方的源地址。它是一个阻塞性调用,除非有数据到达,否则调用该函数的进程会被挂起(休眠)

参数:       

  • int sockfd: 套接字描述符。

  • void *buf: 指向接收缓冲区的指针,用于存放接收到的数据。

  • size_t len: 接收缓冲区 buf 的最大长度。

  • int flags: 控制接收行为的标志位,通常设置为 0(表示无特殊行为)。

  • struct sockaddr *src_addr: (输出参数)指向一个 sockaddr 结构体的指针,用于存放发送方的地址信息(IP和端口)。如果不需要知道发送方是谁,可以设置为 NULL

  • socklen_t *addrlen: (输入输出参数)

    • 输入: 指向一个整数,表示 src_addr 指向的缓冲区的大小。

    • 输出: 函数返回时,该整数会被设置为实际存放的地址信息的长度。

返回值:     成功 返回接收到的字节数

                   失败 返回-1,并设置errno

recvfrom伪代码示例

char buf[512] = {0};
recvfrom(sockfd,buf,sizeof(buf),0, (SA)&cli,&len);    //接收客户端数据
printf("recv client:%s\n",buf);

4、sendto() 向套接字发送数据

原型:        ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

功能:        向一个指定的目标地址发送数据。通常用于无连接的套接字(如UDP),因为每次发送都需要指定目标地址。

参数:        

  • int sockfd: 套接字描述符。

  • const void *buf: 指向发送缓冲区的指针,包含要发送的数据。

  • size_t len: 要发送的数据的字节数。

  • int flags: 控制发送行为的标志位,通常设置为 0

  • const struct sockaddr *dest_addr: 指向一个 sockaddr 结构体的指针,包含了目标接收方的地址信息(IP和端口)。

  • socklen_t addrlen: 第五个参数 dest_addr 所指向结构体的长度。

返回值:     成功 返回实际发送出去的字节数

                   失败 返回-1,并设置errno

sendto伪代码示例

sprintf(buf, "%s %s",buf,ctime(&tm));    //数据处理
sendto(sockfd,buf,strlen(buf),0,(SA)&cli,len);    //发送给客户端处理结束的数据

总代码示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>typedef struct sockaddr * SA;
int	main(int argc, char **argv)
{int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(-1 == sockfd){perror("socket fail");return 1;}//man 7 ipstruct sockaddr_in ser,cli;ser.sin_family = AF_INET;//host to net shortser.sin_port = htons(50000);ser.sin_addr.s_addr = inet_addr("192.168.1.200");int ret = bind(sockfd,(SA)&ser,sizeof(ser));if(-1 == ret){perror("bind fail");return 1;}time_t tm;socklen_t len = sizeof(cli);while (1){char buf[512] = {0};time(&tm);recvfrom(sockfd,buf,sizeof(buf),0, (SA)&cli,&len);printf("recv client:%s\n",buf);sprintf(buf, "%s %s",buf,ctime(&tm));sendto(sockfd,buf,strlen(buf),0,(SA)&cli,len);    }return 0;
}

客户端:

伪代码逻辑

socket() ==》sendto() ==》recvfrom()

//1.创建UDP套接字(Socket Creation)
//  创建一个用于UDP通信的套接字,得到文件描述符sockfd
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);// 2. 构建服务器地址 (Server Address Setup)
//    告诉系统数据要发往哪里(服务器的IP和Port)
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVER_PORT); // 服务器端口,如8080
inet_addr("ser.ip"); // 服务器IP,如"192.168.1.100"// 3. 发送数据 (Sending Data)
//    使用sendto将数据发送到上面指定的serv_addr
sendto(sockfd, request_data, request_len, 0,(struct sockaddr*)&serv_addr, sizeof(serv_addr));// 4. 接收回复 (Receiving Response)
//    使用同一个sockfd接收数据。可以捕获回复方的地址,这里设为NULL表示不关心
recvfrom(sockfd, response_buf, sizeof(response_buf), 0,NULL, NULL); // 不关心回复来自谁,所以后两个参数为NULL

【函数使用方法见上述服务器端!!!】

1、socket() 创建通信节点(打开网络设备)

2、sendto() 向指定目标发送数据

3、recvfrom() 从套接字接收处理后的数据


总示例代码

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <time.h>
#include <unistd.h>typedef struct sockaddr *SA;
int main(int argc, char **argv)
{int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("socket fail");return 1;}//struct sockaddr_in ser;ser.sin_family = AF_INET;// host to net shortser.sin_port = htons(50000);ser.sin_addr.s_addr = inet_addr("192.168.1.200");// ser.sin_addr.s_addr = inet_addr("192.168.1.23");int i = 10;while (i--){char buf[512] = {"hello"};sendto(sockfd, buf, strlen(buf), 0, (SA)&ser, sizeof(ser));bzero(buf, sizeof(buf));recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);printf("from ser:%s\n", buf);sleep(1);}return 0;
}

服务器/客户端模型

c/s  b/s

c/s(客户端/服务器模型)b/s(浏览器/服务器模型)
客户端专用/通用客户端专用客户端通用
使用协议http协议自定义协议、标准协议
资源角度资源大部分都在client,server发送必要的交互资源给clientserver发送给client
功能功能可以相对复杂受到http协议的限制,功能不复杂

p2p模型(peer)

在P2P网络中,每个节点(Peer)既是客户端(向其他节点请求服务),也是服务器(为其他节点提供服务)。没有永恒的中心服务器,所有节点地位对等。

一个P2P节点的核心任务是:既要能主动发起请求,也要能被动接收并处理请求。因此,每个节点的流程都融合了客户端和服务器的行为。

http://www.xdnf.cn/news/20129.html

相关文章:

  • 计算机网络模型入门指南:分层原理与各层作用
  • 对接旅游行业安全需求:旅游安全急救实训室的功能构建与育人目标
  • 网络安全初级-渗透测试
  • 用AI做TikTok影视解说,全流程全自动成片,不懂外语也能做全球矩阵!
  • 办公任务分发项目 laravel vue mysql 第一章:核心功能构建 API
  • 系统越拆越乱?你可能误解了微服务的本质!
  • 【Linux系统】线程同步
  • 正则表达式与转义符的区别。注意输入的东西经过了一次转义,一次正则表达式。\\转义是单斜杠\\在正则表达式也是单斜杠所以\\\\经过两道门才是字符单斜杠
  • MongoDB Change Streams:实时监听数据变化的实战场景
  • clickhouse迁移工具clickhouse-copier
  • Python EXCEL 小技巧:最快重新排列dataframe函数
  • 工业机器人标杆的数字化突围,珞石机器人如何以CRM实现业务重塑
  • 技术视界 | 跨域机器人通信与智能系统:打破壁垒的开源探索
  • 【Linux】环境变量与程序地址空间详解
  • ansible-角色
  • MySQL知识
  • 【C++】17. AVL树实现
  • 探索未来智能自动化,一个强大的自动化引擎
  • 苹果Vision Air蓝图或定档2027,三星/微美全息加速XR+AI核心生态布局卡位
  • 第二阶段WinForm-13:图表控件,N层架构,Dapper
  • 【数学建模学习笔记】机器学习分类:决策树分类
  • 团队协作与接口联调 Charles抓包工具在多人开发中的高效应用
  • WEBSTORM前端 —— 第4章:JavaScript —— 第7节:函数
  • 安徽造价信息网期刊及工程材料信息价
  • 去中心化投票系统开发教程 第一章:区块链基础知识
  • 新一代Agent(智能体),路在低代码?
  • 【Dify】使用工具节点实现 API 接口调用与 JSON 处理
  • 深入 Spring MVC 底层:从 DispatcherServlet 到自定义组件的全链路解析
  • 隔空盗刷、AI钓鱼、代理劫持…金融黑产竟进化至此?
  • Rewind-你人生的搜索引擎