目录
1云盘项目简介
2 项目实现
2.1 首先实现TCP客户端、服务端
2.2 实现客户端函数简化
2.3 实现服务端函数简化
2.4 TCP数据连包现象
2.5 封装send函数和recv函数
2.6 建立readme说明
2.7 实现文件传输
2.8 读取配置文件种的ip、端口号,通过argv[1]参数实现文件传输
3 最终项目
1云盘项目简介
常见的云盘服务软件
什么是云同步?
- 保持云端数据和终端数据的一致
- 上传和下载
- 实时同步
- 定时同步
- 手动同步
需求分析
- 文件的上传和下载
- 文件的大小不确定(文件太大,upd传输就不合适)
- 文件的个数不确定
- 实时同步需要获取文件事件(因为用循环太占cpu)
- 定时同步需要设置定时器
- 使用TCP实现手动同步
如何实现手动同步?
- 实现TCP通信
- 使用TCP实现文件的上传和下载
- 实现整个目录下的文件的同步
- 实现项目框架
- 完成项目
2 项目实现
2.1 首先实现TCP客户端、服务端
cloud-project_v1
为了方便管理头文件,新建tcp.h
#ifndef _TCP_H
#define _TPC_H#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>#define ErrExit(msg) do{ perror(msg); \ //异常处理宏定义exit(EXIT_FAILURE);\}while(0);
typedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;
#define BACKLOG 5#endif
实现server.c
#include "tcp.h"int main(int argc, char *argv[])
{int ret, fd, newfd;Addr_in addr,client_addr;socklen_t addrlen = sizeof(client_addr);char buf[BUFSIZ] = {};//检查参数if(argc < 3){fprintf(stderr, "%s <addr> <port>\n", argv[0]);exit(EXIT_FAILURE);}//创建套接字if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)ErrExit("socket");//设置通信结构体bzero(&addr, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(atoi(argv[2]));if(inet_aton(argv[1], &addr.sin_addr) == 0){fprintf(stderr, "Invalid address\n");exit(EXIT_FAILURE);}//绑定通信结构体if(bind(fd,(Addr *)&addr, sizeof(addr)))ErrExit("bind");//监听模式if(listen(fd, BACKLOG))ErrExit("listen");//接收客户端链接bzero(&client_addr,sizeof(client_addr));do{newfd = accept(fd, (Addr *)&client_addr, &addrlen);}while(newfd < 0 && errno == EINTR);if(newfd < 0)ErrExit("accept");//接收客户端数据while(1){do {ret = recv(newfd, buf, BUFSIZ, 0);}while(ret < 0 && errno == EINTR); //如果信号导致的错误,继续执行if(ret < 0){ErrExit("recv");}else if(!ret) //ret = 0 客户端退出{break;}else //ret > 0{printf("[%s:%d]buf:%s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buf);}}close(newfd);close(fd);return 0;
}
实现服务端client.c(复制服务端进行改动)
#include "tcp.h"int main(int argc, char *argv[])
{int ret, fd;Addr_in addr;char buf[BUFSIZ] = {"===test"};//检查参数if(argc < 3){fprintf(stderr, "%s <addr> <port>\n", argv[0]);exit(EXIT_FAILURE);}//创建套接字if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)ErrExit("socket");//设置通信结构体bzero(&addr, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(atoi(argv[2]));if(inet_aton(argv[1], &addr.sin_addr) == 0){fprintf(stderr, "Invalid address\n");exit(EXIT_FAILURE);}//发起连接请求if(connect(fd,(Addr *)&addr, sizeof(addr)))ErrExit("bind");//发送数据while(1){do {ret = send(fd, buf, BUFSIZ, 0);}while(ret < 0 && errno == EINTR); //如果信号导致的错误,继续执行if(ret < 0){ErrExit("recv");}else if(!ret) //ret = 0{break;}printf("send data:%s\n", buf);fflush(stdout);getchar();}close(fd);return 0;
}
最终实现效果,客户端发送,服务端打印===test
2.2 实现客户端函数简化
cloud_project_v1.1客户端给服务端传文件
主要工作:
- 封装客户端socket创建过程的函数,简化代码
- 创建makefile
tcp.h
#ifndef _TCP_H_
#define _TCP_H_#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <arpa/inet.h>
#include <errno.h>#define BACKLOG 5#define ErrExit(msg) do { perror(msg); \exit(EXIT_FAILURE); } while(0)typedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;void Argment(int argc, char *argv[]);
int SocketInit(char *argv[]);
#endif
tcp.c
#include "tcp.h"void Argment(int argc, char *argv[]){if(argc < 3){fprintf(stderr, "%s <addr><port>\n", argv[0]);exit(EXIT_FAILURE);}
}int SocketInit(char *argv[]){int fd;Addr_in addr;/*创建套接字*/if( (fd = socket(AF_INET, SOCK_STREAM, 0) ) < 0)ErrExit("socket");/*设置通信结构体*/bzero(&addr, sizeof(addr) );addr.sin_family = AF_INET;addr.sin_port = htons( atoi(argv[2]) );if (inet_aton(argv[1], &addr.sin_addr) == 0) {fprintf(stderr, "Invalid address\n");exit(EXIT_FAILURE);}/*发起连接请求*/if( connect(fd, (Addr *)&addr, sizeof(addr) ) )ErrExit("connect");return fd;
}
client.c
#include "tcp.h"int main(int argc, char *argv[])
{int fd;int ret;char buf[BUFSIZ] = {"===test==="};/*检查参数*/Argment(argc, argv);fd = SocketInit(argv);/*发送数据*/while(1){do {ret = send(fd, buf, BUFSIZ, 0);}while(ret < 0 && errno == EINTR); //如果信号导致的错误,继续执行if(ret < 0)ErrExit("recv");else if(!ret)break;printf("send data:%s", buf);fflush(stdout);getchar();}close(fd);return 0;
}
server.c
#include "tcp.h"int main(int argc, char *argv[])
{int ret, fd, newfd;Addr_in addr,client_addr;socklen_t addrlen = sizeof(client_addr);char buf[BUFSIZ] = {};//检查参数if(argc < 3){fprintf(stderr, "%s <addr> <port>\n", argv[0]);exit(EXIT_FAILURE);}//创建套接字if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)ErrExit("socket");//设置通信结构体bzero(&addr, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(atoi(argv[2]));if(inet_aton(argv[1], &addr.sin_addr) == 0){fprintf(stderr, "Invalid address\n");exit(EXIT_FAILURE);}//绑定通