项目需求
- 编写客户端和服务器
- 客户端可以查看服务器端目录中的文件名
- 客户端可以从服务器中下载文件
- 客户端可以上传文件给服务器
开发步骤
1.流程图(一个大致的思路和想法)
2.根据流程图写框架(像做填空题一样,先写出题目,后去填充)
3.逐步实现每个功能(会发现流程图中的错误,回过头去补充完善)
FTP服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#define ERR_LOG(msg) do{\
perror(msg);\
printf("%d %s %s\n",__LINE__,__func__,__FILE__);\
}while(0)
#define FILELEN 20
#define DATALEN 512
typedef struct protocol
{
char type;
char file[FILELEN];
char data[DATALEN];
}PROTOC;
int main(int argc, const char *argv[])
{
if(argc < 3)
{
printf("请输入本地IP号和端口号\n");
return 0;
}
//1.流式套接字socket
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd < 0)
{
ERR_LOG("socket");
return -1;
}
//端口快速重用
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int))<0)
{
ERR_LOG("setsockopt");
return -1;
}
//2.bind绑定本地IP和端口
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(*(unsigned short*)argv[2]);
saddr.sin_addr.s_addr = inet_addr(argv[1]);
if(bind(sfd,(struct sockaddr*)&saddr,sizeof(saddr)) < 0)
{
ERR_LOG("bind");
return -1;
}
//3.listen
if(listen(sfd,5) < 0)
{
ERR_LOG("listen");
return -1;
}
printf("开始监听\n");
//变量定义
int option;
PROTOC buf = {0,{0},{0}};
int recv_len = 0;
char pathfile[FILELEN] = "";
while(1)
{
//4.accept
struct sockaddr_in caddr;
socklen_t caddr_len = sizeof(caddr);
int cfd = accept(sfd,(struct sockaddr*)&caddr,&caddr_len);
if(cfd < 0)
{
ERR_LOG("accept");
return -1;
}
printf("连接成功\n");
while(1)
{
recv_len = recv(cfd,&buf,sizeof(buf),0);
if(recv_len < 0)
{
ERR_LOG("recv");
return -1;
}
else if(0 == recv_len)
{
printf("ftp客户端退出\n");
break;
}
switch(buf.type)
{
case 'D'://下载请求
printf("收到下载文件的请求\n");
//获取文件目录列表
bzero(buf.data,DATALEN);
DIR* dp = opendir("./");
if(NULL == dp)
{
ERR_LOG("opendir");
return -1;
}
struct dirent *readptr = NULL;
while(1)
{
readptr = readdir(dp);
if(NULL == readptr)
{
printf("目录读取完毕\n");
break;
}
if(readptr->d_name[0] != '.')
{
strcat(buf.data,readptr->d_name);
strcat(buf.data,"\n");
}
}
closedir(dp);
//发送文件目录列表
buf.type = 'L';
buf.data[strlen(buf.data)-1] = 0;
if(send(cfd,&buf,sizeof(buf),0) < 0)
{
ERR_LOG("send");
return -1;
}
printf("目录列表发送完毕\n");
//等待接收要下载的文件名
if(recv(cfd,&buf,sizeof(buf),0) < 0)
{
ERR_LOG("recv");
return -1;
}
if('F' == buf.type)//接收到要下载的文件名
{
strcpy(pathfile,"./");
strcat(pathfile,buf.file);
pathfile[strlen(pathfile)-1] = 0;
//printf("%d\n", strlen(pathfile));//text
//printf("%s", pathfile);//text
}
//打开文件
int fd = open(pathfile,O_RDONLY);
if(fd < 0)
{
ERR_LOG("open");
return -1;
}
int read_len;
while(1)//读&发 文件数据
{
//读取文件数据
bzero(buf.data,DATALEN);
read_len = read(fd,buf.data,DATALEN);
if(read_len < 0)
{
ERR_LOG("read_len");
return -1;
}
else if(0 == read_len)//读取完毕
{
buf.type = 'O';
if(send(cfd,&buf,sizeof(buf),0) < 0)
{
ERR_LOG("send");
return -1;
}
printf(">>>>文件%s发送成功\n",pathfile+2);
break;
}
//发送文件数据
buf.type = 'W';
if(send(cfd,&buf,sizeof(buf),0) < 0)
{
ERR_LOG("send");
return -1;
}
}
close(fd);
break;
case 'U'://上传请求//接收客户端发来的文件名
printf("收到上传文件的请求\n");
strcpy(pathfile,"./");
strcat(pathfile,buf.file);
pathfile[strlen(pathfile)-1] = 0;
//创建文件
int fd1 = open(pathfile,O_WRONLY|O_CREAT|O_TRUNC,0664);
if(fd1 < 0)
{
ERR_LOG("open");
return -1;
}
while(1)//收&写 文件数据
{
//等待接收下载数据
if(recv(cfd,&buf,sizeof(buf),0) < 0)
{
ERR_LOG("recv");
return -1;
}
if('W' == buf.type)
{
//写入文件
if(write(fd1,buf.data,strlen(buf.data)) < 0)
{
ERR_LOG("write");
return -1;
}
}
else if('O' == buf.type)
{
printf(">>>>文件%s接收成功\n",pathfile+2);
break;
}
}
close(fd1);
break;
default:
printf("收到无效的请求指令\n");
break;
}
}
//close文件描述符
close(cfd);
}
//close文件描述符
close(sfd);
return 0;
}
FTP客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define ERR_LOG(msg) do{\
perror(msg);\
printf("%d %s %s\n",__LINE__,__func__,__FILE__);\
}while(0)
#define FILELEN 20
#define DATALEN 512
typedef struct protocol
{
char type;
char file[FILELEN];
char data[DATALEN];
}PROTOC;
int main(int argc, const char *argv[])
{
if(argc < 3)
{
printf("请输入服务器IP号和端口号\n");
return 0;
}
//1.流式套接字socket
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd < 0)
{
ERR_LOG("socket");
return -1;
}
//2.connect连接服务器
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(*(unsigned short*)argv[2]);
saddr.sin_addr.s_addr = inet_addr(argv[1]);
if(connect(sfd,(struct sockaddr*)&saddr,sizeof(saddr)) < 0)
{
ERR_LOG("connect");
return -1;
}
printf("连接成功\n");
//变量定义
int option;
PROTOC buf = {0,{0},{0}};
char pathfile[FILELEN] = "";
char inputfile[FILELEN] = "";
while(1)
{
printf("******FTP*******\n");
printf("*****1.下载*****\n");
printf("*****2.上传*****\n");
printf("*****3.退出*****\n");
printf("****************\n");
printf("请输入选项--->");
scanf("%d",&option);
getchar();
switch(option)
{
case 1://下载
//获取服务端文件列表
//发送下载请求给服务器
buf.type = 'D';//下载请求
if(send(sfd,&buf,sizeof(buf),0) < 0)
{
ERR_LOG("send");
return -1;
}
//等待接收文件目录列表
if(recv(sfd,&buf,sizeof(buf),0) < 0)
{
ERR_LOG("recv");
return -1;
}
if('L' == buf.type)
{
printf("*****文件列表*****\n");
printf("%s\n",buf.data);//打印文件目录列表
printf("*****文件列表*****\n");
printf("请输入要下载的文件名--->");
}
//发送要下载的文件名给服务器
bzero(inputfile,FILELEN);
fgets(inputfile,FILELEN,stdin);
buf.type = 'F';//Filename
bzero(buf.file,FILELEN);
strcpy(buf.file,inputfile);
if(send(sfd,&buf,sizeof(buf),0) < 0)
{
ERR_LOG("send");
return -1;
}
//打开(创建)文件
strcpy(pathfile,"./");
strcat(pathfile,inputfile);
pathfile[strlen(pathfile)-1] = 0;
//printf("%d\n", strlen(pathfile));//text
//printf("%s", pathfile);//text
int fd = open(pathfile,O_WRONLY|O_CREAT|O_TRUNC,0664);
if(fd < 0)
{
ERR_LOG("open");
return -1;
}
while(1)//收&写 文件数据
{
//等待接收下载数据
if(recv(sfd,&buf,sizeof(buf),0) < 0)
{
ERR_LOG("recv");
return -1;
}
if('W' == buf.type)
{
//写入文件
if(write(fd,buf.data,strlen(buf.data)) < 0)
{
ERR_LOG("write");
return -1;
}
}
else if('O' == buf.type)
{
printf(">>>>>>>>>文件%s下载成功<<<<<<<<<\n",pathfile+2);
break;
}
}
close(fd);
break;
case 2://上传
REINPUT:
printf("请输入要上传的文件名--->");
bzero(inputfile,FILELEN);
fgets(inputfile,FILELEN,stdin);
//打开文件
strcpy(pathfile,"./");
strcat(pathfile,inputfile);
pathfile[strlen(pathfile)-1] = 0;
int fd1 = open(pathfile,O_RDONLY);
if(fd1 < 0)
{
printf("当前目录没有这个文件\n");
goto REINPUT;
}
buf.type = 'U';//Upload Filename
bzero(buf.file,FILELEN);
strcpy(buf.file,inputfile);
if(send(sfd,&buf,sizeof(buf),0) < 0)
{
ERR_LOG("send");
return -1;
}
int read_len;
while(1)//读&发 文件数据
{
//读取文件数据
bzero(buf.data,DATALEN);
read_len = read(fd1,buf.data,DATALEN);
if(read_len < 0)
{
ERR_LOG("open");
return -1;
}
else if(0 == read_len)//读取完毕
{
buf.type = 'O';
if(send(sfd,&buf,sizeof(buf),0) < 0)
{
ERR_LOG("send");
return -1;
}
printf(">>>>>>>>>文件%s上传成功<<<<<<<<<\n",pathfile+2);
break;
}
//发送文件数据
buf.type = 'W';
if(send(sfd,&buf,sizeof(buf),0) < 0)
{
ERR_LOG("send");
return -1;
}
}
close(fd1);
break;
case 3://退出
close(sfd);
exit(1);
break;
default:
buf.type = 'e';
if(send(sfd,&buf,sizeof(buf),0) < 0)
{
ERR_LOG("send");
return -1;
}
printf("请输入正确的选项\n");
break;
}
}
//close文件描述符
close(sfd);
return 0;
}
演示
本文链接:https://shengto.top/network_programming/tcp_FTP.html
转载时须注明出处及本声明