目录
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);
}
//绑定通