模仿tftp上传、下载文件,
约定格式:
上传 put filename# //中间空格、末尾#号表示完成输入
下载 get filename# //同上,
停止 quit
参考:https://blog.csdn.net/qq_65687767/article/details/126830833
TFTP就是一个传输文件的简单协议,没有FTP功能那么齐全
客户端代码
执行时 ./client ip地址 port
#include<stdlib.h>
#include<stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
void parsestr( char * str1,char cmd[],char filename[]);
int tcp_connect(const char *server_ip,int server_port);
void upload(int sockfd,char *filename);
void download(int sockfd,char *filename);
void Datahandler(int confd,char *cmd,char *filename);
int main(int argc, const char *argv[])
{
char cmdbuf[1024] = {0};
char cmd[100] = {0};
char filename[100] = {0};
int con_fd = 0;
//输入参数个数判断
if(argc != 3)
{
perror("输入错误,请输入ip地址和端口号");
return -1;
}
//链接服务器,返回后的socket对象
con_fd = tcp_connect(argv[1],atoi(argv[2]));
if(con_fd > 0)
{
printf("server connected\n");
}
else
{
printf("server connect failed\n");
}
while(1)
{
printf("请输入如下操作命令:\n");
printf("下载: get 文件名#\n");
printf("上传: put 文件名#\n");
printf("停止: quit \n");
memset(cmdbuf,0,sizeof(cmdbuf));
fgets(cmdbuf,sizeof(cmdbuf),stdin);//键盘输入操作命令
int num = send(con_fd,cmdbuf,strlen(cmdbuf),0);//发送命令到服务器
if(num == -1)
{
perror("send error:");
exit(-1);
}
if(strncmp(cmdbuf,"quit",4) == 0)
exit(-1);
parsestr(cmdbuf,cmd,filename);//解析输入字符串
Datahandler(con_fd,cmd,filename);//处理数据
}
close(con_fd);
return 0;
}
//3、处理数据函数
void Datahandler(int confd,char *cmd,char *filename)
{
if(strncmp(cmd,"get",3) == 0)
{
//下载文件
download(confd,filename);
}
else if(strncmp(cmd,"put",3) == 0)
{
/* 上传文件 */
upload(confd,filename);
}
else
{
printf("cmd is error\n");
}
}
//3.1 下载文件
void download(int sockfd,char *filename)
{
char text[1024] = {0};
int bytes = 0;
int total = 0;
struct stat1
{
int errnum;
struct stat filestat;
};
struct stat1 filestat1;
memset(&filestat1,0,sizeof(filestat1));
recv(sockfd,&filestat1,sizeof(filestat1),0);
if(filestat1.errnum == 1)
{
printf("\n");
printf("************************\n");
printf("%s文件不存在服务器中.........\n",filename);
printf("************************\n");
printf("\n");
return;
}
int fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,777);
if(fd < 0)
{
perror("open error");
return;
}
while(1)
{
if(total >= filestat1.filestat.st_size)
{
printf("大小为 %ldkb 的 %s 文件下载成功!\n",filestat1.filestat.st_size,filename);
close(fd);
break;
}
bytes = recv(sockfd,text,sizeof(text),0);
write(fd,text,bytes);
total += bytes;
}
}
//3.2 上传文件
void upload(int sockfd,char *filename)
{
struct stat1
{
int errnum;
struct stat filestat;
};
struct stat1 filestat1;
char text[1024] = {0};
int bytes = 0;
memset(&filestat1,0,sizeof(filestat1));
errno = 0;
if(stat(filename,&filestat1.filestat) < 0)
{
if(errno == ENOENT)
{
filestat1.errnum = 1;
send(sockfd,&(filestat1),sizeof(filestat1),0);//发送文件大小
printf("error:该文件不存在此目录。\n");
return;
}
else
{
perror("stat file error:");
close(sockfd);
exit(-1);
}
}
else
{
send(sockfd,&(filestat1),sizeof(filestat1),0);//发送文件大小
}
int fd = open(filename,O_RDONLY);
if(fd < 0)
{
perror("open file error");
exit(-1);
}
while(1)
{
memset(text,0,sizeof(text));
bytes = read(fd,text,sizeof(text));
if(0 == bytes)
{
printf("大小为 %ldkb 的 %s 文件上传成功\n",filestat1.filestat.st_size,filename);
close(fd);
break;
}
send(sockfd,text,bytes,0);
}
}
//1、请求链接服务器
int tcp_connect(const char *server_ip,int server_port)
{
char *serverip;
int sock_fd = socket(AF_INET,SOCK_STREAM,0);
if(sock_fd == -1)
{
perror("socket error");
return -1;
}
struct sockaddr_in server_para;
memset(&server_para,0,sizeof(server_para));
server_para.sin_family = AF_INET;
server_para.sin_addr.s_addr = inet_addr(server_ip);
server_para.sin_port = htons(server_port);
if((connect(sock_fd,(struct sockaddr *)&server_para,sizeof(server_para))) < 0)
{
perror("connect errror");
return -1;
}
return sock_fd;
}
//2、解析输入字符串
void parsestr(char * str1,char cmd[],char filename[])
{
memset(filename,0,sizeof(filename));
memset(cmd,0,sizeof(cmd));
int i = 0;
while(str1[i] == ' ') //跳过cmd前面多余的空格
{
i++;
continue;
}
int k = 0;
while(str1[i+k] != ' ')
{
cmd[k] = str1[i+k];
k++;
}
cmd[k] = '\0';
k++;//把空格跳过
while(str1[i+k] == ' ') //跳过cmd后面多余的空格
{
k++;
continue;
}
int j = 0;
while(str1[i+k+j] != '#')
{
filename[j] = str1[i+k+j];
j++;
}
filename[i+k+j] = '\0';
}
服务器代码
执行时 ./server ip地址 port
#include<stdio.h>
#include <sys/socket.h>
#include <errno.h>
#include<netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
int tcp_tocket(char *server_ip,int port1);
void parsestr(char cmdchar[],char cmd[],char filename[]);
void upload(int newfd,char *filename);
void download(int newfd, char *filename);
void Datahandler(int newfd,char *cmd,char *filename);
int main(int argc,char *argv[])
{
char cmdchar[1024];
char cmd[20] = {0};
char filename[500] = {0};
int newfd = 0;
int byte = 0;
newfd = tcp_tocket(argv[1],atoi(argv[2]));
if(newfd > 0)
printf("client connect on\n");
else
printf("client connect failed\n");
while(1)
{
memset(cmdchar,0,sizeof(cmdchar));
byte = recv(newfd,cmdchar,sizeof(cmdchar),0);
if(byte < 0)
{
perror("recv error:");
exit(-1);
}
else if(byte == 0)
{
printf("client connect off\n");
exit(-1);
}
if(strncmp(cmdchar,"quit",4) == 0)
exit(-1);
parsestr(cmdchar,cmd,filename); // 接受字符解析
Datahandler(newfd,cmd,filename);
}
close(newfd);
return 0;
}
void Datahandler(int newfd,char *cmd,char *filename)
{
if(0 == strncmp(cmd,"get",3))
download(newfd,filename);
else if(0 == strncmp(cmd,"put",3))
upload(newfd,filename);
else
{
printf("cmd error\n");
// close(newfd);
// exit(-1);
return;
}
}
void download(int newfd, char *filename) //客户端请求下载文件
{
struct stat1
{
int errnum;
struct stat filestat;
};
struct stat1 filestat1;
char text[1024];
int num = 0;
memset(&filestat1,0,sizeof(filestat1));
errno = 0;
if(stat(filename,&filestat1.filestat) < 0)
{
if(errno == ENOENT)
{
filestat1.errnum = 1;
send(newfd,&filestat1,sizeof(filestat1),0);
printf("客户端需求的文件不存在,等待其重新发送\n");//主要发送文件大小
return;
}
else
{
perror("stat file error:");
close(newfd);
exit(-1);
}
}
else
{
send(newfd,&filestat1,sizeof(filestat1),0);//主要发送文件大小
}
int fd = open(filename,O_RDONLY);
if(fd < 0)
{
perror("open file error");
exit(-1);
}
while (1)
{
memset(text,0,sizeof(text));
num = read(fd,text,sizeof(text));
if(num == 0)
{
printf("大小为%ld 的 %s 文件 发送完毕!!\n",filestat1.filestat.st_size,filename);
close(fd);
break;
}
send(newfd,text,num,0);
}
}
void upload(int newfd,char *filename) //客户端请求上传文件
{
struct stat1
{
int errnum;
struct stat filestat;
};
struct stat1 filestat1;
char text[1024] = {0};
int total = 0;
int bytes = 0;
recv(newfd,&filestat1,sizeof(filestat1),0); //文件大小
if(filestat1.errnum == 1)
return;
int fd = open(filename,O_RDWR|O_CREAT|O_TRUNC,777);
if(fd < 0)
{
perror("open file error");
exit(-1);
}
while(1)
{
memset(&text,0,sizeof(text));
bytes = recv(newfd,text,sizeof(text),0);
total += bytes;
if(total >= filestat1.filestat.st_size)
{
write(fd,text,bytes);
printf("大小为 %ldkb 的 %s 文件 接受完毕\n",filestat1.filestat.st_size,filename);
close(fd);
break;
}
write(fd,text,bytes);
}
}
void parsestr(char cmdchar[],char cmd[],char filename[])
{
memset(cmd,0,sizeof(cmd));
memset(filename,0,sizeof(filename));
int i = 0;
while(cmdchar[i] == ' ') //跳过cmd前面多余的空格
{
i++;
continue;
}
int k = 0;
while (cmdchar[i+k] != ' ')
{
cmd[k] = cmdchar[i+k];
k++;
}
if(cmdchar[i+k] == ' ')
cmd[k] = '\0';
k++;
while(cmdchar[i+k] == ' ') //跳过cmd后面多余的空格
{
k++;
continue;
}
int j = 0;
while (cmdchar[i+k+j] != '#')
{
filename[j] = cmdchar[i+k+j];
j++;
}
filename[i+k+j] = '\0';
}
//tcp连接
int tcp_tocket(char *server_ip,int port1)
{
int sock_fd = socket(AF_INET,SOCK_STREAM,0);
if(sock_fd < 0)
{
perror("connect error");
return -1;
}
struct sockaddr_in sockaddrin;
memset(&sockaddrin,0,sizeof(sockaddrin));
sockaddrin.sin_family = AF_INET;
sockaddrin.sin_addr.s_addr = inet_addr(server_ip);
sockaddrin.sin_port = htons(port1);
int isockoptval = 1;
if(setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&isockoptval,sizeof(isockoptval)) == -1)
{ //解决重新连接服务器时出现bind:address already in use问题
perror("setsockopt fail");
close(sock_fd);
// exit(-1);
}
if(bind(sock_fd, (struct sockaddr*)&sockaddrin, sizeof(sockaddrin)) < 0)
{
perror("bind failed");
return -1;
}
if(listen(sock_fd,10) < 0)
{
perror("listen failed");
return -1;
}
int accept_fd = accept(sock_fd, NULL,NULL);
return accept_fd;
}
以上程序均在ubuntu系统中调试成功