FTP协议:简单来讲就是文本传输协议
实现功能:
1.能够进入指定文件路径 -cd
2.能够查看服务器文件目录 -ls
3.能够上传文件至服务器 -put
4.能够从服务器下载文件 -get
5.能够查看之前对文件的操作 -hist
6.账号密码 验证
7. - quit退出
核心思想:
我们要完成上述功能,大致就分为几步。
1.客户端发送指令给服务端
2.服务端接受指令完成指定操作
3.服务端把完成的结果传回客户端
具体实现:
要完成客户端、服务端之间的通信,我们肯定要先构建几个结构体来存放我们想传输的内容
1.建立一个能够存储功能的枚举型结构体 (ls cd get put hist quit)
2.创建一个能够存放文件内容、传输指令、md5效验码等的结构体
3.构造一个分割传输指令的函数 ,为后续操作铺垫
4.构造一个计算md5效验码的函数
5.构建一个结构体存放账号密码
然后 来讲下指令操作的具体实现
ls:
1.客户端发送ls指令,服务端接收
2.服务端执行ls指令,获得文件目录,传回客户端
get:
1.客户端发送get指令,服务端接收
2.服务端接收get指令,分割出get后面的文件名,打开文件,读取内容,计算md5值,发送给客户端
3.客户端接收信息,创建新文件,,效验md5值,存放服务端传来的内容
put:
1.客户端分割出put后面的文件名,打开文件,读取内容,计算md5值,发送给服务端端
2.服务端接收put指令,效验md5,存放客户端传来的内容
hist:
1.用头插法创建链表来存储对文件的操作指令
2.遍历链表,用strcat() 来拼接操作指令
3.客户端发送指令,服务端接收
4.服务端添加链表内容,再发回客户端
quit:
按quit退出
难点总结
主要难点是大致实现的思想,只要知道了是怎么大致实现的,接下来都是些小问题
1.我们可以在出错的时候在自己觉得错的地方打印出可能错的内容来调试
2.熟练使用gdb调试,gdb用的好,这些都不是事儿
3.在分割出指令后面的文件名,用字符数组去存放,可能会出错,具体为什么我还没找出来
那么 我就用字符指针存放,这个bug难搞噢
接下来就是代码附上啦。
msg.h
#ifndef MSG_H
#define MSG_H
#define PORT 8883
enum FTP_CMD{ //存放指令
FTP_CMD_LS=0,
FTP_CMD_GET=1,
FTP_CMD_PUT=2,
FTP_CMD_QUIT=3,
FTP_CMD_CD=4,
FTP_CMD_AUTH=5,
FTP_CMD_HIST=6,
FTP_CMD_ERROR
};
struct Auth //存放 账号 密码
{
enum FTP_CMD cmd;
char username[32];
char password[32];
};
struct Msg
{
//command get
enum FTP_CMD cmd; //存放指令
//command de canshu get test.c
char args[32]; //存放对文件的操作
//md5
char md5[64]; //存放md5效验码
//data length
int data_length; //存放文件长度
//test.c content i don't know length
char data[5000]; //存放文件内容
};
#endif
link.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"link.h"
void linkinit(struct LINKLIST **head,char *cmd) //链表初始化
{
struct LINKLIST *node=(struct LINKLIST *)malloc(sizeof(struct LINKLIST));
node->next=NULL;
strcpy(node->cmd,cmd);
node->next=*head; //头插法
*head=node;
}
void print_link(struct LINKLIST *head) //打印链表信息
{
struct LINKLIST *p=head;
while(p!=NULL)
{
printf("%s",p->cmd);
p=p->next;
}
}
void get_link(struct LINKLIST *head,char *cmd) //从链表中获取操作指令
{
struct LINKLIST *p=head;
while(p!=NULL)
{
// p=p->next;
strcat(cmd,p->cmd); //拼接链表内容
p=p->next;
}
}
unils.c
#include<string.h>
#include<sys/types.h>
#include<stdio.h>
int division_buf(char *inbuf,char *outbuf)
{
char *first=strstr(inbuf," ");
while(1)
{
if(first=="\0"||first==NULL)
{
return -1;
}
first+=1;
if(*first!=' ')
{
break;
}
}
strncpy(outbuf,first,strlen(first)-1);
return 0;
}
long get_length(char *filename)
{
long length;
FILE *fp=fopen(filename,"r");
if(fp!=NULL)
{
fseek(fp,0,2);
length=ftell(fp);
rewind(fp);
}
else
{
return -1;
}
fclose(fp);
return length;
}
void get_md5(char *filename,char *md5)
{
char buf[64];
char cmd[64];
sprintf(buf,"md5sum %s",filename);
FILE *fp=popen(buf,"r");
if(fp!=NULL)
{
fread(cmd,1,sizeof(buf),fp);
}
strncpy(md5,cmd,32);
fclose(fp);
}
server.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include "msg.h"
#include "log.h"
#include "unils.h"
#include <signal.h>
#include "link.h"
int ssc;
void handle_cmd(struct Msg *incmd, struct Msg *outcmd) {
static struct LINKLIST *head=NULL;
linkinit(&head,incmd->args);
// print_link(head);
if (incmd->cmd == FTP_CMD_LS) {
FILE *fp = popen(incmd->args, "r");
if (fp != NULL) {
int net = fread(outcmd->data, 1, sizeof(outcmd->data), fp);
}
pclose(fp);
} else if (incmd->cmd == FTP_CMD_HIST) {
char buf[1024]={0};
get_link(head,buf);
printf("buf=%s",buf);
strcpy(outcmd->args,buf);
printf("outcmd->args=%s\n",outcmd->args);
} else if (incmd->cmd == FTP_CMD_GET) {
char filename[32];
filename[0] = '_';
if (division_buf(incmd->args, &filename[1]) == -1) {
outcmd->cmd = FTP_CMD_ERROR;
log_write("don't find file\n");
return;
}
strcpy(outcmd->args, filename);
char *p = &filename[1];
outcmd->data_length = get_length(p);
if (outcmd->data_length > 5000 || outcmd->data_length == -1) {
return;
}
// get_md5(p,outcmd->md5);
// log_write("outcmd->md5=%s\n",outcmd->md5);
FILE *fd = fopen(p, "r");
if (fd != NULL) {
fread(outcmd->data, 1, outcmd->data_length, fd);
} else {
outcmd->cmd = FTP_CMD_ERROR;
log_write("don't find file\n");
return;
}
fclose(fd);
get_md5(p,outcmd->md5);
log_write("outcmd->md5=%s\n",outcmd->md5);
} else if (incmd->cmd == FTP_CMD_PUT) {
char filename[32];
char md5[64];
filename[0] = '+';
if (division_buf(incmd->args, &filename[1]) == -1) {
log_write("don't find file\n");
return;
}
FILE *fd = fopen(filename, "w");
fwrite(incmd->data, 1, incmd->data_length, fd);
fclose(fd);
get_md5(filename, md5);
log_write("incmd->md5=%s\n",incmd->md5);
printf("%s\n",md5);
printf("%s\n",incmd->md5);
if(strcmp(incmd->md5,md5)!=0)
{
remove(filename);
}
} else if (incmd->cmd == FTP_CMD_QUIT) {
ssc = 0;
} else if (incmd->cmd == FTP_CMD_CD) {
char *name=malloc(sizeof(char));
if (division_buf(incmd->args, name) == -1) {
return;
}
printf("incmd->args=%s",incmd->args);
printf("cdname=%s\n",name);
chdir(name);
}
}
int main(int argc, char const *argv[]) {
int s_fd;
int c_fd;
int ret;
int addr_len;
struct sockaddr_in s_addr;
struct Msg *msg_send = malloc(sizeof(struct Msg));
struct Msg *msg_recv = malloc(sizeof(struct Msg));
log_creat("zzz.txt");
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if (s_fd == -1) {
log_write("socket error\n");
exit(-1);
}
memset(&s_addr, 0, sizeof(struct sockaddr_in));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(PORT);
s_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr)) == -1) {
log_write("bind error\n");
exit(-1);
}
if (listen(s_fd, 10) == -1) {
log_write("listen error\n");
}
addr_len = sizeof(struct sockaddr);
c_fd = accept(s_fd, (struct sockaddr *)&s_addr, &addr_len);
if (c_fd < 0) {
log_write("accept error\n");
exit(-1);
}
struct Auth in_user;
struct Auth out_user;
char user_name[32];
char pass_word[32];
recv(c_fd, &in_user, sizeof(struct Auth), 0);
FILE *fp = fopen("user.txt", "r");
fscanf(fp, "%s %s", user_name, pass_word);
log_write("username=%s,password=%s\n", user_name, pass_word);
log_write("inname=%s,inword=%s\n", in_user.username, in_user.password);
int a = strcmp(user_name, in_user.username);
log_write("length=%d\n", a);
if (strcmp(user_name, in_user.username) != 0 ||
strcmp(pass_word, in_user.password) != 0) {
log_write("hello\n");
out_user.cmd = FTP_CMD_ERROR;
send(c_fd, &out_user, sizeof(struct Auth), 0);
exit(-1);
}
out_user.cmd = FTP_CMD_AUTH;
send(c_fd, &out_user, sizeof(struct Auth), 0);
fclose(fp);
ssc = 1;
while (ssc) {
memset(msg_recv, 0, sizeof(struct Msg));
memset(msg_send, 0, sizeof(struct Msg));
ret = recv(c_fd, msg_recv, sizeof(struct Msg), 0);
log_write("recv from server=%d\n", ret);
handle_cmd(msg_recv, msg_send);
ret = send(c_fd, msg_send, sizeof(struct Msg), 0);
}
log_distory();
return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <netdb.h>
#include "log.h"
#include "msg.h"
#include "unils.h"
#include "link.h"
int input_msg(struct Msg *msg) {
char buf[32];
enum FTP_CMD cmd;
printf("input command\n");
fgets(buf, 32, stdin);
log_write("buf=%s", buf);
if (memcmp(buf, "ls", 2) == 0) {
cmd = FTP_CMD_LS;
} else if (memcmp(buf, "get", 3) == 0) {
cmd = FTP_CMD_GET;
} else if (memcmp(buf, "put", 3) == 0) {
cmd = FTP_CMD_PUT;
if (division_buf(buf, msg->args) == -1) {
return -1;
}
msg->data_length = get_length(msg->args);
if (msg->data_length > 5000 || msg->data_length == -1) {
return -1;
}
get_md5(msg->args, msg->md5);
// log_write("msg->md5=%s\n", msg->md5);
FILE *fp = fopen(msg->args, "r");
if (fp != NULL) {
fread(msg->data, 1, msg->data_length, fp);
} else {
return -1;
}
fclose(fp);
} else if (memcmp(buf, "quit", 4) == 0) {
cmd = FTP_CMD_QUIT;
} else if (memcmp(buf, "cd", 2) == 0) {
cmd = FTP_CMD_CD;
} else if (memcmp(buf, "hist", 4) == 0) {
cmd = FTP_CMD_HIST;
} else {
cmd = FTP_CMD_ERROR;
}
if (cmd == FTP_CMD_ERROR) {
return -1;
}
msg->cmd = cmd;
strcpy(msg->args, buf);
return 0;
}
int main(int argc, char const *argv[]) {
int c_fd;
int ret;
char buf[128];
struct sockaddr_in c_addr;
struct Msg *msg_send = malloc(sizeof(struct Msg));
struct Msg *msg_recv = malloc(sizeof(struct Msg));
log_creat("client.txt");
c_fd = socket(AF_INET, SOCK_STREAM, 0);
if (c_fd == -1) {
log_write("client socket error\n");
exit(-1);
}
memset(&c_addr, 0, sizeof(struct sockaddr_in));
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(PORT);
inet_aton("192.168.58.131", &c_addr.sin_addr);
if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr)) ==
-1) {
log_write("connect failed\n");
exit(-1);
}
log_write("connect succes from client\n");
struct Auth auth_send;
struct Auth auth_recv;
printf("input username:\n");
scanf("%s", auth_send.username);
printf("input password:\n");
scanf("%s", auth_send.password);
send(c_fd, &auth_send, sizeof(struct Auth), 0);
recv(c_fd, &auth_recv, sizeof(struct Auth), 0);
if (auth_recv.cmd == FTP_CMD_ERROR) {
return;
}
while (1) {
memset(msg_recv, 0, sizeof(struct Msg));
memset(msg_send, 0, sizeof(struct Msg));
if (input_msg(msg_send) == -1) {
continue;
}
ret = send(c_fd, msg_send, sizeof(struct Msg), 0);
log_write("send=%d\n", ret);
ret = recv(c_fd, msg_recv, sizeof(struct Msg), 0);
if (msg_send->cmd == FTP_CMD_LS) {
printf("%s\n", msg_recv->data);
} else if (msg_send->cmd == FTP_CMD_GET) {
// char md5_buf[64];
if (msg_recv->cmd == FTP_CMD_ERROR) {
continue;
}
char md5_buf[128];
FILE *fd = fopen(msg_recv->args, "w");
fwrite(msg_recv->data, 1, msg_recv->data_length, fd);
printf("%d\n", msg_recv->data_length);
fclose(fd);
get_md5(msg_recv->args,md5_buf);
log_write("msg_recv->args=%s\n",md5_buf);
log_write("msg_recv->md5=%s\n",msg_recv->md5);
if(strcmp(msg_recv->md5,md5_buf)!=0)
{
remove(msg_recv->args);
}
} else if (msg_send->cmd == FTP_CMD_QUIT) {
break;
} else if (msg_send->cmd == FTP_CMD_HIST) {
printf("%s\n", msg_recv->args);
}
}
log_distory();
return 0;
}
本文深入解析了FTP协议的工作原理及其实现细节,包括客户端与服务端的交互过程,如账号密码验证、文件上传下载、目录操作等功能。通过具体代码示例,详细介绍了如何构建FTP服务,涵盖了核心思想、难点分析及调试技巧。

被折叠的 条评论
为什么被折叠?



