1sever.c
// 服务器代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <fcntl.h>
#define N 256//文件名和命令名最长为256字节
void commd_ls(int);
void commd_get(int, char *);
void commd_put(int, char *);
int main(int arg, char *argv[])
{
int ser_sockfd,cli_sockfd;
struct sockaddr_in ser_addr,cli_addr;
int ser_len, cli_len;
char commd [N];
bzero(commd,N);//将commd所指向的字符串的前N个字节置为0,包括'\0'
if((ser_sockfd=socket(AF_INET, SOCK_STREAM, 0) ) < 0)
{
printf("Sokcet Error!\n");
return -1;
}
bzero(&ser_addr,sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);//本地ip地址
ser_addr.sin_port = htons (8989);//转换成网络字节
ser_len = sizeof(ser_addr);
//将ip地址与套接字绑定
if((bind(ser_sockfd, (struct sockaddr *)&ser_addr, ser_len)) < 0)
{
printf("Bind Error!\n");
return -1;
}
//服务器端监听
if(listen(ser_sockfd, 5) < 0)
{
printf("Linsten Error!\n");
return -1;
}
bzero(&cli_addr, sizeof(cli_addr));
ser_len = sizeof(cli_addr);
while(1)
{
printf("server>");
//服务器端接受来自客户端的连接,返回一个套接字,此套接字为新建的一个,并将客户端的地址等信息存入cli_addr中
//原来的套接字仍处于监听中
if((cli_sockfd=accept(ser_sockfd, (struct sockaddr *)&cli_addr, &cli_len)) < 0)
{
printf("Accept Error!\n");
exit(1);
}
//由套接字接收数据时,套接字把接收的数据放在套接字缓冲区,再由用户程序把它们复制到用户缓冲区,然后由read函数读取
//write函数同理
if(read(cli_sockfd, commd, N) < 0) //read函数从cli_sockfd中读取N个字节数据放入commd中
{
printf("Read Error!\n");
exit(1);
}
printf("recvd [ %s ]\n",commd);
if(strncmp(commd,"ls",2) == 0)
{
commd_ls(cli_sockfd);
}else if(strncmp(commd,"get", 3) == 0 )
{
commd_get(cli_sockfd, commd+4);
}else if(strncmp(commd, "put", 3) == 0)
{
commd_put(cli_sockfd, commd+4);
}
else if(strncmp(commd, "rename", 6) == 0)
{
commd_rename(cli_sockfd, commd);
}
else
{
printf("Error!Command Error!\n");
}
}
return 0;
}
/*
** 实现文件的重命名
*/
void commd_rename(int sockfd, char *commd)
{
char oldName[N], newName[N];
char response[N];
// 从commd中提取旧文件名和新文件名,命令格式是"rename oldName newName"
sscanf(commd+7, "%s %s", oldName, newName);
printf("Rename From: [ %s ] To: [ %s ]\n", oldName, newName);
// 试图重命名文件
if(rename(oldName, newName) == 0){
sprintf(response, "Rename Success: %s -> %s", oldName, newName);
} else {
sprintf(response, "Rename Failed: %s", strerror(errno));
}
if(write(sockfd, response, strlen(response) + 1) < 0) {
printf("Write Error! At commd_rename\n");
exit(1);
}
close(sockfd);
}
/*
**显示文件列表
*/
void commd_ls(int sockfd)
{
DIR * mydir =NULL;
struct dirent *myitem = NULL;
char commd[N] ;
bzero(commd, N);
//opendir为用来打开参数name 指定的目录, 并返回DIR*形态的目录流
//mydir中存有相关目录的信息
if((mydir=opendir(".")) == NULL)
{
printf("OpenDir Error!\n");
exit(1);
}
while((myitem = readdir(mydir)) != NULL)//用来读取目录,返回是dirent结构体指针
{
if(sprintf(commd, myitem->d_name, N) < 0)//把文件名写入commd指向的缓冲区
{
printf("Sprintf Error!\n");
exit(1);
}
if(write(sockfd, commd, N) < 0 )//将commd缓冲区的内容发送会client
{
printf("Write Error!\n");
exit(1);
}
}
closedir(mydir);//关闭目录流
close(sockfd);
return ;
}
/*
**实现文件的下载
*/
void commd_get(int sockfd, char *filename)
{
int fd, nbytes;
char buffer[N];
bzero(buffer, N);
printf("get filename : [ %s ]\n",filename);
if((fd=open(filename, O_RDONLY)) < 0)//以只读的方式打开client要下载的文件
{
printf("Open file Error!\n");
buffer[0]='N';
if(write(sockfd, buffer, N) <0)
{
printf("Write Error!At commd_get 1\n");
exit(1);
}
return ;
}
buffer[0] = 'Y'; //此处标示出文件读取成功
if(write(sockfd, buffer, N) <0)
{
printf("Write Error! At commd_get 2!\n");
close(fd);
exit(1);
}
while((nbytes=read(fd, buffer, N)) > 0)//将文件内容读到buffer中
{
if(write(sockfd, buffer, nbytes) < 0)//将buffer发送回client
{
printf("Write Error! At commd_get 3!\n");
close(fd);
exit(1);
}
}
close(fd);
close(sockfd);
return ;
}
/*
**实现文件的上传
*/
void commd_put(int sockfd, char *filename)
{
int fd, nbytes;
char buffer[N];
bzero(buffer, N);
printf("get filename : [ %s ]\n",filename);
if((fd=open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0)//以只写的方式打开文件,若文件存在则清空,若文件不存在则新建文件
{
printf("Open file Error!\n");
return ;
}
while((nbytes=read(sockfd, buffer, N)) > 0)//将client发送的文件写入buffer
{
if(write(fd, buffer, nbytes) < 0)//将buffer中的内容写到文件中
{
printf("Write Error! At commd_put 1!\n");
close(fd);
exit(1);
}
}
close(fd);
close(sockfd);
return ;
}
2client.c
// An highlighted block
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
#define USERNAME "user"
#define PASSWORD "pass"
#define N 256
void commd_help();
void commd_exit();
void commd_ls(struct sockaddr_in, char *);
void commd_get(struct sockaddr_in , char *);
void commd_put(struct sockaddr_in , char *);
int main(int argc, char *argv[])
{
char commd[N];
struct sockaddr_in addr;
int len;
bzero(&addr, sizeof(addr)); //将&addr中的前sizeof(addr)字节置为0,包括'\0'
addr.sin_family = AF_INET; //AF_INET代表TCP/IP协议
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //将点间隔地址转换为网络字节顺序
addr.sin_port = htons(8989); //转换为网络字节顺序
len = sizeof(addr);
printf("Please login to use the FTP service.\n");
if(!commd_login()) {
printf("Login failed, exiting...\n");
return 0; // 登录失败,退出程序
}
printf("Login successful, welcome to the FTP service!\n");
while(1)
{
printf("ftp>");
bzero(commd,N);
//fgets函数从stdin流中读取N-1个字符放入commd中
if(fgets(commd,N,stdin) == NULL)
{
printf("Fgets Error!\n");
return -1;
}
commd[strlen(commd)-1]='\0'; //fgets函数读取的最后一个字符为换行符,此处将其替换为'\0'
printf("Input Command Is [ %s ]\n",commd);
if(strncmp(commd,"help",4) == 0) //比较两个字符串前4个字节,若相等则返回0
{
commd_help();
}else if(strncmp(commd, "exit",4) == 0)
{
commd_exit();
exit(0); //结束进程
}else if(strncmp(commd, "ls" , 2) == 0)
{
commd_ls(addr, commd);
}else if(strncmp(commd, "get" , 3) == 0)
{
commd_get(addr, commd);
}else if(strncmp(commd, "put", 3) ==0 )
{
commd_put(addr, commd);
}
else if(strncmp(commd, "rename", 6) == 0)
{
commd_rename(addr, commd);
}
else
{
printf("Command Is Error!Please Try Again!\n");
}
}
return 0;
}
/*
**帮助信息
*/
void commd_help()
{
printf("\n=---------------------欢迎使用FTP--------------------------|\n");
printf("| |\n");
printf("| help:显示所有FTP服务器命令 |\n");
printf("| |\n");
printf("| exit:离开FTP服务器 |\n");
printf("| |\n");
printf("| ls : 显示FTP服务器的文件列表 |\n");
printf("| |\n");
printf("| get <file>:从FTP服务器下载文件 |\n");
printf("| |\n");
printf("| put <file>:上传文件到FTP服务器 |\n");
printf("| |\n");
printf("|-----------------------------------------------------------|\n");
return ;
}
int commd_login()
{
char username[N];
char password[N];
printf("Username: ");
if (fgets(username, N, stdin) == NULL) {
return -1;
}
username[strcspn(username, "\n")] = 0; // 删除换行符
printf("Password: ");
if (fgets(password, N, stdin) == NULL) {
return -1;
}
password[strcspn(password, "\n")] = 0; // 删除换行符
// 简单比较输入的用户名和密码,如果匹配则返回1,否则返回0
if (strcmp(username, USERNAME) == 0 && strcmp(password, PASSWORD) == 0) {
return 1; // 登录成功
}
return 0; // 登录失败
}
/*
**退出FTP服务器
*/
void commd_exit()
{
printf("Bye!\n");
}
/*
** 实现文件的重命名
*/
void commd_rename(struct sockaddr_in addr, char *commd)
{
int sockfd;
char response[N];
// 创建套接字
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Socket Error!\n");
exit(1);
}
// 客户端与服务端连接
if(connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
printf("Connect Error!\n");
exit(1);
}
// 向服务端发送重命名命令
if(write(sockfd, commd, N) < 0)
{
printf("Write Error! At commd_rename\n");
exit(1);
}
// 读取服务端的响应
if(read(sockfd, response, N) < 0)
{
printf("Read Error! At commd_rename\n");
exit(1);
}
printf("%s\n", response);
close(sockfd);
}
/*
**显示文件列表
*/
void commd_ls(struct sockaddr_in addr, char *commd)
{
int sockfd;
//创建套接字
if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Socket Error!\n");
exit(1);
}
if(connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
printf("Connect Error!\n");
exit(1);
}
//将commd指向的内容写入到sockfd所指的文件中,此处即指套接字
if(write(sockfd, commd, N) < 0)
{
printf("Write Error!\n");
exit(1);
}
while(read(sockfd, commd, N) > 0) //从sockfd中读取N字节内容放入commd中,
{ //返回值为读取的字节数
printf(" %s ",commd);
}
printf("\n");
close(sockfd);
return ;
}
/*
**实现文件的下载
*/
void commd_get(struct sockaddr_in addr, char *commd)
{
int fd;
int sockfd;
char buffer[N];
int nbytes;
//创建套接字,并进行错误检测
if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Socket Error!\n");
exit(1);
}
//connect函数用于实现客户端与服务端的连接,此处还进行了错误检测
if(connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
printf("Connect Error!\n");
exit(1);
}
//通过write函数向服务端发送数据
if(write(sockfd, commd, N) < 0)
{
printf("Write Error!At commd_get 1\n");
exit(1);
}
//利用read函数来接受服务器发来的数据
if(read(sockfd, buffer, N) < 0)
{
printf("Read Error!At commd_get 1\n");
exit(1);
}
//用于检测服务器端文件是否打开成功
if(buffer[0] =='N')
{
close(sockfd);
printf("Can't Open The File!\n");
return ;
}
//open函数创建一个文件,文件地址为(commd+4),该地址从命令行输入获取
if((fd=open(commd+4, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0)
{
printf("Open Error!\n");
exit(1);
}
//read函数从套接字中获取N字节数据放入buffer中,返回值为读取的字节数
while((nbytes=read(sockfd, buffer, N)) > 0)
{
//write函数将buffer中的内容读取出来写入fd所指向的文件,返回值为实际写入的字节数
if(write(fd, buffer, nbytes) < 0)
{
printf("Write Error!At commd_get 2");
}
}
close(fd);
close(sockfd);
return ;
}
/*
**实现文件的上传
*/
void commd_put(struct sockaddr_in addr, char *commd)
{
int fd;
int sockfd;
char buffer[N];
int nbytes;
//创建套接字
if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Socket Error!\n");
exit(1);
}
//客户端与服务端连接
if(connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
printf("Connect Error!\n");
exit(1);
}
//从commd中读取N字节数据,写入套接字中
if(write(sockfd, commd, N)<0)
{
printf("Wrtie Error!At commd_put 1\n");
exit(1);
}
//open函数从(commd+4)中,读取文件路径,以只读的方式打开
if((fd=open(commd+4, O_RDONLY)) < 0)
{
printf("Open Error!\n");
exit(1);
}
//从fd指向的文件中读取N个字节数据
while((nbytes=read(fd, buffer, N)) > 0)
{
//从buffer中读取nbytes字节数据,写入套接字中
if(write(sockfd, buffer, nbytes) < 0)
{
printf("Write Error!At commd_put 2");
}
}
close(fd);
close(sockfd);
return ;
}
1编译两个代码,先运行sever,后运行client。