项目简介:
Linux网络编程实现的FTP服务器,服务器由服务端和客户端组成,具有浏览远程服务端的文件和浏览客户端本地文件,客户端对远程服务端文件的上传和下载。
基本功能:
ls———查看服务端文件
lls———查看客户端自己的文件
cd———切换服务端目录
lcd———切换客户端自己的目录
put———上传文件
get———下载文件
基本思路:
服务端:socket 创建服务端的套接字 2:bind 端口号和IP地址 3:listen 监听客户端的连接 4:accept 接受客户端的接 入 5:read 接收
客户端:.socket 创建客户端的套接字,构建客户端和服务端发送和接收信息的桥梁 2.connect 连接上服务端 3.获取用户键 盘输入,处理输入命令buf 4.send (write)客户端的command到服务端 5.read 服务端返回的message
服务端代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"
//分割字符串
char *getDesDir(char *msg)
{
char *p;
p = strtok(msg," ");
p = strtok(NULL," ");
return p;
}
//把指令转化为整形数
int get_cmd_type(char *cmd)
{
if(!strcmp("ls",cmd)) return LS;
if(!strcmp("pwd",cmd)) return PWD;
if(!strcmp("quit",cmd)) return QUIT;
if(strncmp("cd",cmd,2)==0) return CD;
if(strncmp("get",cmd,3)==0) return GET;
if(strncmp("put",cmd,3)==0) return PUT;
return 100;
}
//处理客户端发来的指令
void msg_handler(struct Msg msg,int fd)
{
int filefd;
char *filename = NULL;
char databuf[1024] = {0};
printf("执行命令:%s\n",msg.cmd);
int ret = get_cmd_type(msg.cmd);
switch(ret){
case LS:
case PWD:
{
FILE *r = popen(msg.cmd,"r");
fread(msg.cmd,sizeof(msg.cmd),1,r);
write(fd,msg.cmd,sizeof(msg.cmd));
break;
}
/*从服务器上获取指定的文件*/
case GET:
filename = getDesDir(msg.cmd);
if(access(filename,F_OK) == -1){ //判断文件是否存在
strcpy(msg.cmd,"文件不存在");
write(fd,&msg,sizeof(msg));
}else{
msg.type = DOFILE;
filefd = open(filename,O_RDWR);
read(filefd,databuf,sizeof(databuf));
close(filefd);
strcpy(msg.buf,databuf);
write(fd,&msg,sizeof(msg));
}
break;
/*将本地客户端上的指定文件上传到服务器*/
case PUT:
filefd = open(getDesDir(msg.cmd),O_RDWR|O_CREAT,0666);
write(filefd,msg.buf,strlen(msg.buf));
close(filefd);
break;
/*改变服务器上的当前工作目录*/
case CD:
{
// msg.type = 1;
char *dir = getDesDir(msg.cmd);
printf("dir = %s\n",dir);
chdir(dir);
break;
}
case QUIT:
printf("服务器已退出\n");
exit(-1);
}
}
int main(int argc,char** argv){
int s_fd;
int c_fd;
int n_read;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
struct Msg msg;
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.创建套接字
s_fd = socket(AF_INET,SOCK_STREAM,0);
//2.绑定IP地址和端口号
s_addr.sin_family = AF_INET; //选择协议族
s_addr.sin_port = htons(atoi(argv[2])); //绑定端口号
inet_aton(argv[1],&s_addr.sin_addr); //将IP地址转化成网络能识别的格式
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.监听连接
listen(s_fd,10);
int clen = sizeof(struct sockaddr_in);
while (1)
{
//4.等待客户端连接
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
if(c_fd == -1){
perror("accpet");
exit(-1);
}
printf("connect = %s\n",inet_ntoa(c_addr.sin_addr));
//5.创建子进程对接客户端
if(fork() == 0){
while (1)
{
memset(msg.cmd,0,sizeof(msg.cmd));
//6.读取客户端发来的指令
n_read = read(c_fd,&msg,sizeof(msg));
if(n_read == 0){
printf("client quit\n");
break;
}else if(n_read > 0){
//7.处理指令
msg_handler(msg,c_fd);
}
}
}
}
close(s_fd);
close(c_fd);
return 0;
}
客户端代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"
//分割字符串
char *getDesDir(char *msg)
{
char *p;
p = strtok(msg, " ");
p = strtok(NULL, " ");
return p;
}
//把指令转化为整形数
int get_cmd_type(char *cmd)
{
if(!strcmp("ls",cmd)) return LS;
if(!strcmp("pwd",cmd)) return PWD;
if(!strcmp("quit",cmd)) return QUIT;
if(!strcmp("lls",cmd)) return LLS;
if(strncmp("cd",cmd,2)==0) return CD;
if(strncmp("get",cmd,3)==0) return GET;
if(strncmp("put",cmd,3)==0) return PUT;
if(strncmp("lcd",cmd,3)==0) return LCD;
return -1;
}
//处理指令
int cmd_handler(struct Msg msg,int fd)
{
// printf("msg.cmd = %s\n",msg.cmd);
int ret = get_cmd_type(msg.cmd);
char *dir = (char*)malloc(strlen(msg.cmd) + 1);
int filefd;
char buf[32]; //接收输入命令缓冲区
switch (ret){
case LS:
case PWD:
case CD:
{
write(fd,&msg,sizeof(msg));
break;
}
/*从服务器上获取指定的文件*/
case GET:
write(fd,&msg,sizeof(msg));
break;
/*将本地客户端上的指定文件上传到服务器*/
case PUT:
strcpy(buf,msg.cmd);
dir = getDesDir(buf); //获取文件名
if(access(dir,F_OK) == -1){
printf("文件不存在\n");
}else{
filefd = open(dir,O_RDWR);
read(filefd,msg.buf,sizeof(msg.buf));
close(filefd);
write(fd,&msg,sizeof(msg));
}
break;
/*改变本地客户端的当前工作目录*/
case LCD:
dir = getDesDir(msg.cmd); //获取文件名
if (chdir(dir) == -1) {
printf("无法进入目录 %s\n", dir);
}else{
printf("进入目录成功\n");
}
break;
/*列出本地客户端上的文件和目录*/
case LLS:
system("ls");
break;
case QUIT:
strcpy(msg.cmd,"quit");
write(fd,&msg,sizeof(msg));
close(fd);
exit(-1);
}
return ret;
}
void handler_server_message(int c_fd,struct Msg msg)
{
int n_read;
int filefd;
char *filename =(char*)malloc(strlen(msg.cmd) + 1);
struct Msg msgget;
n_read = read(c_fd,&msgget,sizeof(msgget));
//判断是否断开连接
if(n_read == 0){
printf("服务器已退出\n");
exit(-1);
}
//判断是否执行get指令
else if(msgget.type == DOFILE){
filename = getDesDir(msg.cmd);
printf("file=%s\n",filename);
filefd = open(filename,O_RDWR|O_CREAT,0600);
if (filefd < 0) {
perror("open");
exit(-1);
}
write(filefd,msgget.buf,sizeof(msgget.buf));
close(filefd);
putchar('>');
}
else{
printf("------------------------------------\n");
printf("%s\n",msgget.cmd);
printf("------------------------------------\n");
putchar('>');
fflush(stdout);
}
}
int main(int argc,char** argv)
{
int c_fd;
int n_read;
int ret;
struct Msg msg = {0};
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
c_fd = socket(AF_INET,SOCK_STREAM,0);
c_addr.sin_family = AF_INET; //选择协议族
c_addr.sin_port = htons(atoi(argv[2])); //绑定端口号
inet_aton(argv[1],&c_addr.sin_addr); //将IP地址转化成网络能识别的格式
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){
perror("connect");
exit(-1);
}
printf("connect...\n");
while(1){
memset(msg.cmd,0,sizeof(msg.cmd));
printf(">");
fgets(msg.cmd, sizeof(msg.cmd), stdin);
msg.cmd[strcspn(msg.cmd, "\n")] = 0; // 去掉换行符
ret = cmd_handler(msg,c_fd); //处理指令并送给服务端
if(ret > IFGO){
putchar('>');
fflush(stdout);
continue;
}
if(ret == -1){
printf("找不到该命令\n");
putchar('>');
fflush(stdout);
continue;
}
handler_server_message(c_fd,msg);
}
return 0;
}
config.h
#define LS 0
#define GET 1
#define PWD 2
#define IFGO 3
#define LCD 4
#define LLS 5
#define CD 6
#define PUT 7
#define QUIT 8
#define DOFILE 9
struct Msg{
int type;
char cmd[1024]; //指令接收缓冲区
char buf[1024]; //数据传输缓冲区
};