基于UDP的tftp客户端
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#define ERR_MSG(msg) do{\
printf("__%d__\n", __LINE__);\
perror(msg);\
} while(0)
#define SER_IP "192.168.9.81"
#define SER_PORT 69
int download(int sfd, struct sockaddr_in sin);
int upload(int sfd, struct sockaddr_in sin);
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("socket create success sfd=%d\n", sfd);
//服务器的IP信息
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
int mode = 0;
while(1)
{
printf("1>下载 2>上传 0>退出\n请选择模式:");
scanf("%d", &mode);
while(getchar() != '\n');
switch(mode)
{
case 1:
if(download(sfd, sin) < 0)
printf("下载失败\n");break;
case 2:
if(upload(sfd, sin) < 0)
printf("上传失败\n");break;
case 0: goto exit;break;
}
}
exit:
if(close(sfd) < 0)
{
ERR_MSG("close");
return -1;
}
return 0;
}
int download(int sfd, struct sockaddr_in sin)
{
//组下载请求包
char buf[516] = "";
*(short*)buf = htons(1); //操作码1读 2写
char *ptr1 = buf + 2; //文件名
printf("请输入下载文件名>>>");
char name[128] = "";
scanf("%s", name);
strcpy(ptr1, name);
char *ptr2 = buf + strlen(ptr1) + 3; //模式
strcpy(ptr2, "octet");
int buflen = 2 + strlen(ptr1) + 1 + strlen(ptr2) + 1;
if(sendto(sfd, buf, buflen, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//收发数据包
struct sockaddr_in recvaddr;
socklen_t addrlen = sizeof(recvaddr);
short opflag = 0;
short req_num = 1; //己方需要的块编号
short rev_num = 0; //对方发送的块编号
ssize_t res = 0;
int fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if(fd < 0)
{
ERR_MSG("open");
return -1;
}
while(1)
{
bzero(buf, sizeof(buf));
res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&recvaddr, &addrlen);
if(res < 0)
{
ERR_MSG("recvfrom");
return -1;
}
opflag = ntohs(*(short*)buf);
rev_num = ntohs(*(short*)(buf+2));
if(3 == opflag && req_num++ == rev_num) //数据包
{
//将接收到的数据写入文件中
if(write(fd, buf+4, res-4) < 0)
{
ERR_MSG("write");
return -1;
}
//发送ACK应答包
*(short*)buf = htons(4);
if(sendto(sfd, buf, 4, 0, (struct sockaddr*)&recvaddr, addrlen) < 0)
{
ERR_MSG("sendto");
return -1;
}
if(res < 516)
break;
}
else if(5 == opflag) //错误码
{
printf("接收失败error%hd: %s\n", ntohs(*(short*)(buf+2)), buf+4);
break;
}
}
if(close(fd) < 0)
{
ERR_MSG("close");
return -1;
}
printf("下载成功\n");
printf("----------------------\n");
}
int upload(int sfd, struct sockaddr_in sin)
{
//组上传请求包
char buf[516] = "";
*(short*)buf = htons(2); //操作码1读 2写
char *ptr1 = buf + 2; //文件名
printf("请输入上传的文件名>>>");
char name[128] = "";
scanf("%s", name);
strcpy(ptr1, name);
char *ptr2 = buf + strlen(ptr1) + 3; //模式
strcpy(ptr2, "octet");
int buflen = 2 + strlen(ptr1) + 1 + strlen(ptr2) + 1;
if(sendto(sfd, buf, buflen, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//收发数据包
struct sockaddr_in recvaddr;
socklen_t addrlen = sizeof(recvaddr);
short opflag = 0;
short req_num = 0; //对方需要的块编号
short snd_num = 0; //己方发送的块编号
ssize_t res = 0;
int fd = open(name, O_RDONLY);
if(fd < 0)
{
ERR_MSG("open");
return -1;
}
//发送上传请求后,对面会回答一个ACK(0)
while(1)
{
//接受应答信息
bzero(buf, sizeof(buf));
res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&recvaddr, &addrlen);
if(res < 0)
{
ERR_MSG("recvfrom");
return -1;
}
opflag = ntohs(*(short*)buf);
req_num = ntohs(*(short*)(buf+2));
if(4==opflag && req_num==snd_num)
{
//发送数据包
bzero(buf, sizeof(buf));
*(short*)buf = htons(3);
*(short*)(buf+2) = htons(++snd_num);
res = read(fd, buf+4, sizeof(buf)-4);
if(res < 0)
{
ERR_MSG("read");
return -1;
}
else if(res == 0)
{
break;
}
if(sendto(sfd, buf, res+4, 0, (struct sockaddr*)&recvaddr, addrlen) < 0)
{
ERR_MSG("sendto");
return -1;
}
}
else
{
printf("error__%d__\n", __LINE__);
return -1;
}
}
if(close(fd) < 0)
{
ERR_MSG("close");
return -1;
}
printf("上传成功\n");
printf("----------------------\n");
}