为了了解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;
}