C语言编程实现TFTP网络通信
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//打印错误新的宏函数
#define ERR_MSG(msg) do{\
fprintf(stderr, " __%d__ ", __LINE__);\
perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{
if(argc < 2)
{
fprintf(stderr, "请输入IP\n");
return -1;
}
/*
//将获取到的端口号字符串,转换成整形
int port = atoi(argv[2]);
if(port < 1024 || port > 49151)
{
fprintf(stderr, "port %d input error!! 1024~49151\n", port);
return -1;
}
*/
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//组一个请求包发送给服务器
char filename[10] = "";
char buf[516] = "";
printf("请输入你要下载的文件名:");
scanf("%s",filename);
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"); //将模式存入
size_t size = 2+strlen(pb)+1+strlen("octet")+1; //求出请求包的长度
//填充服务器的IP地址以及端口号 -->因为客户端要主动发送数据包给服务器
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(69);
sin.sin_addr.s_addr = inet_addr(argv[1]);
//发送请求包
sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin));
struct sockaddr_in rcv_addrmsg; //存储接收到的数据包来自哪里
socklen_t addrlen = sizeof(rcv_addrmsg);
//打开一个要存的文件
int fd = open("./copy.png",O_RDWR|O_TRUNC|O_CREAT,0777);
if(fd < 0)
{
ERR_MSG("open");
return -1;
}
printf("打开成功\n");
unsigned char ack[4]; //ACK包
unsigned short code = 0; //操作码
unsigned short newnum = 0; //块编号 或者 错误码
//数据的长度以512Byte传输
char text[512] = {0}; //文件内容 或 错误信息
int oldnum = 0;
//主要功能代码
while(1)
{
bzero(buf,sizeof(buf));
bzero(text,sizeof(text));
ssize_t res;
//接收服务器发来的数据包
res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&rcv_addrmsg, &addrlen);
if(res < 0)
{
ERR_MSG("recvfrom");
return -1;
}
printf("%ld接收成功\n",res);
//解析操作码
code = ntohs(*(unsigned short *)buf);
//解析块编号 或者 错误码
newnum = ntohs(*(unsigned short *)(buf + 2));
//判断操作码和块编号
if(code == 3 && newnum == (oldnum + 1))
{
char* a = buf + 4;
if(res < 516)
{
write(fd,buf+4,res - 4);
printf("传输完成\n");
break;
}
else if(res == 516)
{
write(fd,buf+4,512);
}
//组建新的ack包
*(unsigned short *)ack = htons(4);
*(unsigned short *)(ack + 2) = htons(newnum);
//向服务器发送ack包
if(sendto(sfd, ack, 4, 0, (struct sockaddr*)&rcv_addrmsg, sizeof(rcv_addrmsg)) < 0)
{
ERR_MSG("sendto");
return -1;
}
printf("sendto success\n");
oldnum++;
}
else if(code == 5)
{
printf("发生错误[%s]\n",text);
return -1;
}
}
printf("下载完成\n");
close(sfd);
return 0;
}