文章目录
基于Socket套接字的FTP服务简单实现
文件传输协议(File Transfer Protocol,FTP)是用于在网络上进行文件传输的一套标准协议,它工作在 OSI 模型的第七层, TCP 模型的第四层, 即应用层, 使用 TCP 传输而不是 UDP, 客户在和服务器建立连接前要经过一个“三次握手”的过程, 保证客户与服务器之间的连接是可靠的, 而且是面向连接, 为数据传输提供可靠保证。
一、项目简介
1、利用套接字实现客户端和服务端的连接;
2、服务端和客户端可以互传文件;
3、客户端和服务端信息交互实现类似Linux系统下vi的快捷指令操作。
二、项目具体实现功能
1、客户端本地功能(xx代表文件或者文件夹名称):
- 客户端查看当前文件路径:lpwd
- 查看客户端当前目录下所有本地文件:lls
- 客户端进入本地文件夹:lcd xx
2、客户端和服务端远程功能:
- 客户端查看服务端的当前文件路径:pwd
- 客户端查看服务端当前路径下所有文件:ls
- 客户端进入服务端文件夹:cd xx
- 客户端上传文件至服务端:upload xx
- 客户端下载服务端文件到本地:download xx
三、代码实现
1、服务端代码实现:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <fcntl.h>
//宏定义相关端指令
#define QUIT 0
#define LS 1
#define PWD 2
#define LLS 3
#define LPWD 4
#define LCD 5
#define CD 6
#define UPLOAD 7
#define DOWNLOAD 8
struct Msg{
int type;
char cmd[128];
char filename[128];
char data[1024];
}msg;
char *getDir(char *cmd);//分割指令和文件,返回文件/文件夹名称
int analyse_Cmd(char *cmd);//分析指令
void getCmd(int c_fd,struct Msg msg);//根据指令做出响应
int main(int argc, char **argv)
{
int s_fd;
int c_fd;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
int s_len = sizeof(struct sockaddr_in);
int c_len = sizeof(struct sockaddr_in);
memset(&s_addr,'\0',s_len);
memset(&c_addr,'\0',c_len);
//1.socket
if((s_fd = socket(AF_INET,SOCK_STREAM,0)) == -1){
printf("creat socket failed\n");
exit(-1);
}
//2.bind
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
if(bind(s_fd,(struct sockaddr *)&s_addr,s_len) != 0){
perror("bind");
exit(-1);
}
//3.listen
if(listen(s_fd,10) != 0){
perror("listen");
exit(-1);
}
while(1){
printf("wait connect -----\n");
//4.accept
if((c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len)) == -1){
perror("accept");
exit(-1);
}else{
printf("get connect %s\n",inet_ntoa(c_addr.sin_addr));
}
//创建子进程专门处理指令
if(fork() == 0){
while(1){
memset(&msg,'\0',sizeof(struct Msg));
read(c_fd,&msg,sizeof(struct Msg));
printf("cmd:%s\n",msg.cmd);
getCmd(c_fd,msg);
}
}
close(s_fd);
close(c_fd);
return 0;
}
//将指令和文件名称分割,得到文件/文件夹名称
char *getDir(char *cmd)
{
char *p;
p = strtok(cmd," ");
p = strtok(NULL," ");
return p;
}
//分析指令
int analyse_Cmd(char *cmd)
{
if(!strcmp(cmd,"quit")) return QUIT;
if(!strcmp(cmd,"ls")) return LS;
if(!strcmp(cmd,"pwd")) return PWD;
if(strstr(cmd,"cd")) return CD;
if(strstr(cmd,"upload")) return UPLOAD;
if(strstr(cmd,"download")) return DOWNLOAD;
return -1;
}
//根据指令实现相应功能
void getCmd(int c_fd, struct Msg msg)
{
int fd;
FILE *fp;
char *pret;
char readBuf[1024] = {
0};
int ret = analyse_Cmd(msg.cmd);
switch(ret)
{
case LS:
case PWD:
fp = popen(msg.cmd,"r");
fread(msg.data,1,sizeof(msg.data),fp);
write(c_fd,&msg,sizeof(struct Msg));
pclose(fp);
printf("msg.data \n %s\n",msg.data);
break;
case CD:
pret = getDir(msg.cmd);
if(chdir(pret) == -1){
perror("chdir");
}
getcwd(msg.data,sizeof(msg.data));
write(c_fd,&msg,sizeof(struct Msg));
break;
case UPLOAD:
pret = getDir(msg.filename);
fd = open(pret,O_RDWR|O_CREAT,0600);
write(fd,msg.data,strlen(msg.data)+8);
close(fd);
printf("upload success\n");
break;
case DOWNLOAD:
msg.type = DOWNLOAD;
strcpy(msg.filename,msg.cmd);
pret = getDir(msg.cmd);
if(open(pret,O_RDWR) < 0 ){
write(c_fd,"Fiel not found",20);
exit(-1);
}else{
fd = open(pret,O_RDWR);
read(fd,readBuf,sizeof(readBuf));
strcpy(msg.data,readBuf);
write(c_fd,&msg,sizeof(struct Msg));
close(fd);
}
break;
case QUIT:
printf("client quit!\n");
exit(1);
break;
}
}
2、客户端代码实现:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
//宏定义相关指令
#define QUIT 0
#define LS 1
#define PWD 2
#define LLS 3
#define LPWD 4
#define LCD 5
#define CD 6
#define UPLOAD 7
#define DOWNLOAD 8
struct Msg{
int type;
char cmd[128];
char filename[128];
char data[1024];
}msg;
char *getDir(char *cmd);//分割指令和文件夹名称,返回文件/文件夹名称
int analyse_Cmd(char *cmd);//分析客户端发出的指令
void cmd_Handler(int c_fd, struct Msg msg);//处理客户端的指令
void getData(int c_fd, struct Msg msg);//得到服务端返回的数据
int main(int argc, char **argv)
{
int c_fd;
int mark = 0;
struct sockaddr_in c_addr;
int c_len = sizeof(struct sockaddr_in);
memset(&c_addr,'\0',c_len);
//1.socket
if((c_fd = socket(AF_INET,SOCK_STREAM,0)) == -1){
printf("creat socket failed\n");
exit(-1);
}
//2.connect
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
if(connect(c_fd,(struct sockaddr *)&c_addr,c_len) != 0){
perror("connect");
exit(-1);
}
printf("connect success\n");
while(1){
memset(&msg,'\0',sizeof(struct Msg));
if(mark == 0){
printf(">");
}
gets(msg.cmd);
if(strlen(msg.cmd) == 0){
if(mark == 1){
printf(">");
}
continue;
}
mark = 1;
cmd_Handler(c_fd,msg);
if(fork() == 0){
getData(c_fd,msg);
}
}
close(c_fd);
return 0;
}
//返回文件/文件夹名称
char *getDir(char *cmd)
{
char *p;
p = strtok(cmd," ");
p = strtok(NULL," ");
return p;
}
//分析指令
int analyse_Cmd(char *cmd)
{
if(!strcmp(cmd,"quit")) return QUIT;
if(!strcmp(cmd,"ls")) return LS;
if(!strcmp(cmd,"pwd")) return PWD;
if(!strcmp(cmd,"lls")) return LLS;
if(!strcmp(cmd,"lpwd")) return LPWD;
if(strstr(cmd,"lcd")) return LCD;
if(strstr(cmd,"cd")) return CD;
if(strstr(cmd,"upload")) return UPLOAD;
if(strstr(cmd,"download")) return DOWNLOAD;
return -1;
}
//处理指令内容
void cmd_Handler(int c_fd,struct Msg msg)
{
int fd;
int ret;
char *pret;
char readBuf[1024] = {
0};
ret = analyse_Cmd(msg.cmd);
switch(ret)
{
case LS:
case PWD:
case CD:
case DOWNLOAD:
write(c_fd,&msg,sizeof(struct Msg));
break;
case LLS:
system("ls");
break;
case LPWD:
system("pwd");
break;
case LCD:
pret = getDir(msg.cmd);
if(chdir(pret) == -1){
perror("chdir");
}
fd = chdir(pret);
printf("dir: %s\n",pret);
getcwd(msg.data,sizeof(msg.data));
printf("change client dir:%s\n",msg.data);
break;
case UPLOAD:
strcpy(msg.filename,msg.cmd);
pret = getDir(msg.cmd);
if(open(pret,O_RDWR) < 0){
printf("no file\n");
}else{
fd = open(pret,O_RDWR);
read(fd,msg.data,sizeof(msg.data));
close(fd);
write(c_fd,&msg,sizeof(struct Msg));
printf("upload success\n");
}
break;
case QUIT:
write(c_fd,&msg,sizeof(struct Msg));
exit(1);
default:
write(c_fd,&msg,sizeof(struct Msg));
break;
}
}
//
void getData(int c_fd,struct Msg msg)
{
int fd;
char *pret;
memset(msg.data,'\0',sizeof(msg.data));
read(c_fd,&msg,sizeof(struct Msg));
if(msg.data == NULL){
printf("no msg\n");
}
else if(msg.type == DOWNLOAD){
pret = getDir(msg.filename);
fd = open(pret,O_RDWR|O_CREAT,0600);
write(fd,msg.data,strlen(msg.data)+8);
printf("download success\n");
close(fd);
}
else{
printf("<---------------------->\n");
printf("\n%s\n",msg.data);
printf("<---------------------->\n");
}
}
3、代码实现效果
第一步:客户端和服务端建立连接
第二步:客户端发送lls指令查看当前路径下所有本地文件,发送ls指令查看服务端当前路径下的文件
第三步:客户端发送uoload aa.txt上传本地文件aa.txt至服务端,发送download config.txt下载服务端config.txt文件到客户端
第四步:查看上传和下载的文件
第五步:客户端发送pwd指令查看服务端文件当前所在路径,然后cd …进入上层文件夹,然后cd xx回到当前文件夹
第六步:客户端发送指令查看本地文件夹所在目录,然后cd FTP进入FTP文件夹,cd …回到当前文件夹
第七步:客户端发送quit指令退出连接
4、总结:
在数据交互之前,要记得使用memset清空上一次客户端和服务端数据交换的内容。另外,除了自己写的简单指令外,还可以使用Linux自带的指令功能:输入ftp 127.0.0.1之后在输入乌班图的账号密码,用ftp+指令(如ftp ls)就可以使用相关指令了。
至此,基于Socket套接字实现简单FTP功能项目基本完成,如遇不足,烦请交流指正。