功能描述:
利用socket和TCP协议实现多个客户端和服务端之间文件的上传、下载等基本网盘功能
配置文件
confing.h
//配置文件
#ifndef _CONFIG_H
#define _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
#define LPWD 10
struct Msg//装载消息的结构体
{
int type;
char data[1024];
char secondBuf[128];
};
#endif
服务端
ftpserver.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <config.h>
#include <sys/stat.h>
#include <fcntl.h>
int get_cmd_type(char *cmd) //将传入的命令进行匹配,返回对应的宏给命令处理函数进行处理
{
if(!strcmp("ls",cmd)) return LS;
if(!strcmp("quit",cmd)) return QUIT;
if(!strcmp("pwd",cmd)) return PWD;
if(strstr(cmd,"cd") != NULL) return CD;
if(strstr(cmd,"get") != NULL) return GET;
if(strstr(cmd,"put") != NULL) return PUT;
}
char *getDesdir(char *cmsg)//将传入的字符串进行分割,返回分割后的字符串
{
char *p = NULL;
p = strtok(cmsg," ");
p = strtok(NULL," ");
return p;
}
void msg_handler(struct Msg msg,int fd)//命令处理函数
{
char dataBuf[1024] = {
0};
char *file = NULL;
int fdfile;
printf("cmd:%s\n",msg.data);
int ret = get_cmd_type(msg.data);//获取命令
switch(ret){
case LS://ls、pwd命令
case PWD:
msg.type = 0;
FILE *r = popen(msg.data,"r");//用popen函数执行ls、pwd命令 ,返回一个文件流
fread(msg.data,sizeof(msg.data),1,r);//将刚刚返回的文件流读入到消息息结构体中
pclose(r);//关闭文件流
write(fd,&msg,sizeof(msg));//向客户端发送消息结构体
break;
case CD://cd 命令
msg.type = 1;
char *dir = getDesdir(msg.data);//将cd命令和其后面跟的目录分隔开,返回后面跟的目录
printf("dir:%s\n",dir);
chdir(dir);//用chdir函数改变当前进程所在的目录
break;
case GET://获取文件
file = getDesdir(msg.data);//得到get命令后面跟的文件名
if(access(file,F_OK) == -1){
//判断文件是否存在
strcpy(msg.data,"NO This File!");//不存在则将文件不存在写入到消息结构体中
write(fd,&msg,sizeof(msg));//向客户端发送消息结构体
}else{
//文件存在
msg.type = DOFILE;//设置消息结构体中的type成员
fdfile = open(file,O_RDWR);//打开文件
read(fdfile,dataBuf,sizeof(dataBuf));//将文件内容读到dataBuf中
close(fdfile);//关闭文件
strcpy(msg.data,dataBuf);//将dataBuf中的内容拷贝到消息结构体中
write(fd,&msg,sizeof(msg));//向客户端发送消息结构体
}
break;
case PUT://上传文件
fdfile = open(getDesdir(msg.data),O_RDWR|O_CREAT,0666);//根据客户端传过来的文件名打开或创建对应文件名的文件
write(fdfile,msg.secondBuf,strlen(msg.secondBuf));//将消息结构体中的secondBuf成员中存放的内容(文件内容)写入到刚刚打开的文件中
close(fdfile);//关闭文件
break;
case QUIT://客户端退出命令
printf("client quit!\n");
exit(-1);
}
}
int main(int argc,char *argv[])
{
int s_fd;
int c_fd;
int n_read;
struct sockaddr_in s_addr;//存放服务端socket连接信息的结构体
struct sockaddr_in c_addr;//用来存放接入客户端信息的结构体
struct Msg msg;//消息结构体
memset(&s_addr,0,sizeof(struct sockaddr_in));//清空结构体中的内容
memset(&c_addr,0,sizeof(struct sockaddr_in));
if(argc != 3){
//判断用户给的参数全不全,参数不够则退出程序
printf("param deficiency!\n");
exit(-1);
}
//1.socket
s_fd = socket(AF_INET,SOCK_STREAM,0);// 用套接字创建一个支持TCP协议的连接 ,返回一个socket描述符
if(s_fd == -1){
//判断套接字是否创建成功,失败则输出原因以及退出程序
perror("socket");
exit(-1);
}
//2.bind
s_addr.sin_family = AF_INET;//采用的通信协议族为IPV4
s_addr.sin_port = htons(atoi(argv[2]));//将Linux的小端字节序端口号转换为网络字节序的端口号赋给结构体的端口变量
inet_aton(argv[1],&(s_addr.sin_addr));//将IP地址转换为网络能识别的形式赋给IP地址变量
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//将上面初始化好的信息绑定到刚刚创建的socket服务端上上
//3.listen
listen(s_fd,10);//监听客户端,同时设置最多可以接入的客户端数量
//4.accept
socklen_t s_len = sizeof(struct sockaddr_in);
while(1){
//死循环不断接受新客户端的接入
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&s_len);//接受客户端接入
if(c_fd == -1){
//客户端接入失败打印错误信息
perror("accept");
}
printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));//打印接入的客户端的ip地址
if(fork() == 0){
//每接入一个客户端,则创建一个子进程去服务它
while(1){
memset(&msg.data,0,sizeof(msg.data));//每次接受消息之前清空一下命令buf中的内容
n_read = read(c_fd,&msg,sizeof(msg));//接受客户端发过来的消息结构体
if(n_read == 0){
printf("client quit!\n");
break;
}else if(n_read >0){
msg_handler(msg,c_fd);//将收到的消息结构体传入处理函数中进行处理
}
}
}
}
close(c_fd);//关闭客户端连接
close(s_fd); //关闭服务端连接
return 0;
}
客户端
ftpclient.c
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <config.h>
int get_cmd_type(char *cmd)//将传入的命令进行匹配,返回对应的宏给命令处理函数进行处理
{
if(strstr(cmd,"lcd") != NULL) return LCD;
if(strstr(cmd,"cd") != NULL) return CD;
if(strstr(cmd,"put") != NULL) return PUT;
if(strstr(cmd,"get") != NULL) return GET;
if(!strcmp("ls",cmd)) return LS;
if(!strcmp("lls",cmd)) return LLS;
if(!strcmp("pwd",cmd)) return PWD;
if(!strcmp("lpwd",cmd)) return LPWD;
if(!strcmp("quit",cmd)) return QUIT;
return -1;
}
char *getDesdir(char *cmsg)//将传入的字符串进行分割,返回分割后的字符串
{
char *p = NULL;
p = strtok(cmsg," ");
p = strtok(NULL," ");
return p;
}
int cmd_handler(struct Msg msg,int fd)
{
char *dir = NULL;
char buf[32] = {
0};
int ret;
int fdfile;
ret = get_cmd_type(msg.data);//获取命令
switch(ret){
case LS://ls、cd、pwd命令
case CD:
case PWD:
msg.type = 0;
write(fd,&msg,sizeof(msg));//将ls、cd、pwd命令发送给服务端
break;
case GET://获取文件命令
msg.type = 2;
write(fd,&msg,sizeof(msg));//将获取文件命令发送给服务端
break;
case PUT://上传文件命令
strcpy(buf,msg.data);//将消息结构体中的命令拷贝到buf中
dir = getDesdir(buf);//获取文件名
if(access(dir,F_OK) == -1){
//判断文件是否存在
printf("%s is no exist\n",dir);//文件不存在则打印错误进行提示用户
}else{
//文件存在
fdfile = open(dir,O_RDWR);//打开对应文件
read(fdfile,msg.secondBuf,sizeof(msg.secondBuf));//将文件内容读取到消息结构体的secondBuf成员中
close(fdfile);//关闭文件
write(fd,&msg,sizeof(msg));//将消息结构体发送给服务端
break;
}
case LLS://客户端自己执行ls命令
system("ls");//用system函数执行脚本ls
break;
case LPWD://客户端自己执行pwd命令
system("pwd");//用system函数执行脚本pwd
break;
case LCD://客户端自己执行cd命令
dir = getDesdir(msg.data);//得到目录
chdir(dir);//改变客户端的当前目录
break;
case QUIT://客户端退出命令
strcpy(msg.data,"quit");//将quit拷贝到消息中
write(fd,&msg,sizeof(msg));//向服务端发送消息结构体
close(fd);//关闭客户端的socket
exit(-1);//退出程序
}
return ret;//返回命令对应的宏
}
void handler_server_messag(int c_fd,struct Msg msg)
{
int n_read;
struct Msg msgget;
int newfilefd;
n_read = read(c_fd,&msgget,sizeof(msgget));//接收服务端穿回来的消息结构体
if(n_read == 0){
//如果传回的消息为空,则服务端退出了
printf("server quit!\n");
exit(-1);
}
else if(msgget.type == DOFILE){
//如果传回来的消息中type为DOFILE
char *dir = getDesdir(msg.data);//获取文件名字
newfilefd = open(dir,O_RDWR|O_CREAT,0666);//打开或创建对应文件名的文件
write(newfilefd,msgget.data,strlen(msgget.data));//将收到的消息中的文件内容写入刚刚打开的文件中
close(newfilefd);//关闭文件
putchar('>');
fflush(stdout);
}
else{
//打印传回来的消息中的内容
printf("-----------------------------------\n");
printf("\n%s\n",msgget.data);
printf("-----------------------------------\n");
putchar('>');
fflush(stdout);
}
}
int main(int argc,char *argv[])
{
int c_fd;
int n_read;
struct sockaddr_in c_addr;//存放socket连接消息的结构体
struct Msg msg;//消息结构体
memset(&c_addr,0,sizeof(struct sockaddr_in));//清空结构体中的内容
if(argc != 3){
//判断用户给的参数全不全,参数不够则退出程序
printf("param definiency!\n");
exit(-1);
}
//1.socket
c_fd = socket(AF_INET,SOCK_STREAM,0);// 用套接字创建一个支持TCP协议的连接 ,返回一个socket描述符
if(c_fd == -1){
//判断套接字是否创建成功,失败则输出原因以及退出程序
perror("socket");
exit(-1);
}
c_addr.sin_family = AF_INET;//采用的通信协议族为IPV4
c_addr.sin_port = htons(atoi(argv[2]));//将Linux的小端字节序端口号转换为网络字节序的端口号赋给结构体的端口变量
inet_aton(argv[1],&(c_addr.sin_addr));//将IP地址转换为网络能识别的形式赋给IP地址变量
//2.connect
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) == -1){
//客户端连接服务端
perror("connect");
exit(-1);
}
printf("connect...\n");
int mark = 0;
while(1){
//客户端业务
memset(msg.data,0,sizeof(msg.data));//每次循环都清空一下命令
if(mark == 0) printf(">");//格式控制
gets(msg.data);//键盘不断获取用户的命令输入
if(strlen(msg.data) == 0 && mark == 1){
//格式控制
printf(">");
continue;
}
mark = 1;
int ret = cmd_handler(msg,c_fd);//将用户输入的命令传入进行处理后返回一个值方便下面的逻辑控制
if(ret>IFGO){
//在客户端方执行的命令
putchar('>');
fflush(stdout);
continue;
}
if(ret == -1){
//没有这个命令
printf("command not find\n");
fflush(stdout);
printf(">");
continue;
}
handler_server_messag(c_fd,msg);//接受服务端的消息处理函数
}
return 0;
}