【网络编程】
创始人
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;
}

相关内容

热门资讯

深粮控股涨2.09%,成交额4... 5月19日,深粮控股盘中上涨2.09%,截至11:16,报6.85元/股,成交4763.88万元,换...
雷军:玄戒O1采用第二代3nm...   Hehson科技讯 5月19日上午消息,小米董事长雷军今日在微博发文称,小米战略新品发布会,定在...
国家矿山安监局四川局监察执法四... 近日,国家矿山安监局四川局监察执法四处依据批准的监察执法计划,联合区县煤矿安全监管部门对辖区某煤矿开...
医疗设备ETF(159873)... 5月19日,截止午间收盘,医疗设备ETF(159873)跌0.54%,报0.557元,成交额23.0...
中证1000ETF(15984... 5月19日,截止午间收盘,中证1000ETF(159845)跌0.04%,报2.471元,成交额2....
雪域“村BA”球王争霸赛开赛:... 5月18日晚,2025年肃北雪域“村BA”球王争霸赛在甘肃省酒泉市肃北蒙古族自治县党城湾镇党城村村民...
银河电子涨2.59%,成交额8... 5月19日,银河电子盘中上涨2.59%,截至11:28,报4.75元/股,成交8605.59万元,换...
TÜV南德授予精一家具检测认证...   近日,TÜV南德意志集团与广东精一家具股份有限公司在佛山顺德成功举行检测认证合作实验室授牌仪式。...
探秘麋鹿、看展闻香……北京经开... 新京报讯(记者吴婷婷)5月18日,北京经开区开启“科技馆之城”国际博物馆日、中国旅游日专场活动。北京...
三联虹普:公司现有业务尚未涉及... 每经AI快讯,有投资者在投资者互动平台提问:请问公司是否生产peek材料?三联虹普(300384.S...
“飞车党”夜里紧追小女孩将其吓... 来源:扬子晚报 近日,有网友发布的视频显示,5月17日晚9时29分许,有两人骑车紧追一名小女孩,女孩...
协鑫新能源以AI引擎驱动电力行...   在碳中和浪潮席卷全球的当下,一场由数字技术驱动的能源革命正悄然改写产业格局。5月8日至9日,中国...
新赛股份涨2.12%,成交额8... 5月19日,新赛股份盘中上涨2.12%,截至11:00,报4.81元/股,成交8729.13万元,换...
最高法、国家发改委:整治招投标... 为治理招投标乱象,最高人民法院、国家发展改革委5月19日联合发布6件人民法院审结的串通投标及其关联犯...
多只港股创新药ETF涨逾1% 5月19日,港股创新药板块持续走高,多只ETF涨逾1%,截至发稿,易方达恒生港股通创新药ETF涨1....
京华茶香漫校园,青春创意开启新... 春茶荡新香,2025年首农食品集团旗下中华老字号京华携茉莉茶香与年轻人共赴一场新式茶饮的创意之旅。当...
2025年全国游泳冠军赛:徐嘉... 5月18日晚,2025年全国游泳冠军赛男子100米仰泳半决赛在广东深圳举行,浙江寿仙谷队选手徐嘉余以...
【研报推荐】安徽合力:收入稳健... 转自:新华财经安徽合力:收入稳健增长,费用影响短期业绩.pdf
业绩释压,招行如何在逆风局中领... 伴随5月初政策中枢利率下调,市场对银行新一轮“降息”的预期被推向高点,这对息差承压多时的银行并不友好...
A股估值仍处于相对低位,证监会... 财联社5月19日讯(记者 闫军)5月19日至20日,由深圳证券交易所主办的2025全球投资者大会在深...