12_tftp
输模式:octet:二进制模式netascii:文本模式2、TFTP的通信过程决定了 编程流程TFTP通信过程
总结(无选项)1、服务器在69号端口等待客户端的请求2、服务器若批准此请求,则使用临时端口与客户端进行通信3、
每个数据包的编号都有变化(从1开始)4、每个数据包都要得到ACK的确认如果出现超时,则需要重新发送最后的包(数
据或ACK)5、数据的长度以512Byte传输6、小于512Byte的数据意味着传输结束3、TFTP的协议分析
4、TFTP带选项的读写报文(创新)tsize选项当读操作时,tsize选项的参数必须为“0”,服务器会返回待
读取的文件的大小当写操作时,tsize选项参数应为待写入文件的大小,服务器会回显该选项blksize选项修改传
输文件时使用的数据块的大小(范围:8~65464)timeout选项修改默认的数据传输超时时间(单位:秒)知识
点2【抓包工具wireshark】1、过滤规则1、IP过滤1 ip.src==10.9.11.251只显示源I
P为10.9.11.251的数据报文2 ip.dst==10.9.11.251只显示目的IP为10.9.11.
251的数据报文3 ip.addr==10.9.11.251只显示源IP或目的IP为10.9.11.251的数
据报文2、协议过滤1 tcp udp arp tftp3、端口过滤1 udp.srcport==8000只显示
源端口为8000的udp报文2 udp.dstport==8000只显示目的端口为8000的udp报文3 ud
p.port==8000只显示目的或源端口为8000的udp报文45 tcp.srcport==8000只显示
源端口为8000的tcp报文6 tcp.dstport==8000只显示目的端口为8000的tcp报文7 tc
p.port==8000只显示目的或源端口为8000的tcp报文4、mac地址过滤1 eth.src==aa:
bb:cc:dd:ee:ff只显示源mac为aa:bb:cc:dd:ee:ff的数据报文2 eth.dst==
aa:bb:cc:dd:ee:ff只显示目的mac为aa:bb:cc:dd:ee:ff的数据报文3 eth.a
ddr==aa:bb:cc:dd:ee:ff只显示源mac或目的mac为aa:bb:cc:dd:ee:ff的数
据报文5、逻辑与与逻辑或1 and ==> && 逻辑与 同时满足2 or ==> || 逻辑或 只要有一个满
足就行2、对抓取到的数据报文分析知识点3【TFTP案例】需求:编写TFTP客户端(并不带选项) 从服务器下载文
件流程:1 sockfd = 创建UDP套接字2 构建读请求报文、并发送到服务器的69号端口3 fd=open
以写的方式打开本地文件4 while(1)5 {6 unsigned char buf[516]="";7 i
nt len = recvfrom(sockfd,buf,sizeof(buf),0, &dst_addr,s
izeof(ds_addr));8 write(fd,buf+4,len‐4);9 if(len < 516)
10 break;11 }12 close(fd);13 close(sockfd);构建下载a.txt的报文
1 unsigned char read_cmd[128]="";2 int s_len = sprintf(
read_cmd,"%c%c%s%c%s%c",0,1,"a.txt", 0,"octet", 0);3 se
ndto(sockfd,read_cmd,s_len,0,dst_addr‐‐>69 )tftp客户端(下载文
件)1 #include <stdio.h>2 #include <stdlib.h>3 #include <
unistd.h>4 #include <fcntl.h>5 #include <string.h>6 #in
clude <sys/socket.h>7 #include <netinet/in.h>8 #include
<arpa/inet.h>9 #include <signal.h>1011 static int sock
fd;1213 void sig_dispose(int sig)14 {15 if(SIGINT == si
g){16 close(sockfd);17 puts("\nclose!");18 exit(0);19 }
20 }21 /***********************************************
*************************22 函数名称: int main(int argc, ch
ar *argv[])23 函数功能: tftp客户端程序,可以从tftp服务器下载程序24 ********
*******************************************************
*********/25 int main(int argc, char *argv[])26 {27 uns
igned short p_num = 0;28 unsigned char cmd = 0;29 char
cmd_buf[512] = "";30 char recv_buf[516] = "";31 struct
sockaddr_in client_addr;32 socklen_t cliaddr_len = size
of(client_addr);33 signal(SIGINT,sig_dispose);3435 if(
argc < 3 ){ //如果参数个数小于3,则认为输入命令错误36 printf("cmd example
:./tftpc 192.168.8.8 hello.txt\n");37 return 0;38 }3940
sockfd = socket(AF_INET, SOCK_DGRAM, 0);41 if(sockfd <
0){42 perror("socket error");43 exit(‐1);44 }4546 stru
ct sockaddr_in dest_addr;47 bzero(&dest_addr, sizeof(de
st_addr));48 dest_addr.sin_family = AF_INET;49 dest_add
r.sin_port = htons(69);50 inet_pton(AF_INET, argv[1], &
dest_addr.sin_addr.s_addr);5152 //构造下载请求,argv[2]为文件名53
int len = sprintf(cmd_buf, "%c%c%s%c%s%c", 0, 1, argv[2
], 0, "octet",发送读数据包请求54 sendto(sockfd, cmd_buf, len, 0
, (struct sockaddr *)&dest_addr, sizeof(dest_addr));555
6 int fd = open(argv[2], O_WRONLY|O_CREAT, 0666);57 if(
fd < 0 ){58 perror("open error");59 close(sockfd);60 ex
it(‐1);61 }6263 do{64 //接收服务器发送的内容65 len = recvfrom(soc
kfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&
client_addr, &cliaddr_len);6667 cmd = recv_buf[1];68 if
( cmd == 3 ) //是否为数据包69 {70 //接收的包编号是否为上次包编号+171 if((un
signed short)(p_num+1) == ntohs(*(unsigned short*)(recv
_buf+2) ))72 {73 write(fd, recv_buf+4, len‐4);74 p_num
= ntohs(*(unsigned short*)(recv_buf+2));7576 printf("re
cv:%d\n", p_num);//十进制方式打印包编号77 }78 recv_buf[1] = 4;79
sendto(sockfd, recv_buf, 4, 0, (struct sockaddr*)&clien
t_addr, sizeof(client_addr));80 }81 else if( cmd == 5 )
//是否为错误应答82 {83 close(sockfd);84 close(fd);85 unlink(a
rgv[2]);//删除文件86 printf("error:%s\n", recv_buf+4);87 ex
it(‐1);88 }89 }while(len == 516); //如果收到的数据小于516则认为出错90
91 close(fd);92 return 0;93 }知识点4【UDP广播】1、广播的概述每一个网段,主机
ID全为0,为网段地址,主机ID全为1,为广播地址。10.9.11.0/24 它的网段地址为10.9.11.0
广播地址为10.9.11.255广播:由一台主机向该主机所在子网内的所有主机发送数据的方式广播只能用UDP或
原始IP实现,不能用TCP。以下几个协议都用到广播1、地址解析协议(ARP)2、动态主机配置协议(DHCP)3
、网络时间协议(NTP)2、UDP广播的特点1、处于同一子网的所有主机都必须处理数据2、UDP数据包会沿协议栈
向上一直到UDP层3、运行音视频等较高速率工作的应用,会带来大负4、局限于局域网内使用3、UDP广播地址{网络
ID,主机ID}网络ID表示由子网掩码中1覆盖的连续位主机ID表示由子网掩码中0覆盖的连续位定向广播地址:主机
ID全11、例:对于192.168.220.0/24,其定向广播地址为192.168.220.2552、通常路
由器不转发该广播受限广播地址:255.255.255.255路由器从不转发该广播4、广播与单播的对比单播:广播
:5、广播编程socket函数创建的套接字 一般不支持广播。setsockopt函数 可以让 套接字 支持广播
。1 int setsockopt(int sockfd, int level,int optname,con
st void *optval,2 socklen_t optlen);成功执行返回0,否则返回-1level
optname 说明 optval类型SOL_SOCKETSO_BROADCAST 允许发送广播数据包int
1 #include <stdio.h>2 #include <stdlib.h>3 #include <un
istd.h>4 #include <fcntl.h>5 #include <string.h>6 #incl
ude <sys/socket.h>7 #include <netinet/in.h>8 #include <
arpa/inet.h>9 int main(int argc, char const *argv[])10
{11 //创建一个udp套接字12 int sockfd = socket(AF_INET, SOCK_DG
RAM, 0);1314 //使用setsockopt函数设置sockfd支持广播15 int yes = 1
;16 setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &yes,
sizeof(int));1718 //发送广播数据19 struct sockaddr_in dst_add
r;20 // memset(&dst_addr,0,sizeof(dst_addr));21 bzero(&
dst_addr, sizeof(dst_addr));22 dst_addr.sin_family = AF
_INET;23 dst_addr.sin_port = htons(8000);24 inet_pton(A
F_INET, "10.9.11.255", &dst_addr.sin_addr.s_addr);2526
sendto(sockfd, "broadcast msg", strlen("broadcast msg")
, 0,27 (struct sockaddr *)&dst_addr, sizeof(dst_addr));
2829 close(sockfd);3031 return 0;32 }知识点5【udp多播】1、多播的概述
多播:数据的收发仅仅在同一分组中进行多播的特点:1、多播地址标示一组接口2、多播可以用于广域网使用3、在IPv
4中,多播是可选的2、多播地址多播IP地址范围:224.0.0.1 ~ 239.255.255.254多播ma
c地址映射:基于mac地址的不完备硬件过滤:基于IP地址的完备软件过滤3、多播地址结构体1 int setso
ckopt(int sockfd, int level,int optname, const void *op
tval,2 socklen_t optlen);level optname 说明 optval类型IPPRO
TO_IPIP_ADD_MEMBERSHIP 加入多播组 ip_mreq{}IP_DROP_MEMBERSHI
P 离开多播组 ip_mreq{}1 #include <stdio.h>2 #include <stdlib
.h>3 #include <unistd.h>4 #include <fcntl.h>5 #include
<string.h>6 #include <sys/socket.h>7 #include <netinet/
in.h>8 #include <arpa/inet.h>9 int main(int argc, char
const *argv[])10 {11 //创建一个udp套接字12 int sockfd = socket
(AF_INET, SOCK_DGRAM, 0);1314 //bind绑定固定端口15 struct soc
kaddr_in my_addr;16 bzero(&my_addr, sizeof(my_addr));17
my_addr.sin_family = AF_INET;18 my_addr.sin_port = hto
ns(8000);19 my_addr.sin_addr.s_addr = htonl(INADDR_ANY)
;20 bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my
_addr));2122 //使用setsockopt函数加入224.0.0.2多播组中23 struct i
p_mreq mreq;24 // inet_pton(AF_INET,"224.0.0.2",&mreq.i
mr_multiaddr.s_addr);25 mreq.imr_multiaddr.s_addr = ine
t_addr("224.0.0.2"); //多播组IP26 mreq.imr_interface.s_add
r = inet_addr("10.9.11.45"); //加入多播组的IP27 setsockopt(so
ckfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(stru
ct ip_mreq));2829 //接收多播组的消息30 while (1)31 {32 unsigned
char buf[1500] = "";33 recvfrom(sockfd, buf, sizeof(bu
f), 0, NULL, NULL);34 printf("%s\n", buf);35 }3637 clos
e(sockfd);3839 return 0;