一、项目简介
模仿小米的云服务,实现一个通过TCP实现的私人云盘,因为能力有限,所以只实现自动云同步这一个功能,具体可以分为三个小功能,即保持云端和终端数据一致、实现文件的上传与下载以及手动同步
二、涉及到的知识点
主要有文件的打开与关闭,文件的发送与接收,文件的写入及socket通信,为了项目整体的间接性,还使用到的枚举类型的结构体以及链表
三、TCP通信实现流程图
四、讲解
在项目实现过程中,代码程序修改了多次,我将最终的项目程序压缩包上传到了我的资源上面,有兴趣的可以自行下载。
这里放的程序是我在编写项目程序过程中的一个版本,只实现了单个文件的传输,需要自己手动输入要传输的文件,最终的版本在资源里面请不要搞错了
1、文件说明:
①client.c 是客户端的代码
②server.c 是服务器端的代码
③tcp.c 是客户端和服务器所使用到的一些头文件,以及自己封装的一些函数和自定义的宏
④Makefile 这个就不用多说了吧
2、程序文件
client.c文件
#include "tcp.h"
#define FILENAME "森林风声-呜呼呜呼-树木摇曳.mp3"
int main(int argc,char *argv[]){
int socketfd,filefd;
int ret;
char buf[BUFSIZ];
/*检查参数*/
Argment(argc,argv);
/*创建套接字并对其初始化*/
socketfd = SocketInit_Client(argv);
/*打开文件*/
filefd = open(FILENAME,O_RDONLY);
if(filefd == -1){
ErrExit("open");
}
/*发送文件名字*/
SocketDataHandle(socketfd,FILENAME,strlen(FILENAME),(DataHand_t)send);
SocketDataHandle(socketfd,buf,1,recv);
/*发送文件内容*/
if(buf[0] == OK){
while(1){
do{
ret = read(filefd,buf,BUFSIZ);
}while(ret < 0 && errno == EINTR);
if(ret < 0){
ErrExit("read");
}
if(!ret){
break;
}
ret = SocketDataHandle(socketfd,buf,ret,(DataHand_t)send);
if(!ret){
break;
}
}
}
close(filefd);
close(socketfd);
return 0;
}
server.c文件
#include "tcp.h"
int main(int argc,char *argv[]){
int socketfd,newsocketfd,filefd;
int ret;
char buf[BUFSIZ] = {};
Addr_in clientaddr;
socklen_t addrlen = sizeof(Addr_in);
/*检查参数*/
Argment(argc,argv);
/*创建套接字*/
socketfd = SocketInit_server(argv);
/*接收客户端的连接并生成一个新的套接字*/
do{
newsocketfd = accept(socketfd,(Addr *)&clientaddr,&addrlen);
}while(newsocketfd < 0 && errno == EINTR); //erron=EINTR如果信号导致的中断,重新执行一次
if(newsocketfd == -1){
ErrExit("accept");
}
/*接收文件名字*/
ret = SocketDataHandle(newsocketfd,buf,BUFSIZ,recv);
/*创建文件*/
filefd = open(buf,O_WRONLY|O_CREAT,0660);
if(filefd == -1){
ErrExit("open");
}
buf[0] = OK;
SocketDataHandle(newsocketfd,buf,1,(DataHand_t)send);
/*接收文件*/
while(1){
ret = SocketDataHandle(newsocketfd,buf,BUFSIZ,recv);
if(!ret){
break;
}
write(filefd,buf,ret);
}
close(filefd);
close(newsocketfd);
close(socketfd);
return 0;
}
tcp.h文件
#ifndef TCP_H
#define TCP_H
/*使用的头文件*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <math.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
/*自己定义的宏*/
#define ErrExit(msg) do{perror(msg); exit(EXIT_FAILURE);}while(0)
#define BACKLOG 5
#define OK '1'
typedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;
typedef ssize_t(* DataHand_t)(int ,void *,size_t,int);
/*函数声明*/
void Argment(int argc,char *argv[]);
int SocketInit_Client(char *argv[]);
int SocketInit_server(char *argv[]);
int SocketDataHandle(int fd,void *buf,size_t len,DataHand_t datahandle);
//参数检查函数
void Argment(int argc,char *argv[]){
if(argc < 3){
fprintf(stdin,"%s<addr><port>\n",argv[0]);
exit(EXIT_FAILURE);
}
}
//初始化客户端套接字函数
int SocketInit_Client(char *argv[]){
int socketfd;
Addr_in addr;
/*创建套接字*/
socketfd = socket(AF_INET,SOCK_STREAM,0);
if(socketfd == -1){
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(socketfd,(Addr *)&addr,sizeof(addr)) == -1){
ErrExit("connect");
}
return socketfd;
}
//初始化服务器端套接字函数
int SocketInit_server(char *argv[]){
int socketfd;
Addr_in addr;
/*创建套接字*/
socketfd = socket(AF_INET,SOCK_STREAM,0);
if(socketfd == -1){
ErrExit("socket");
}
/*设置地址快速重用*/
int flag = 1;
if(setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag)) == -1){
perror("setsockopt");
}
/*设置通信结构体*/
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(socketfd,(Addr *)&addr,sizeof(addr)) == -1){
ErrExit("bind");
}
/*设置套接字的模式为监听*/
if(listen(socketfd,BACKLOG) == -1){
ErrExit("listen");
}
return socketfd;
}
//数据处理函数
int SocketDataHandle(int fd,void *buf,size_t len,DataHand_t datahandle){
int ret;
char *str = datahandle == recv?"recv":"send";
do{
ret = datahandle(fd,buf,len,0);
}while(ret < 0 && errno == EINTR);
if(ret < 0){
ErrExit(str);
}
return ret;
}
#endif
Makefile文件
all:server client
CC=gcc
CFLAGS=-g -Wall
server:server.c
client:client.c
MV_client:
mv client /mnt/hgfs/Share/
clean:server client
rm server client