tftp通信协议分析 使用udp模型进行分析:
1. 服务器在69号端口等待客户端的请求
2. 服务器若批准此请求,则使用 ==**临时端口**== 与客户端进行通信。
3. 每个数据包的编号都有变化(从1开始)
4. 每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包
5. 数据长度以512Byte传输的,小于512Byte的数据意味着数据传输结束。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
//打印错误的宏函数
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
#define PORT 69
#define IP "192.168.31.145"
#define filename "5.png"
int main(int argc, const char *argv[])
{
//创建套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0); //SOCK_DGRAM为UDP协议
if(sfd<0){
ERR_MSG("socket");
return -1;
}
//允许端口快速复用
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt\n");
return -1;
}
//绑定客户端自身的地址信息结构体---》非必须绑定
//服务器的IP地址以及端口号-->因为客户端要主动发送数据包给服务器
struct sockaddr_in sin;
sin.sin_family =AF_INET;
sin.sin_port =htons(PORT);
sin.sin_addr.s_addr =inet_addr(IP);
//保存与客户端通信的目标地址结构体
struct sockaddr_in tar;
socklen_t addrlen=sizeof(tar);
//使用字符数组当消息载体
//组一个下载请求包
char buf[516]="";
char* ptr=buf;
short int *pa=(short int*)ptr; //操作码
*pa=htons(1);
char* pb=ptr+2; //文件名
strcpy(pb,filename);
char *pc=pb+strlen(pb); //模式
*pc=0;
char* pd=pc+1;
strcpy(pd,"octet");
int size=2+strlen(pb)+1+strlen("octet")+1;
printf("size=%d\n",size);
//发送请求
if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin))<0){
ERR_MSG("sendto");
return -1;
}
//在本地创建一个文件保存下载的内容
int fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0664);
if(fd<0){
ERR_MSG("open");
return -1;
}
char* file =(char*)(pa+2); //指向接收文件的数据首地址
short int data; //数据包的操作码
short int block; //数据包的块编号
int rec_size; //接受的数据大小
int size_cin=sizeof(sin); //服务器地址信息结构体大小
//循环接受文件数据
while(1){
bzero(buf,sizeof(buf));
//分析接受的数据
rec_size=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&size_cin);
if(rec_size<0){
ERR_MSG("recvfrom");
return -1;
}
//数据包操作码
data=ntohs(*pa);
if(data==5){
printf("数据包错误\n");
return -1;
}
//操作包的块编号
block=ntohs(*(pa+1));
//下载
if(write(fd,file,rec_size-4)<0){
ERR_MSG("write");
return -1;
}
//文件数据小于512,就是最后一次传输数据
printf("%d\n",rec_size);
if(rec_size<516){
printf("%s下载完成\n",filename);
break;
}
*pa=htons(4);
sendto(sfd,buf,4,0,(struct sockaddr*)&sin,sizeof(sin));
}
//关闭套接字
return 0;
}
实验现象;