用C语言实现tftp协议的服务器与客户端的实验
扫描二维码
随时随地手机看文章
开发环境:ubuntu
所用知识点:c,socket, tcp/ip协议
A)本实验主要实现tftp协议的服务器与客户端。
服务器实现功能有:
1)接收处理客户端请求,上传下下载文件
2)进行用户验证
3)对传输数据进行加密解密处理
4)生成日志文件
客户端实现功能有:
1)向服务器发出请求,上传或下载文件
2)对传输数据加密解密
3)对用户信息进行MD5加密
B)相关代码实现:
宏定下:
#ifndef MAKEWORD
#define MAKEWORD(l,h) ((unsigned short)(((unsigned char)(l))|(((unsigned short)(unsigned char)(h))<<8)))
#endif
#define WSA_MAJOR_VERSION 1
#define WSA_MINOR_VERSION 1
#define WSA_VERSION MAKEWORD(WSA_MAJOR_VERSION, WSA_MINOR_VERSION)
#define TFTP_OCTET 1
#define TFTP_WSTAT_FIRSTACK 0
#define TFTP_WSTAT_NEXTACK 1
#define TFTP_WSTAT_LASTACK 2
#define TFTP_RRQ 1 //读请求
#define TFTP_WRQ 2 //写请求
#define TFTP_DATA 3 //数据
#define TFTP_ACK 4 //ACK
#define TFTP_ERROR 5 //Error
#define MAX_RETRY 3 //最大重复次数
#define TFTP_NOTEND_DATALEN 512+2+2 //数据块长度
//错误种类
#define Not_defined 0
#define File_not_found 1
#define Access_violation 2
#define Disk_full 3
#define Illegal_TFTP_operation 4
#define Unknown_port 5
#define File_already_exists 6
#define No_such_user 7
#define Time_out 8
#define Read_file_Error 9
#define Cannot_create_file 10
#define passwd_or_user_error 11
包的填充:
#include "define.h"
#include
#include
int makeack(unsigned short num,char *buffer,int size );
int makedata(unsigned short num,char *data,int datasize,char *buffer,int bufsize);
int makeerr(unsigned short num,char *buffer);
//ACK包填充
int makeack(unsigned short num,char *buffer,int size )
{
int pos = 0;
buffer[pos] = 0;
pos++;
buffer[pos] = TFTP_ACK; //操作码为04
pos++;
buffer[pos] = (char)(num>>8);//块号2个字节
pos++;
buffer[pos] = (char)num;
pos++;
return pos;
}
//Data包填充
int makedata(unsigned short num,char *data,int datasize,char *buffer,int bufsize)
{
int pos = 0;
buffer[pos] = 0;
pos++;
buffer[pos] = TFTP_DATA; //操作码为03
pos++;
buffer[pos] = (char)(num>>8);//块号
pos++;
buffer[pos] = (char)num;
pos++;
memcpy(&buffer[pos],data,datasize);//填充数据
pos = pos + datasize;
return pos;
}
//ERROR包填充
int makeerr(unsigned short num,char *buffer)
{
int pos=0;
buffer[pos]=0;
pos++;
buffer[pos]=TFTP_ERROR; //操作码为05
pos++;
buffer[pos] = (char)(num>>8); //错误种类号
pos++;
buffer[pos] = (char)num;
pos++;
return pos;
}
日志log.c实现
#include
static char log[100]; //日志
char datetime[20]; //记录时间变量
int timeout=2,retran=3; //服务器参数
void record(int a,struct sockaddr_in *sin,char *file)
{
char tem[60];
time_t t=time(0); //初始化日历时间
strftime(datetime,sizeof(datetime),"%y/%m/%d %X",localtime(&t));//将时间格式化
strcat(log,datetime);//将时间写入记录
//将字符串格式化
bzero(&tem,sizeof(tem));
if(a==1)
sprintf(tem," 收到来自 %s 上传文件 %s 的请求。n",inet_ntoa(sin->sin_addr),file);
if(a==2)
sprintf(tem," %s 上传文件 %s 完毕。n",inet_ntoa(sin->sin_addr),file);
if(a==3)
sprintf(tem," 收到来自 %s 下载文件 %s的请求。n",inet_ntoa(sin->sin_addr),file);
if(a==4)
sprintf(tem," %s 下载文件 %s 完毕。n",inet_ntoa(sin->sin_addr),file);
if(a==5)
sprintf(tem," 出现出错,操作中断。n",inet_ntoa(sin->sin_addr),file);
//将具体信息写入记录
strcat(log,tem);
FILE *write;
if((write=fopen("log.txt","a+"))==NULL)
printf("打开记录文件失败n");
//将记录写入文件
fwrite(&log,strlen(log),1,write);
fclose(write);
bzero(&log,sizeof(log));
}
加密解密实现
#include
#include
#include
int decrypt(FILE *in,FILE *out);
int encrypt(FILE *in,FILE *out);
unsigned char atoh(char *hexstr);
int encrypt(FILE *in,FILE *out)
{
if(in == NULL || out == NULL)
{
fprintf(stderr,"%sn","file error!n");
return -1;
}
unsigned char hex;
while(fread(&hex,1,1,in))
{
hex = ~hex^0x98;
fprintf(out,"%02X",hex);
}
return 0;
}
int decrypt(FILE *in,FILE *out)
{
if(in == NULL || out == NULL)
{
fprintf(stderr,"%sn","file error!");
return -1;
}
unsigned char hexstr[3];
unsigned char hex = 0;
int i = 0;
while(fread(hexstr,2,1,in))
{
hex = atoh(hexstr);
hex = ~(hex ^ 0x98);
fwrite(&hex,1,1,out);
}
return 0;
}
/* convert string to hex */
unsigned char atoh(char *hexstr)
{
int i;
int hextodec[]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
char chtodec[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
unsigned char hexnum = 0;
for(i = 0; i < sizeof(chtodec); ++i)
{
if(hexstr[0] == chtodec[i])
{
hexnum += hextodec[i]*16;
}
}
for(i = 0; i < sizeof(chtodec); ++i)
{
if(hexstr[1] == chtodec[i])
{
hexnum += hextodec[i];
}
}
return hexnum;
}
上传数据:
//上传函数
void upload(struct sockaddr_in sour_addr,char buffer[])
{
char send_buffer[1024] = {0};
char recv_buffer[1024] = {0};
struct sockaddr_in dest_addr;
struct timeval timeout = {10,0};
int sour_len = 0;
int ret = 0;
int len = 0;
int flen = 0;
fd_set fdr;
unsigned short lastdata = 0;
unsigned short blocknum = 0;
FILE *file;
FILE *decrypt_file = NULL;
char filename[256];
//获取文件名
strcpy(filename,buffer+2);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = sour_addr.sin_port;
dest_addr.sin_addr.s_addr = inet_addr(desthost);//
//如果本地存在同名文件
if((file=fopen(filename,"rb"))!=NULL)
{
//发送一个error包,报告存在同名文件
printf("***存在同名文件***");
len = makeerr(File_already_exists,send_buffer);
ret = sendto(sock,send_buffer,len,0,(struct sockaddr *)&dest_addr,sizeof(dest_addr));
record(5,&sour_addr,filename);
return;
}
//建立文件
if((file=fopen(filename,"w+b"))==NULL)
{
//如果失败,发送error包
printf("创建文件失败n");
len = makeerr(Cannot_create_file,send_buffer);
ret = sendto(sock,send_buffer,len,0,(struct sockaddr *)&dest_addr,sizeof(dest_addr));
record(5,&sour_addr,filename);
return;
}
//发送ACK
len = makeack(blocknum,send_buffer,sizeof(send_buffer));
ret = sendto(sock,send_buffer,len,0,(struct sockaddr *)&dest_addr,sizeof(dest_addr));
blocknum++;
while(1){
FD_ZERO(&fdr);
FD_SET(sock, &fdr);
ret = select(sock+1, &fdr, NULL,NULL, &timeout);
if(-1==ret)
{
printf("Socket 错误n");
fclose(file);
record(5,&sour_addr,filename);
return;
}
else
{
if(0==ret)
{
printf("超时n");
fclose(file);
record(5,&sour_addr,filename);
return;
}
else
{
if (FD_ISSET(sock,&fdr))
{
//接收数据包
sour_len = sizeof(struct sockaddr);
ret = recvfrom(sock,recv_buffer,sizeof(recv_buffer),0,(struct sockaddr *)&sour_addr,&sour_len);
//如果是数据包
if(TFTP_DATA==recv_buffer[1])
{
lastdata = MAKEWORD(recv_buffer[3],recv_buffer[2]); //块号
//如果块号正确
if(lastdata == blocknum)
{
//发送ACK包
len = makeack(blocknum,send_buffer,sizeof(send_buffer));
sendto(sock,send_buffer,len,0,(struct sockaddr *)&dest_addr,sizeof(dest_addr));
blocknum++;
if(blocknum > 65535)
blocknum = 0;
//最后一包
if(ret < TFTP_NOTEND_DATALEN)
{
//写入文件
fwrite(&recv_buffer[4],1,ret-4,file);
flen = flen + ret -4;
#ifdef _DEBUG_
printf("%s %s %dn", __FILE__, __FUNCTION__, __LINE__);
printf("*****传输结束,共收到 %d 字节*****n",flen);
#endif
rewind(file);
if((decrypt_file =fopen("decrypt_temp","wb+"))==NULL){
printf("decrypt file open error n");
return;
}
decrypt(file, decrypt_file);
fclose(decrypt_file);
rename("decrypt_temp", filename);
fclose(file);
record(2,&sour_addr,filename);
return;
}
else
{
fwrite(&recv_buffer[4],1,512,file);
flen = flen + 512;
#ifdef _DEBUG_
printf("%s %s %dn", __FILE__, __FUNCTION__, __LINE__);
printf("已收到 %d 字节n",flen);
#endif
}
}
else
{
//重新发送ACK包
printf("数据包块号错误.n");
sendto(sock,send_buffer,len,0,(struct sockaddr *)&dest_addr,sizeof(dest_addr));
}
}
}
}
}
}
}
C)客户端与服务都配有工程管理器,可通过make 完成编译。
D)实现效果: