C练习--网络服务器之文件同步

为了了解C语言的通信方式,做了一个简单的网络服务器,实现了文件的同步.

#include <sys/types.h>  
#include <sys/socket.h>  
#include <stdio.h>  
#include <unistd.h>  
#include <string.h>  
#include <arpa/inet.h>  
#include <pthread.h>  
#include <stdlib.h>  

#include <sys/stat.h> 
#include <dirent.h>


#define PORT 8802  //监听端口
#define SIZE 1024  //每次接收的最大长度

//错误代码
#define ERR_FILE_MAX 201  //流水号过大,服务器掉包了,以服务器为准重新发送
#define ERR_FILE_MIN 202  //已经写入的包又发过来了,丢掉
#define ERR_FILE_ERR 203  //写入文件出错

/*
	检查文件
	0不需要传输|非0需要传输
*/
int CheckFile(char* path, char* file, int* size1, int* time){
	printf("检查文件:%s,%s,%d,%d\n", path, file, *size1, *time);
	int p = opendir(path);
	printf("p : %d\n", p);
	if (p != NULL)
	{
		FILE * fp;
		fp = fopen(file, "r");
		printf("fp : %d\n", fp);
		if (NULL != fp)
		{
			int fd;
			struct stat buf;
			fd = fileno(fp);
			fstat(fd, &buf);
			int size = buf.st_size; //get file size (byte)
			if (size != *size1)
			{
				printf("大小不一致:%d<==>%d\n", *size1, size);
				return 1;
			}
			/*
			long modify_time = buf.st_mtime;
			if (modify_time != *time)
			{
				printf("时间不一致:%d<==>%d\n", *time, modify_time);
				return 1;
			}*/
			return 0;
		}
		else
		{
			printf("文件不存在:%s\n", file);
			return 1;
		}
	}
	else
	{
		printf("目录不存在:%s\n", path);
		mkdir(path,S_IRUSER|S_IWUSER|S_IXUSER);
		return 1;
	}
}
/*
	更新文件

*/
int WriteFile(char* Buf, FILE * outfile, int serial, int tmp){
	int Local = 1;
	int serial1 = ((*(Buf + Local ) & 0xff) << 16)
		| ((*(Buf + Local + 1) & 0xff) << 8)
		| ((*(Buf + Local + 2) & 0xff) << 0);
	Local = Local + 3;
	if (serial1<serial)//已经写入的包又发过来了,丢掉
	{
		printf("serial 大于:%d,%d\n", serial, serial1);
		return ERR_FILE_MIN;
	}
	else if (serial1>serial)//掉包了,以服务器为准,重新发送
	{
		printf("serial 小雨:%d,%d\n", serial, serial1);
		return ERR_FILE_MAX;
	}
	else//正常,写入文件
	{
		int fr = fwrite(Buf + Local, tmp - Local, 1, outfile);
		printf("开始写入文件:%d,%d\n", Local + 1, tmp - Local);
		if (fr == -1){
			perror("write error:");
			return ERR_FILE_ERR;
		}
		else
		{
			fflush(outfile);
			printf("写入文件:%d,buf长度:%d,tmp长度:%d\n", fr, sizeof(Buf)-Local, tmp);
			return 1;
		}
		
	}

}

/*
*	创建套接字和初始化以及监听函数
*	正常返回listen_socket(int),错误返回-1
*/
int CreatSocket()
{
	int listen_socket = socket(AF_INET, SOCK_STREAM, 0);      //创建一个负责监听的套接字    
	if (listen_socket == -1)
	{
		perror("socket");
		return -1;
	}
	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;  /* Internet地址族 */
	addr.sin_port = htons(PORT);  /* 端口号 */
	addr.sin_addr.s_addr = htonl(INADDR_ANY);   /* IP地址 */
	if (bind(listen_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1)//连接  
	{
		perror("bind");
		return -1;
	}
	if (listen(listen_socket, 10) == -1)//监听  
	{
		perror("listen");
		return -1;
	}
	return listen_socket;
}
/**
*	创建一个和客户端交流的套接字
*	正常返回client_socket,错误返回-1
*/
int WaitClient(int listen_socket)
{
	struct sockaddr_in cliaddr;
	int addrlen = sizeof(cliaddr);
	printf("等待客户端连接......\n");
	int client_socket = accept(listen_socket, (struct sockaddr *)&cliaddr, &addrlen);
	if (client_socket == -1)
	{
		perror("accept");
		return -1;
	}

	printf("成功接收到一个客户端:%s,socket:%d\n", inet_ntoa(cliaddr.sin_addr), client_socket);

	return client_socket;
}
/*
*		字符串拷贝
*/
int copy_string(char src[], char *string, int start, int end){
	/*if (sizeof(src)<end || sizeof(string)<end - start || start>end)
	{
	return -1;
	}
	else
	{*/
	char string1[30] = { '0', '0', '1' };
	int index = 0;
	int i;
	for (i = start; i < end; i++)
	{

		*(string + index) = (char)*(src + i) & 0xff;
		string1[index] = (char)*(src + i) & 0xff;
		char t = (char)*(src + i) & 0xff;
		/*
		*(string + index) = (char)src[i];
		string1[index] = (char)src[i];
		char t = (char)src[i];*/
		index = index + 1;
	}
	//}
	return 1;
}
/*
*		处理文件信息
*/

int Handle0(char info[], char *name, char* path, int * time, int* size){
	int Local = 1;
	int FileNameLen = *(info + Local) + *(info + Local + 1);
	printf("文件名字长度:%d\n", FileNameLen);
	//char* FileName = malloc(sizeof(char)*FileNameLen);
	char FileName[50] = { 'a', 'c', '4' };
	Local = Local + 2;
	copy_string(info, name, Local, Local + FileNameLen);
	printf("文件名字:%s,Local:%d\n", name, Local);
	Local = Local + FileNameLen;
	//处理路径
	int PathLen = *(info + Local) + *(info + Local + 1);
	printf("路径长度:%d\n", PathLen);
	Local = Local + 2;
	char Path[100] = { 'a', 'c', '4' };
	copy_string(info, path, Local, Local + PathLen);
	printf("路径:%s,Local:%d\n", path, Local);
	Local = Local + PathLen;
	//处理修改时间
	int Time = 0;
	Time = ((*(info + Local + 0) & 0xff) << 40)
		| ((*(info + Local + 1) & 0xff) << 32)
		| ((*(info + Local + 2) & 0xff) << 24)
		| ((*(info + Local + 3) & 0xff) << 16)
		| ((*(info + Local + 4) & 0xff) << 8)
		| ((*(info + Local + 5) & 0xff) << 0);
	Local = Local + 6;
	printf("时间:%d\n", Time);
	//处理文件大小
	int Size = 0;
	Size = ((*(info + Local + 0) & 0xff) << 40)
		| ((*(info + Local + 1) & 0xff) << 32)
		| ((*(info + Local + 2) & 0xff) << 24)
		| ((*(info + Local + 3) & 0xff) << 16)
		| ((*(info + Local + 4) & 0xff) << 8)
		| ((*(info + Local + 5) & 0xff) << 0);
	printf("大小:%d\n", Size);
	*time = Time;
	*size = Size;
	return 1;
}

void response(char* res, int val){
	if (val <= 255)
	{
		*(res) = 0x0;
		*(res + 1) = 0x0;
		*(res + 2) = val;
	}
	else if (val <= 65535)
	{
		*(res) = 0x0;
		*(res + 1) = val >> 8;
		*(res + 2) = val & 0xFF;
	}
	else
	{
		*(res) = val >> 16;
		*(res + 1) = val >> 8;
		*(res + 2) = val & 0xFF;
	}
}

/*
*	处理客户端信息
*/
void* Handle(void* client_socket1)
{
	printf("开始处理客户端信息,当前:%u,父进程:%u\n",getpid(),getppid());
	int client_socket = (int)client_socket1;
	char buf[SIZE];
	char FileName[100];//文件名字
	char FilePath[200];//文件路径
	memset(FilePath, 0x00, sizeof (char)* 200);
	memset(FileName, 0x00, sizeof (char)* 100);
	int FileSize = 0;
	int Time = 0;
	FILE * outfile = NULL;
	int flag = 0;
	int tmp = 0;
	char* info = &buf + 1;
	int serial = 1;//流水号
	//传输规则:8位标记(一个字符)+最大1016长度的内容
	//1.文件信息:
	//		接收:0(8位标记位)+16位文件名长度+文件名+16位路径长度+路径+48位的修改时间+48位的文件大小
	//		返回:0(更新文件)|1(略过文件)
	//2.文件内容:
	//		接收:1(8位标记位)+16位流水号+1000的文件大小
	//		返回:下一次流水号
	//3.传输完成:
	//		接收:2(8位标记位)
	//		返回:无

	while (tmp = recv(client_socket, buf, sizeof(buf), 0)){
		if (tmp == -1) break;
		char * point;
		switch (buf[0])
		{
		case 0x0://文件信息:
			memset(FilePath, 0x00, sizeof (char)* 200);
			memset(FileName, 0x00, sizeof (char)* 100);
			char pFile[200];
			printf("文件信息之前:%s,%s,%d,%d ,%s\n", FileName, FilePath, FileSize, Time, pFile);
			Handle0(buf, &FileName, &FilePath, &Time, &FileSize);
			printf("文件信息之后:%s,%s,%d,%d ,%s\n", FileName, FilePath, FileSize, Time, pFile);
			bzero(&pFile, 200);
			strcat(pFile, FilePath);
			strcat(pFile, FileName);
			int res = CheckFile(&FilePath, &pFile, &FileSize, &Time);
			if (res!=0)
			{
				outfile = fopen(pFile, "wb");
			}
			printf("返回:%d\n", res);
			char rep[] = { res };
			write(client_socket1, rep, 1);
			break;
		case 0x1://2,文件内容
			printf("文件内容:tmp=%d,serial=%d \n", tmp, serial);
			int res1 = WriteFile(buf, outfile, serial, tmp);
			printf("res1:%d \n", res1);
			bzero(&buf, sizeof(buf));
			printf("bzero.... \n");
			if (res1 == 1)
			{
				serial++;
				char rep[3] ;
				response(&rep, serial);
				printf("返回serial:%d\n", serial);
				write(client_socket1, rep, 3);
			}
			else
			{
				char rep[] = { res1 };
				printf("返回res1:%d\n", res1);
				write(client_socket1, rep, 1);
			}
			break;
		case 0x2://3,传输完成,关闭连接
			printf("传输完成,关闭连接...\n");
			bzero(&buf, sizeof(buf));
			fclose(outfile);
			goto the_end;
			break;
		default://不明标记
			//flag = 0;
			printf("不明标记: %s,flag=%d\n", buf[0], flag);
			bzero(&buf, sizeof(buf));
			break;
		}
	}
the_end:
	close(client_socket);
}

int main()
{
	int listen_socket = CreatSocket();

	while (1)
	{
		int client_socket = WaitClient(listen_socket);
		pthread_t id;
		pthread_create(&id, NULL, Handle, (void *)client_socket);  //创建一个线程,来处理客户端。  
		pthread_detach(id);   //把线程分离出去。  
	}

	close(listen_socket);

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值