【网络编程】
创始人
2024-06-02 13:20:47
0

UDP网络编程

流程

在这里插入图片描述

服务器流程:

创建用户数据报套接字
填充服务器网络信息结构体
将套接字和服务器网络信息结构体绑定
收发数据 recvfrom sendto
关闭套接字

客户端流程:

创建用户数据报套接字
填充服务器网络信息结构体
收发数据 recvfrom sendto
关闭套接字

函数说明

recvfrom 函数

功能:在套接字上接收一条消息
头文件:#include #include 
函数原型:ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
参数:前4个参数的功能 和recv函数的一样后两个参数和 accept函数的后两个参数一样是用来保存客户端的网络信息结构体的 如果不关心 可以传 NULL
返回值:成功 实际接收的字节数失败 -1 重置错误码注意:recvfrom 不会返回 0

sendto 函数

功能:向套接字上发送一条消息
头文件:#include #include 
函数原型:ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
参数:前4个参数的功能 和send函数的一样后2个参数和 connect函数的后两个参数一样是用来指定接收方的信息的
返回值:成功 实际发送的字节数失败 -1 重置错误码

代码实现

服务器代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #define ERRLOG(msg) do{\printf("%s:%s:%d\n", __FILE__, __func__, __LINE__);\perror(msg);\exit(-1);\}while(0)int main(int argc, const char *argv[])
{//入参合理性检查if(3 != argc){printf("Usage : %s  \n", argv[0]);exit(-1);}//1.创建用户数据报套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(-1 == sockfd){ERRLOG("socket error");}//2.填充服务器网络信息结构体struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));serveraddr.sin_addr.s_addr = inet_addr(argv[1]);socklen_t serveraddrlen = sizeof(serveraddr);//3.将套接字与服务器网络信息结构体绑定if(-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddrlen)){ERRLOG("bind error");}//定义一个结构体 来保存客户端的信息//UDP服务器如果不保存客户端的信息 那么就没法给客户端回信了struct sockaddr_in clientaddr;memset(&clientaddr, 0, sizeof(clientaddr));socklen_t clientaddr_len = sizeof(clientaddr);char buff[128] = {0};int nbytes = 0;//6.收发数据while(1){memset(buff, 0, 128);if(-1 == (nbytes = recvfrom(sockfd, buff, 128, 0, (struct sockaddr *)&clientaddr, &clientaddr_len))){ERRLOG("recvfrom error");}printf("客户端[%s:%d]发来数据[%s]\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port), buff);//组装应答消息strcat(buff, "--hqyj");//给客户端发送应答消息//如果没有保存客户端的网络信息结构体,sendto的后两个参数就没法填写//相当于没法回信了if(-1 == sendto(sockfd, buff, 128, 0, (struct sockaddr *)&clientaddr, clientaddr_len)){ERRLOG("send error");}}//7.关闭套接字close(sockfd);return 0;
}

客户端代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #define ERRLOG(msg) do{\printf("%s:%s:%d\n", __FILE__, __func__, __LINE__);\perror(msg);\exit(-1);\}while(0)int main(int argc, const char *argv[])
{//入参合理性检查if(3 != argc){printf("Usage : %s  \n", argv[0]);exit(-1);}//1.创建用户数据报套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(-1 == sockfd){ERRLOG("socket error");}//2.填充服务器网络信息结构体struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));serveraddr.sin_addr.s_addr = inet_addr(argv[1]);socklen_t serveraddrlen = sizeof(serveraddr);//3.收发数据char buff[128] = {0};while(1){memset(buff, 0, 128);//在终端获取数据fgets(buff, 128, stdin);buff[strlen(buff)-1] = '\0'; //清理结尾的 \n//把数据发给服务器if(-1 == sendto(sockfd, buff, 128, 0, (struct sockaddr *)&serveraddr, serveraddrlen)){ERRLOG("send error");}if(!strncmp(buff, "quit", 4)){break;}//接收应答消息//不用再重新保存服务器的网络信息结构体了 因为我们的serveraddr 没有改动过if(-1 == recvfrom(sockfd, buff, 128, 0, NULL, NULL)){ERRLOG("recv error");}//打印应答消息printf("应答:[%s]\n", buff);}//5.关闭套接字close(sockfd);return 0;
}

wireshark抓包分析

软件说明

Wireshark 是网络包分析工具。
网络包分析工具的主要作用是尝试捕获网络包,并尝试显示包的尽可能详细的情况。

网络管理员用来解决网络问题
网络安全工程师用来检测安全隐患
开发人员用来测试协议执行情况
初学者用来学习网络协议

过滤器常用的过滤语句

tcp.port==xxx
ip.src==xxxx
ip.dst==xxxxx
ip.addr==xxxx
也可以通过 and  或者 or 连接多个过滤语句 

链路层

以太网头(MAC头)
在这里插入图片描述
目的mac地址:接收方的mac地址 --我的windows的mac地址
源mac地址:发送方的mac地址 --我的Ubuntu的mac地址
类型:指定了后面使用的协议的类型
0800 IP协议
0806 ARP协议
8035 RARP协议

查看mac地址的方法:
windows ipconfig/all 物理地址对应的就是mac地址
Ubuntu ifconfig ether后面对应的就是mac地址
在这里插入图片描述
交换机是工作在链路层的设备,他通过MAC地址来决定数据如何转发。

网络层

IP头
在这里插入图片描述
版本:IP协议协议的版本 4 表示IPV4
首部长度:(由于首部长度只有4个bit为能表示最大数 1111 是 15,但是ip头是20个字节
所以此处使用的4倍的单位, 此处是5 就表示 20了)

总长度:IP头+TCP头+用户数的长度
对于我们的例子来说: 20+20+128 == 168

生存时间TTL:
表明是数据包在网络中的寿命,即为“跳数限制”,由发出数据包的源点设置这个字段。路由器在转发数据之前就把TTL值减一,当TTL值减为零时,就丢弃这个数据包。通常设置为 32、64、128

协议类型:表明后面使用的协议
ICMP(1),IGMP(2),TCP(6),UDP(17)

源IP地址:发送方的IP地址 Ubuntu的IP地址
目的IP地址:接收方的IP地址 windows的IP地址
在这里插入图片描述
路由是工作网络层的设备,是根据IP地址决定数据如何转发的。

传输层

TCP头
在这里插入图片描述
源端口号:Ubuntu的端口号 操作系统指定的 随机值
目的端口号:windows服务器的端口号 6789
序列号:seq
确认号:ack
头部长度:TCP头的长度 也是 4倍的单位
在这里插入图片描述

应用层

在这里插入图片描述

TCP的三次握手和四次挥手

三次握手

三次握手是由客户端主动发起的,发生在建立连接的过程中。
发生在服务器的accept函数(listen函数)和客户端的connect函数之间。
三次握手就为了保证通信的双方都知道对方收发数据的能力没问题。
同时,也是同步序列号的过程。
在这里插入图片描述

四次挥手

四次挥手是由主动关闭方发起的(一般都是客户端发起)
四次挥手发生在连接断开的过程中
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

TCP/UDP网络编程总结

TCP网络编程

在这里插入图片描述
1.客户端一般不需要绑定自己的网络信息结构体
因为操作系统会自动给客户端的ip地址和端口号赋值,也方便用户操作。
如果想要手动指定,也可以,需要调用bind()函数即可。
2.服务器端accept函数的后两个参数即使设置成NULL,服务器也可以给客户端回复消息,
原因是,服务器侧不是依赖于手动给定的IP地址和端口号来联系客户端的,而是给每个客户端都分配一个独立的文件描述符 acceptfd,来专门用于和该客户端通信,也就是说TCP的服务器,acceptfd和客户端是一一对应的关系。
3.TCP网络编程中可以使用 read/write send/recv sendto/recvfrom 来收发数据
4.服务器端的accept函数本质就是一个阻塞的读函数,也就是一个接收函数,
客户端的connect函数本质就是个写函数,也就是一个发送函数,
收发的数据本质就是客户端的网络信息结构体

5.TCP的服务器默认的是一个循环服务器,没法同时处理多个客户端的请求,
原因是tcp的服务器有两个阻塞函数 accept 和 recv,两个函数之间相互会有影响。
可以使用 多进程 多线程 IO多路复用 来解决。

UDP网络编程

在这里插入图片描述
1.UDP是无连接的,但是也可以双向的收发数据
因为UDP使用的是 sendto/recvfrom 来收发数据,sendto时可以指定接收方的信息。
sendto函数相当于 send函数和 connect函数的二合一
recvfrom函数相当于 recv函数和 accept函数的二合一
2.UDP中客户端也可以使用 connect 函数先将自己的网络信息结构体发给服务器,
然后就可以使用 send 和 recv 收发数据了

3.如果UDP服务器端的recvfrom函数的后两个参数设置成 NULL了,那么接收数据是没有问题的,但是就没法给发送方回信了,因为sendto的后两个参数没法填写。
4.UDP服务器默认的就是一个并发服务器,因为只有一个阻塞的函数 recvfrom。

TFTP协议

TFTP概述

TFTP:简单文件传送协议
最初用于引导无盘系统,被设计用来传输小文件
特点:
基于UDP协议实现
不进行用户有效性认证

数据传输模式:
octet:二进制模式
netascii:文本模式
mail:已经不再支持

TFTP通信过程

在这里插入图片描述

TFTP通信过程总结(无选项)

1、服务器在69号端口等待客户端的请求
2、服务器若批准此请求,则使用临时端口与客户端进行通信
3、每个数据包的编号都有变化(从1开始)
4、每个数据包都要得到ACK的确认如果出现超时,则需要重新发送最后的包(数据或ACK)
5、数据的长度以512Byte传输
6、小于512Byte的数据意味着传输结束

TFTP协议分析

在这里插入图片描述
错误码:

0 未定义,参见错误信息
1 File not found.
2 Access violation.
3 Disk full or allocation exceeded.
4 illegal TFTP operation.
5 Unknown transfer ID.
6 File already exists.
7 No such user.
8 Unsupported option(s) requested.

例:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #define ERRLOG(msg) do{\printf("%s:%s:%d\n", __FILE__, __func__, __LINE__);\perror(msg);\exit(-1);\}while(0)int main(int argc, const char *argv[])
{//入参合理性检查if(3 != argc){printf("Usage : %s  \n", argv[0]);exit(-1);}//1.创建用户数据报套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(-1 == sockfd){ERRLOG("socket error");}//2.填充服务器网络信息结构体struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET;//端口号不能乱填 应该填 69serveraddr.sin_port = htons(atoi(argv[2]));serveraddr.sin_addr.s_addr = inet_addr(argv[1]);socklen_t serveraddrlen = sizeof(serveraddr);char filename[32] = {0};printf("请输入要下载的文件名:");fgets(filename, 32, stdin);filename[strlen(filename)-1] = '\0';char buff[600] = {0};//组装请求的数据包//组装方式1:一个字节一个字节的组装//buff[0] = 0;//buff[1] = 1;//组装方式2:可以通过修改指针的操作空间来组装//*(short *)buff = htons(1);//组装方式3:可以使用 sprintf 组装int nbytes = sprintf(buff, "%c%c%s%c%s%c", 0, 1, filename, 0, "octet", 0);//给服务器发送下载文件的请求if(-1 == sendto(sockfd, buff, nbytes, 0, (struct sockaddr *)&serveraddr, serveraddrlen)){ERRLOG("sendto error");}//接收服务器的发来的消息//自己思考此处 后两个参数能不能传 NULLif(-1 == (nbytes = recvfrom(sockfd, buff, sizeof(buff), 0, NULL, NULL))){ERRLOG("recvfrom error");}//解析接到的数据short code = 0;//操作码short num = 0;//块编号或者差错码char txt[512] = {0};//正文内容或者差错信息//解析操作码code = ntohs(*(short *)buff);//解析块编号或者差错码num = ntohs(*(short *)(buff+2));//解析正文内容或者差错信息strncpy(txt, buff+4, nbytes-4);printf("code = [%d], num = [%d], txt = [%s]\n", code, num, txt);//5.关闭套接字close(sockfd);return 0;
}

练习:编写客户端程序,实现在TFTP服务器上下载文件的功能。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #define ERRLOG(msg) do{\printf("%s:%s:%d\n", __FILE__, __func__, __LINE__);\perror(msg);\exit(-1);\}while(0)int main(int argc, const char *argv[])
{//入参合理性检查if(3 != argc){printf("Usage : %s  \n", argv[0]);exit(-1);}//1.创建用户数据报套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(-1 == sockfd){ERRLOG("socket error");}int fd = 0;int block_num = 0; //用来记录已经收到的数据包的编号//2.填充服务器网络信息结构体struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr(argv[1]);socklen_t serveraddrlen = sizeof(serveraddr);
RE_NAME://端口号不能乱填 应该填 69  如果出错了 重发请求时 需要重新赋值端口号 69//因为服务器只在 69 端口上等待请求serveraddr.sin_port = htons(atoi(argv[2]));char filename[32] = {0};short code = 0;//操作码short num = 0;//块编号或者差错码char txt[512] = {0};//正文内容或者差错信息printf("请输入要下载的文件名:");fgets(filename, 32, stdin);filename[strlen(filename)-1] = '\0';char buff[600] = {0};//组装请求的数据包//组装方式1:一个字节一个字节的组装//buff[0] = 0;//buff[1] = 1;//组装方式2:可以通过修改指针的操作空间来组装//*(short *)buff = htons(1);//组装方式3:可以使用 sprintf 组装int nbytes = sprintf(buff, "%c%c%s%c%s%c", 0, 1, filename, 0, "octet", 0);//给服务器发送下载文件的请求if(-1 == sendto(sockfd, buff, nbytes, 0, (struct sockaddr *)&serveraddr, serveraddrlen)){ERRLOG("sendto error");}while(1){//接收服务器的发来的消息//此处recvfrom后两个参数不能传 NULL,因为临时端口我们需要保存 回复ack需要回复到临时端口上if(-1 == (nbytes = recvfrom(sockfd, buff, sizeof(buff), 0, (struct sockaddr *)&serveraddr, &serveraddrlen))){ERRLOG("recvfrom error");}//解析接到的数据//解析操作码code = ntohs(*(short *)buff);//解析块编号或者差错码num = ntohs(*(short *)(buff+2));//解析正文内容或者差错信息strncpy(txt, buff+4, nbytes-4);if(5 == code){//出错了printf("errno = [%d], errstr = [%s]\n", num, txt);if(1 == num){//文件不存在goto RE_NAME;}else{exit(-1);}}else if(3 == code && num == block_num+1){block_num++;//自己对已经收到的包的编号做校验 防止收到重复的包if(1 == num){//第一个数据包发来时  打开文件if(-1 == (fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664))){ERRLOG("open error");}}//把文件内容写入文件if(-1 == write(fd, txt, nbytes-4)){ERRLOG("write error");}//组装ACK*(short *)buff = htons(4);*(short *)(buff+2) = htons(num);//发送ACKif(-1 == sendto(sockfd, buff, 4, 0, (struct sockaddr *)&serveraddr, serveraddrlen)){ERRLOG("sendto error");}//结束的表示if(nbytes-4 < 512){break;}}}printf("文件 [%s] 下载完成..\n", filename);close(fd);//5.关闭套接字close(sockfd);return 0;
}

相关内容

热门资讯

小学课文叶公好龙的意思是什么 小学课文叶公好龙的意思是什么叶公好龙是一句成语,讲述了叶公爱龙成癖,被天上的真龙知道后,便从天上下降...
完美世界前传图一图二图三的问题... 完美世界前传图一图二图三的问题?我是电二龙现的,101魔尊,图我都开完了,图一可进 千年前天泪之城图...
声开头的四字成语大全 声开头的四字成语大全声开头的四字成语大全 :声色俱厉、声如洪钟、声泪俱下、声情并茂、声东击西、声嘶力...
网络时代消费者心理特征和行为特... 网络时代消费者心理特征和行为特征是怎样的由于它能够提供丰富的商品信息,突破时空的限制,具有低廉的价格...
人生如梦,后面一句是什么 人生如梦,后面一句是什么人生如梦 一樽还酹江月人生如梦,需及时醒来,面对现实一樽还酹江月
求青梅竹马的小说 求青梅竹马的小说总是推的我都看过,多推点吧《夏有乔木,雅望天堂》感人死呢!!!!玄幻小说中有很多
想你第15集里面尹恩惠用的彩笔... 想你第15集里面尹恩惠用的彩笔是什么牌子的?这是马克笔 不管什么牌子效果都一样、和普通彩笔不同的就是...
焉栩嘉被痛斥劈腿背叛,情感失格... 焉栩嘉被痛斥劈腿背叛,情感失格的偶像算劣迹艺人吗?我认为情感失格的偶像应该就算是劣迹艺人人,因为他们...
求异界类似 {异界逍遥公}!和... 求异界类似 {异界逍遥公}!和幻神这样的! 或都市类的像 {龙啸九天-人界风云篇}!!主角蓝玉!我来...
我是从教师转行到财产保险公司做... 我是从教师转行到财产保险公司做保险营销员的,是个到公司快一年的新人,现在急求一份年终总结啊?manm...
改写人生是什么意思? 改写人生是什么意思?就是完全打破以往的人生规划,迎接一个不一样的人生。
找一本主角牙口特别好的小说? 找一本主角牙口特别好的小说?完美世界吗?
无双无对无法比打一数字? 无双无对无法比打一数字?无双无对无法比的数字是0。因为两个O仍是O。
一切都为了生活,那生活又为了什... 一切都为了生活,那生活又为了什么?生活就是你的一切,生活?生存活着!你的所有的努力只是为了活着,为了...
喜欢安静的人是什么性格 喜欢安静的人是什么性格喜欢安静的人通常本身也是比较文静的人,这类人的性格会属于内敛,内向型的。内向、...
哪个播放器能看《一生一世》 哪个播放器能看《一生一世》不好看,暴风影音就有哇如果有关视频的格式是播放器支持的都能看或播放
心里莫名的悸动是什么? 心里莫名的悸动是什么?心里老是莫名的悸动 搞不懂耶失眠、健忘、眩晕、耳鸣等并存,凡各种原因引起心脏搏...
怎样训犬 怎样训犬受训犬是指接受训练的犬。受训犬一般要求除符合本品种的特征外,还应注意:(1)体形外貌。机体各...
天为什么会黑? 天为什么会黑?这是因为地球自转造成的日月更替。地球绕太阳是公转,而在公转的同时地球也在自转。当地球自...
为什么前男友屏蔽朋友圈不让我看... 为什么前男友屏蔽朋友圈不让我看,但是又不删除我?为什么会这样啊都已经让对方变成前任啦!还纠结这些干嘛...