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

千锋教育+计算机四级网络-计算机网络学习-04

UDP概述

UDP协议

    面向无连接的用户数据报协议,在传输数据前不需要先建立连接;目地主机的运输层收到UDP报文后,不需要给出任何确认

UDP特点

  1. 相比TCP速度稍快些
  2. 简单的请求/应答应用程序可以使用UDP
  3. 对于海量数据传输不应该使用UDP
  4. 广播和多播应用必须使用UDP

UDP应用

DNS(域名解析)、NFS(网络文件系统)、RTP(流媒体)等

网络编程接口socket

网络通信要解决的是不同主机进程间的通信

  1. 不同主机识别,哪一个主机发,哪一个主机收
  2. 以及多重协议的识别问题,是UDP还是TCP
  3. 首要问题是网络间进程标识问题,因为一个主机存在很多进程,你需要准确的把数据给目的进程(使用端口表示一个进程),哪一个端口发,哪一个端口收

总结就是:IP、端口、协议

20世纪80年代初,加州大学Berkeley分校在BSD(一个UNIX OS版本)系统内实现了TCP/IP协议;其网络程序编程开发接口为socket

随着UNIX以及类UNIX操作系统的广泛应用, socket成为最流行的网络程序开发接口

socket作用

提供不同主机上的进程之间的通信

socket特点

  1. socket也称“套接字”
  2. 是一种文件描述符(特殊的文件描述符),代表了一个通信管道的一个端点(因此socket其实创建的就是一个虚拟的管道-全双工,可以这么简单的理解)
  3. 类似对文件的操作一样,可以使用read、write、close等函数对socket套接字进行网络数据的收取和发送等操作
  4. 得到socket套接字(描述符)的方法调用socket()

UDP编程C/S架构 

这个架构决定我们写代码的流程

UDP编程-创建套接字 

创建socket套接字

int socket(int family,int type,int protocol);

功能:

创建一个用于网络通信的socket套接字(描述符)

参数:

family:协议族(AF_INET、AF_INET6、PF_PACKET等)

type:套接字类(SOCK_STREAM、SOCK_DGRAM、SOCK_RAW等)

protocol:协议类别(0、IPPROTO_TCP、IPPROTO_UDP等

protocol写0(自动寻找),让它以type为主,确定协议SOCK_STREAM--TCP

SOCK_DGRAM--UDP

返回值:

套接字

特点:

创建套接字时,系统不会分配端口

创建的套接字默认属性是主动的,即主动发起服务的请求;当作为服务器时,往往需要修改为被动的

头文件:

#include <sys/socket.h>

创建UDP套接字demo
int sockfd = 0;
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd < 0)
{perror("socket");close(sockfd);exit(-1);
}注意:
AF_INET:IPv4协议
SOCK_DGRAM:数据报套接字
0:选择所给定的family和type组合的系统默认值

 IPv4套接字地址结构

struct sockaddr_in{sa_family_t sin_family;//2字节   -- 协议族in_port_t sin_port;//2字节          --端口struct in_addr sin_addr;//4字节  --发送方地址或接收方地址char sin_zero[8]//8字节 ---为0,起补位作用,因为要求结构体16个字节,因此定义了8字节的补位};struct in_addr  //该结构体只有一个成员,属于历史遗留问题,之前是有其他成员的{in_addr_t s_addr;//4字节}

在赋值结构体的时候,由于 sin_zero的值需要赋值0,那么我们可以一开始就把结构体清零,然后只赋值协议、端口、IP地址即可

当主机是IPV4或者IPV6发送数据的时候,由于IP地址不同,导致函数使用的不同,因此为了让函数的实用性更强,那么我就把IPV4和IPV6当成整体,因此就把结构体转换为相同的类型即可

为了使不同格式地址能被传入套接字函数,地址须要强制转换成通用套接字地址结构

头文件:#include <netinet/in.h>

struct sockaddr

{

      sa_family_t sa_family;    // 2字节

      char sa_data[14]     //14字节

};

 为了类型转换,就像一个函数能处理char*、int*,那么这个函数就需要成为void*,数据转换,其实我们自己设计一个通用函数的时候也需要注意,如果类型不同,那么我们需要存在一个通用的类型,像void*,能被任何一个数据类型赋值,因此同理,这里也产生了一个类似于void*的类型(为了数据转换,以便处理更多的数据类型)

两种地址结构使用场合

在定义源地址和目的地址结构的时候,选用struct sockaddr_in;

例:

struct  sockaddr_in  my_addr;

这个特点记住就好,等写代码的时候注意一下 

当调用编程接口函数,且该函数需要传入地址结构时需要用struct sockaddr进行强制转换

例:

bind(sockfd,(struct sockaddr*)&my_addr,sizeof(my_addr));

发送数据—sendto函数

ssize_t sendto(int sockfd,const void *buf,

                 size_t nbytes,int flags,

                 const struct sockaddr *to,       

                 socklen_t addrlen);

功能:

向to结构体指针中指定的ip,发送UDP数据

参数:

sockfd:套接字

buf:       发送数据缓冲区

nbytes: 发送数据缓冲区的大小

flags:一般为0

to: 指向目的主机地址结构体的指针

addrlen:to所指向内容的长度

注意:

通过to和addrlen确定目的地址

可以发送0长度的UDP数据包

返回值:

成功:实际发送数据的字符数

失败: -1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>int main(int argc,char *argv[])
{//定义好端口和IP地址,并且IP地址是字符串形式,其实这里可以不用初始化unsigned short server_port= 808;  //可以赋值0char *server_ip = "192.168.52.1"; //可以赋值NULL//传递参数需要符合规定,否则错误,并且退出if(argc < 3 ){printf("error\n");exit(1);}//把从终端传进去的IP地址和端口号进行赋值server_ip = argv[1];//通过main函数传参,传入端口号server_port= atoi(argv[2]);    //由于终端输入的是字符串,而端口号是整型,因此使用atoi函数把数字字符串转换为数字/*创建UDP套接字*/int sockfd;sockfd = socket(AF_INET,SOCK_DGRAM,0);  //IPV4和UDPif(sockfd < 0){perror("socket");exit(-1);}/*填充目的server socket地址*/struct sockaddr_in dest_addr;bzero(&dest_addr,sizeof(dest_addr));  //先清零dest_addr.sin_family = AF_INET;//目的套接字地址的协议家族赋值dest_addr.sin_port = htons(server_port);//目的套接字地址的端口号赋值,并且转成网络字节序inet_pton(AF_INET,server_ip,&dest_addr.sin_addr);//目的套接字地址的ip地址赋值printf("send data to UDP server %s:%d!\n",server_ip,port);/*发送数据到目的server*/while(1){char send_buf[512];fgets(send_buf,sizeof(send_buf),stdin);  //使用fgets从终端中输入字符串send_buf[strlen(send_buf)-1] = '\0';//字符串最后一个'\n'变成'\0' //这个很容易忘记,在末尾添加结束字符sendto(sockfd,send_buf,strlen(send_buf),0,(struct sockaddr *)&dest_addr,sizeof(dest_addr));}close(sockfd);  //记得使用完成后,关闭套接字return 0;
}

绑定 bind函数

UDP网络程序想要收取数据需什么条件?

确定的ip地址

确定的port

怎样完成上面的条件呢?

接收端 使用bind函数,来完成地址结构与socket套接字的绑定,这样ip、port就固定了

发送端 在sendto函数中指定接收端的ip、port,就可以发送数据了

因此bind既可以使用在发送端,也可以使用在接收端(客户端和服务端都可以使用bind,都是一般服务端使用bind)

int bind(int sockfd,

        const struct sockaddr *myaddr,socklen_t addrlen);

功能:

将本地协议地址与sockfd绑定

参数:

sockfd: socket套接字

myaddr: 指向特定协议的地址结构指针

addrlen:该地址结构的长度

返回值:

成功:返回0

失败:其他

    struct sockaddr_in Mysource_addr;bzero(&Mysource_addr,sizeof(Mysource_addr));Mysource_addr.sin_family = AF_INET;//目的套接字地址的协议家族赋值Mysource_addr.sin_port = htons(8000);//目的套接字地址的端口号赋值Mysource_addr.sin_addr.s_addr = htonl(INADDR_ANY);INADDR_ANY是通配地址,值为0

接收数据—recvfrom 函数

ssize_t recvfrom(int sockfd, void *buf,

size_t nbytes,int flags,

               struct sockaddr *from,

               socklen_t *addrlen);

功能:

接收UDP数据,并将源地址信息保存在from指向的结构中

并且recvfrom默认是带阻塞的,他需要等信息

参数:

sockfd: 套接字

buf:接收数据缓冲区

nbytes:接收数据缓冲区的大小

flags: 套接字标志(常为0)

from:  源地址结构体指针,用来保存数据的来源

addrlen: from所指内容的长度

注意:

通过from和addrlen参数存放数据来源信息

from和addrlen可以为NULL, 表示不保存数据来源

返回值:

成功:接收到的字符数

失败: -1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>int main(int argc,char *argv[])
{struct sockaddr_in Mysource_addr;bzero(&Mysource_addr,sizeof(Mysource_addr));Mysource_addr.sin_family = AF_INET;//目的套接字地址的协议家族赋值Mysource_addr.sin_port = htons(8000);//目的套接字地址的端口号赋值Mysource_addr.sin_addr.s_addr = htonl(INADDR_ANY);/*创建UDP套接字*/int sockfd;sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd < 0){perror("socket");exit(-1);}if(bind(sockfd,(struct sockaddr *)&Mysource_addr,sizeof(Mysource_addr)) != 0 ){perror("bind is error");exit(1);}char recvBuff[512];while(1){while( recvfrom(sockfd,recvBuff,512,0,NULL,NULL) == 0 );printf("%s\n",recvBuff);}
}

 

 模拟QQ情况

QQ既可以发信息也可以收消息,说明存在多进程或者多线程情况,一个进程或者线程收消息,而另外一个发消息

 独立完成改代码

http://www.lryc.cn/news/11417.html

相关文章:

  • 蓝桥杯算法训练合集十四 1.P08052.P07053.同余方程4.P08015.ascii应用
  • 判断字符串中的字符的类型isdecimal();isalpha();isdigit();isalnum()
  • VSCode远程调试Linux代码,python解释器配置
  • 03:入门篇 - CTK Plugin Framework 基本原理
  • 面试攻略,Java 基础面试 100 问(九)
  • JavaScript 代码不嵌套主义
  • 使用默认参数的4大要点
  • Linux文件系统中的硬链接及常见面试题
  • opencv-StereoBM算法
  • 图像分类竞赛进阶技能:OpenAI-CLIP使用范例
  • Metasploit框架基础(一)
  • pytorch零基础实现语义分割项目(二)——标签转换与数据加载
  • python(8.5)--列表习题
  • rt-thread pwm 多通道
  • C语言练习 | 初学者经典练习汇总
  • 华为OD机试 - 自动曝光(Python) | 机试题算法思路 【2023】
  • 「6」线性代数(期末复习)
  • 1.1 硬件与micropython固件烧录及自编译固件
  • 【MySQL进阶】视图 存储过程 触发器
  • [Linux篇] Linux常见命令和权限
  • 29岁从事功能测试被辞,面试2个月都找不到工作吗?
  • 【C#个人错题笔记1】
  • 基于lambda的mongodb查询插件
  • 基于微信小程序的微信社团小程序
  • GEE学习笔记 七十三:【GEE之Python版教程七】静态展示影像和动态展示影像
  • PGLBox全面解决图训练速度、成本、稳定性、复杂算法四大问题!
  • 超详细的 pytest 教程(一)使用入门篇
  • 二叉树理论基础知识点
  • 【算法基础】堆⭐⭐⭐
  • 时序预测 | MATLAB实现CNN-SVM卷积支持向量机时间序列预测