项目简介
FTP服务器(File Transfer Protocol Server)是在互联网上提供文件存储和访问服务的计算机,它们依照FTP协议提供服务。 FTP是File Transfer
Protocol(文件传输协议)。
程序运行,服务端不断接收客户端指令,服务端可同时处理多个客户端接入并对指令作出解析,并把执行结果返回给客户端,客户端根据服务端对指令的解析并把由服务端传递过来的处理信息通过客户端呈现给客户,实现文件的各种操作。
嵌入式面试话术使用
这个项目分成ftp客户端及服务端,实现的功能和Linux开源的ftp服务器类似,客戶端通过网络,远程获取服务端磁盘上的文件夹内容,下载文件,上传文件等功能。(基本功能描述)
ftp服务器用到的是Socket通信,当收到客户端接入的时候,创建子进程对接连接,子进程启动后分析来自客户端的指令,比如收到get file1的指令,是客户端想要获取file1文件的,我先用strstr函数进行字符串分割,获取到文件名,在判断文件是否存在,如果文件存在,就读取文件內容,再将內容通过套接字发给客户端,客户端收到数据后,创建文件,并将收到的数据写入文件,完成文件的远程下载。(说明网络编程,字符串编程,文件编程的功底)
上传文件和下载文件类似,主要还是涉及文件的操作,字符串的操作,以及网络编程。
还支持了Is’pwd,cd等Linux系统常用的指令。普通指令的实现用popen来调用系统质量,并读取执行的结构。如果不需要获取执行结果,用system函数调用就可以了。(说明popen,system的编程)
这个项目我是来锻炼我的LinUx系统编程能力的,在学习系统编程的时候,我还学习了进程间通信,如管道,信号,共享内存,消息队列等。现在正在优化这个项目,想把这块知识用到项目中去,下次遇到项目的话就比较得心应手,做开发就是要多多折腾嘛。
功能说明
本文是基于Linux网络编程实现的FTP服务器,服务器由服务端和客户端组成,具有浏览远程服务端的文件和浏览客户端本地文件,同时支持对远程服务端文件的删除,存储,归档操作处理,以及客户端对远程服务端文件的上传和下载。
利用socket实现云盘的基本功能
- ls———查看服务端文件
- lls———查看客户端自己的文件
- cd———切换服务端目录
- lcd———切换客户端自己的目录
- put———上传文件
- get———下载文件
- pwd———显示路径
- quit———退出
代码编写
config.h
#define LS 0
#define LLS 1
#define CD 2
#define LCD 3
#define PUT 4
#define GET 5
#define PWD 6
#define QUIT 7
typedef struct msg
{
int type;
char data[2048];
char cmd[24];
}Msg;
tcpServer.c
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <errno.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include "config.h"
#include <fcntl.h>
int get_Cmd(char *readBuf)
{
if(strcmp("ls",readBuf) == 0) return LS;
if(strcmp("lls",readBuf) == 0) return LLS;
if(strstr(readBuf,"cd") != NULL) return CD;
if(strstr(readBuf,"lcd") != NULL) return LCD;
if(strstr(readBuf,"put") != NULL) return PUT;
if(strstr(readBuf,"get") != NULL) return GET;
if(strcmp("pwd",readBuf) == 0) return PWD;
if(strcmp("quit",readBuf) == 0) return QUIT;
return -1;
}
char *get_File_Name(char *cmd)//获取文件名
{
char *file_Name = NULL;
file_Name = strtok(cmd," ");
file_Name = strtok(NULL," ");//strtok函数固定用法
return file_Name;
}
int server_Handler(int *c_fd)
{
int n_read;
int fd;
int cmd = 666;
char *fileName = (char *)malloc(sizeof(char)*24);
FILE *p_fd;//popen的返回类型为FILE*
Msg r_msg;
Msg w_msg;
memset(&r_msg,'\0',sizeof(Msg));
memset(&w_msg,'\0',sizeof(Msg));
read(*c_fd,&r_msg,sizeof(Msg));
cmd = get_Cmd(r_msg.cmd);
switch(cmd)
{
case LS:
p_fd = popen("ls","r"); //调用popen函数执行 "ls-l"
if(p_fd == NULL) //判断是否popen成功
{
printf("popen error\n");
exit(-1);
}
fread(w_msg.data,1024,1,p_fd);//从块设备读到缓存
write(*c_fd,&w_msg,sizeof(Msg)); //缓存发给客户端,客户端调用read获得结果
fclose(p_fd); //关闭
printf("get cmd: %s\n",r_msg.cmd); //服务端调试信息
break;
case LLS:
printf("get cmd: lls\n");
break;
case CD:
fileName = get_File_Name(r_msg.cmd);//获取文件名
if(access(fileName,F_OK)==0)//判断该文件是否存在
{
//int chdir(const char *path)//改变当前工作目录
chdir(fileName);//系统调用函数(同cd)改变当前目录,即进入了文件夹
//不能用system(源码是fork 另起了一个shell 这里要求自己进入文件夹)
strcpy(w_msg.data,fileName);
write(*c_fd,&w_msg,sizeof(Msg));
}
else
{
strcpy(w_msg.data,"the server no have this file directory!");//如果没有则写入"the server no have this file directory!"
write(*c_fd,&w_msg,sizeof(Msg));
}
printf("get cmd: %s %s\n",r_msg.cmd,fileName);//服务端调试信息
break;
case LCD:
printf("get cmd: lcd\n");
break;
case PUT:
fileName = get_File_Name(r_msg.cmd);
read(*c_fd,&r_msg,sizeof(Msg));
if(strcmp(r_msg.data, "The client no have this document!") != 0)
{
if(access(fileName,F_OK)==0) //如果文件存在
{
fd = open(fileName,O_RDWR|O_TRUNC);//调用O_TRUNC将源文件内容删除,写入新内容
write(fd,r_msg.data,strlen(r_msg.data));
close(fd);
}
else
{
fd = creat(fileName, 0666);//如果不存在,创建
if(fd == -1)
{
perror("creat error!\n");
}
if(write(fd, r_msg.data, strlen(r_msg.data)) == -1)//写入新内容
{
perror("write error!\n");
}
close(fd);
}
}
printf("get cmd: %s %s\n",r_msg.cmd,fileName);
break;
case GET:
fileName = get_File_Name(r_msg.cmd);//获取文件名
if(access(fileName,F_OK)==0)//通过文件名判断文件是否存在
{
fd = open(fileName,O_RDWR);
read(fd,w_msg.data,2048);
write(*c_fd,&w_msg,sizeof(w_msg));//如果存在,即打开,读取,写入。
close(fd);
}else{
strcpy(w_msg.data,"no this document!");//不存在则写入"no this document!"
write(*c_fd,&w_msg,sizeof(w_msg));
}
printf("get cmd: %s %s\n",r_msg.cmd,fileName);
break;
case PWD:
p_fd = popen("pwd","r");//调用popen函数执行 "pwd"
if(p_fd == NULL)
{
printf("popen error\n");
exit(-1);
}
fread(w_msg.data,1024,1,p_fd);//将popen执行结果放在w_msg_buf.data_buf中
write(*c_fd,&w_msg,sizeof(Msg));//通过套接字将w_msg_buf.data_buf写到客户端吗,客户端调用read,然后输出即可获得结果
fclose(p_fd);
printf("get cmd : %s\n",r_msg.cmd);//服务端调试信息
break;
case QUIT:
strcpy(w_msg.data,"quit");
write(*c_fd,&w_msg,sizeof(w_msg));
printf("=====client exit=====\n");
close(*c_fd);
exit(0);
break;
case -1:
strcpy(w_msg.data, "指令错误");
write(*c_fd,&w_msg,sizeof(w_msg));
printf("===== cmd error =====\n");
break;
}
}
int main(int argc, char *argv[])
{
int s_fd;
int c_fd;
struct sockaddr_in c_addr;
struct sockaddr_in s_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
memset(&s_addr,0,sizeof(struct sockaddr_in));//数据清空
if(argc != 3){
printf("参数出错\n");
exit(-1);
}
//1.socket int socket(int domain, int type, int protocol);
s_fd=socket(AF_INET,SOCK_STREAM,0);
//ipv4 tcp协议
if(s_fd == -1){
printf("创建socket失败");
perror("socket:");
exit(-1);
}
//2.bind int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
s_addr.sin_family=AF_INET;//ipv4
s_addr.sin_port=htons(atoi(argv[2]));//端口号,选择5000以上。honts返回网络字节序,atoi(argv[2])防止端口被占用
//int inet_aton(const char *cp, struct in_addr *inp)
inet_aton(argv[1],&s_addr.sin_addr);//转换为网络能识别的格式
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.listen int listen(int sockfd, int backlog);
listen(s_fd,10);//监听10个连接
//4.accept int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int client=sizeof(struct sockaddr_in);
while(1){
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&client);
if(c_fd == -1){
printf("连接失败\n");
perror("accept:");
exit(-1);
}
printf("客户端IP:%s\n",inet_ntoa(c_addr.sin_addr));
if(fork() == 0){
while(1){
server_Handler(&c_fd);
}
}
}
close(s_fd);
return 0;
}
tcpcClient.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include<string.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"
int get_Cmd(char *readBuf)
{
if(strcmp("ls",readBuf) == 0) return LS;
if(strcmp("lls",readBuf) == 0) return LLS;
if(strstr(readBuf,"cd") != NULL) return CD;
if(strstr(readBuf,"lcd") != NULL) return LCD;
if(strstr(readBuf,"put") != NULL) return PUT;
if(strstr(readBuf,"get") != NULL) return GET;
if(strcmp("pwd",readBuf) == 0) return PWD;
if(strcmp("quit",readBuf) == 0) return QUIT;
return -1;
}
char *get_File_Name(char *cmd)
{
char *fileName = NULL;
fileName = strtok(cmd," "); //分割字符串
fileName = strtok(NULL," "); //strtok函数固定用法
return fileName;
}
void client_handler(int *c_fd)
{
int fd;
int n_read;
FILE *p_fd;//popen的返回类型为FILE*
int cmd = 0;
char *fileName = (char *)malloc(sizeof(char)*24);
Msg r_msg;
Msg w_msg;
memset(&r_msg,'\0',sizeof(Msg));
memset(&w_msg,'\0',sizeof(Msg));
printf(">>");
scanf("%[^\n]%*c",w_msg.cmd);
write(*c_fd,&w_msg,sizeof(w_msg));
cmd = get_Cmd(w_msg.cmd);
printf("-------------------------------------------------\n");
printf("\n");
switch(cmd){
case LS:
n_read = read(*c_fd, &r_msg, sizeof(Msg));
printf("%s",r_msg.data);//打印
printf("\n");
printf("-------------------------------------------------\n");
break;
case LLS:
p_fd = popen("ls","r");//调用popen函数执行"ls-l"
fread(r_msg.data,1024,1,p_fd);//客户端自己读取自己popen返回的内容
printf("%s\n",r_msg.data);//打印
fclose(p_fd);
break;
case PWD:
read(*c_fd, &r_msg, sizeof(Msg));
printf("%s\n",r_msg.data);//打印
printf("\n");
printf("-------------------------------------------------\n");
break;
case CD:
read(*c_fd, &r_msg, sizeof(Msg));
if(strcmp(r_msg.data,"the server no have this file directory!")==0)
{
printf("%s\n",r_msg.data);
}
else
{
printf("enter %s\n",r_msg.data);
}
printf("-------------------------------------------------\n");
break;
case LCD:
fileName = get_File_Name(w_msg.cmd);
if(access(fileName,F_OK) !=0)//判断该文件是否存在
{
printf("no find this file\n");
printf("\n");
printf("-------------------------------------------------\n");
}else{
//int chdir(const char *path)//改变当前工作目录
chdir(fileName);//系统调用函数(同cd)改变当前目录,即进入了文件夹
//不能用system(源码是fork 另起了一个shell 这里要求自己进入文件夹)
//strcpy(w_msg.data,"yes");
}
printf("get cmd: LCD\n");
printf("\n");
printf("-------------------------------------------------\n");
break;
case PUT:
fileName = get_File_Name(w_msg.cmd);
if(access(fileName, F_OK) == 0){
fd = open(fileName,O_RDWR);
read(fd,w_msg.data,sizeof(w_msg.data));
write(*c_fd, &w_msg, sizeof(Msg));
close(fd);
}else{
strcpy(w_msg.data,"The client no have this document!");
write(*c_fd,&w_msg,sizeof(Msg));
printf("%s\n",w_msg.data);
printf("\n");
printf("-------------------------------------------------\n");
}
break;
case GET:
read(*c_fd, &r_msg, sizeof(Msg));
fileName = get_File_Name(w_msg.cmd);
if(strcmp(r_msg.data,"no this document!")==0){
printf("%s\n",r_msg.data);
printf("\n");
printf("-------------------------------------------------\n");
}else{
if(access(fileName,F_OK)==0){
fd = open(fileName,O_RDWR|O_TRUNC);
if(fd==-1){
printf("open error!\n");
perror("why");
}else{
int w_ret = write(fd,r_msg.data,strlen(r_msg.data));//写入内容
if(w_ret==-1)
{
printf("write error!\n");
perror("why");
}
close(fd);
}
}else
{
fd = creat(fileName, 0666);//不存在即创建
if(fd == -1)
{
perror("creat error: ");
}
if(write(fd, r_msg.data, strlen(r_msg.data)) == -1)//写入
{
perror("write error: ");
}
close(fd);
}
printf("%s download success!\n",fileName);//提示下载成功
printf("\n");
printf("-------------------------------------------------\n");
}
break;
case QUIT:
printf("quit\n");
printf("\n");
printf("-------------------------------------------------\n");
exit(0);
break;
default :
read(*c_fd, &r_msg, sizeof(Msg));
printf("%s\n", r_msg.data);
printf("\n");
printf("-------------------------------------------------\n");
break;
}
}
int main(int argc,char *argv[])
{
int c_fd;
struct sockaddr_in c_addr;
if(argc != 3)
{
printf("param error\n");
exit(-1);
}
int clen = sizeof(struct sockaddr);
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket//创建套接字
//int socket(int domain, int type, int protocol);
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -1)
{
printf("client socket error\n");
perror("why");
}
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&(c_addr.sin_addr));
//2.connect 连接//客户连接主机
//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
if(connect(c_fd,(struct sockaddr *)&c_addr,clen) == -1)
{
printf("conncet error\n");
perror("why");
}
while(1){
client_handler(&c_fd);
}
return 0;
}