类百度网盘功能实现

类ftp文件传输:

主要功能:文件的上传与下载、通过客户端修改服务器文件的相关命令。
项目描述:运用流式套接字建立客户端与服务器的正常连接,通过多线程编程来实现多个客户端与服务器交互, 客户端从键盘输入命令向服务器发送,服务器端通过分割字符的方式判断用户需要执行的命令,并执行;服务器端创建通过fork()创建一个子进程,用execvp()进程替换,将子进程替换成用户需要实现的功能;文件下载: 客户端向服务器申请需要下载的文件,服务器计算文件大小,定义一个数组保存文件数据,并且向客户端发送标识符和文件大小,服务器正常时客户端根据服务器发送的标识符接收文件大小,并且向服务器发送确认信息,客户端创建同名文件,接收文件数据;文件上传:客户端向服务器发送上传命令,服务器接收到正确命令时,客户端打开相应的文件,计算文件大小,定义一个数组保存需要上传的文件中的数据,向服务器发送标识符和文件大小,服务器建立新文件接收数据,服务器回复确认信息,客户端读取数组中保存的数据,同时向服务器发送文件的数据,服务器向新建文件中写入客户端发送过来的数据,信息传输完毕。

服务器:
#include"thread.h"
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<string.h>
#include<assert.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<errno.h>

#define LIS_MAX 5

int create_socket()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);//创建流式套接字
if(sockfd == -1)
{
return -1;
}
struct sockaddr_in saddr;//结构体指针,指向一个缓冲区
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr(“127.0.0.1”);//这五行代码都是套接字编程中必有的代码

int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//将套接字绑定一个地址上,定制一个端口号如上6000
if(res == -1)
{
return -1;
}
listen(sockfd,LIS_MAX);//监听客户端的连接请求

return sockfd;//连接成功返回socked
}

int main()
{
int sockfd = create_socket();
while(1)
{
struct sockaddr_in caddr;
int len = sizeof(caddr);
int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//从已完成连接队列返回下一个建立成功的连接,返回一个连接
printf(“accept:c = %d\n”,c);
if(c < 0)
{
perror(“accept error”);
return -1;
}
thread_start©;
}
}
多线程实现
#include"thread.h"

#define MAX_ARGS 10

void send_file(int c,char* name)//这个c就相当于套接字用于通信的
{
if(name == NULL)//如果文件名为空,退出
{
send(c,“err: No filename!”,17,0);//客户端向服务器申请的文件没有,服务器就给客户端发送该文件名
return;
}

int fd = open(name,O_RDONLY);//打开文件,fd拿到句柄
if (fd == -1)
{
send(c,“err: not found file!”,20,0);//如果这个文件里面没有东西,就向客户端发送没有找到这个文件
return;
}

int size = lseek(fd,0,SEEK_END);//获取文件长度,SEEK_END是将读写位置指向文件尾部后再增加0个偏移量,指的就是文件的尾部
lseek(fd,0,SEEK_SET);//SEEK_SET是将读写位置只想文件头部后增加0个偏移量,就是文件的头部,

char res_buff[64] = {0};//此时要判断服务器状态
sprintf(res_buff,“ok#%d”,size);//如果服务器打赢出来刚好是"ok#"的长度也就是4;就表示服务器与客户端连接正常

send(c,res_buff,strlen(res_buff),0);//服务器发给服务器这个文件的文件长度和文件名

char cli_status[32] = {0};
if(recv(c,cli_status,31,0) <= 0)//接收客户端状态,<=0表示客户端此时不能接收文件
{
return;
}

if(strcmp(cli_status,“ok”) != 0)//如果客户端回复内容和"ok"不一样,表示客户端也不能接收文件
{
return;
}

char data[512] = {0};//定义的这个数组用来保存客户端向服务器申请的下载的文件的数据
int num = 0;

while( (num = read(fd,data,512)) > 0)//文件长度必须大于0
{
send(c,data,num,0);//发送文件
}

close(fd);//释放
printf(“finish\n”);//打印完成
}

void recv_file(int sockfd,char* name)
{
send(sockfd,“ok”,2,0);//客户端发送过来文件,向客户端回复ok表示可以发送
char ser_status[32] = {0};

if(recv(sockfd,ser_status,31,0) <= 0)//接收的文件<=0
{
printf(“recv error\n”);
return;
}
if(strncmp(ser_status,“ok#”,3)!=0)//对比发过来的前三位是不是ok#,
{
printf("%s\n",ser_status);
return;
}
int size = 0;
sscanf(ser_status+3,"%d",&size);//字符串输入源,输入文件的大小,加3是因为有个ok#
printf(“File:%s Size:%d\n”,name,size);//打印发过来的文件名,大小

int fd = open(name,O_WRONLY|O_CREAT,0600);//创建与一个新的同名文件
assert(fd != -1);

send(sockfd,“ok”,2,0);//向客户端发送ok表示文件名和大小已经接收

char data[512] = {0};
int curr_size = 0;
int num = 0;

while((num = recv(sockfd,data,512,0)) > 0)//每次接收的文件数据必须大于0
{
write(fd,data,num);//打开创建的同名文件,写入发送过来的数据
curr_size+=num;//累加直到文件发送完成,

if(curr_size >= size)//如果接收的文件数据大于或者等于计算的文件大小就直接break
{
break;
}

}
printf("\n");
close(fd);//释放
}

void* work_thread(void* arg)
{
int c = (int)arg;//客户端于服务器命令交互的"ok#"大小
while(1)
{

char buff[128] = {0};
int num = recv(c,buff,127,0);//接收客户端发送过来可以连接的命令长度"ok#"
if(num <= 0)
{
break;
}
printf(“Client(%d):%s\n”,c,buff);//打印

char* myargv[MAX_ARGS] ={0};
char* ptr = NULL;
int i = 0;
char* s = strtok_r(buff," “,&ptr);//strtok_r是个字符串分割函数,指针ptr为空,指向的为0,以空格分隔发送过来的命令
while(s != NULL)//分割的字符串不能为空
{
myargv[i++] = s;//用这个数组保存这个客户端发送过来的命令
s = strtok_r(NULL,” ",&ptr);//这个第一个参数为空,表示函数保存的指针在下一次调用中作为起始位置
}

if(myargv[0] == NULL)//如果发送过来的命令开头都为空的话,就给客户端发送没有这个命令
{
send(c,“ok#No cmd!”,10,0);
continue;
}

if(strcmp(myargv[0],“get”) == 0)//文件下载,对比命令
{
send_file(c, myargv[1]);
}
else if(strcmp(myargv[0],“put”)== 0)//文件上传,对比文件
{
recv_file(c, myargv[1]);
}
else
{
int pipefd[2];
pipe(pipefd);//创建管道,管道的作用就是把一个程序的输入直接连接到另外一个程序的输出
//pipe函数用于父子进程间通信,fd[0]读,fd[1]写
pid_t pid = fork();//fork()一个子进程,得到子进程的pid
assert(pid != -1);

if(pid == 0)//进程号为0的话意思就是还是父进程
{
dup2(pipefd[1],1);//复制进程描述符,进程描述符就是把一个进程的所有信息写在一个交task_struct下的结构,
//dup2函数分为0,1,2三种,分别为标准输入,标准输出,标准错误,这行代码的意思就是,把父进程的信息写入子进程中标准输出
dup2(pipefd[1],2);//输出错误,连续调用两个dup2,防止修改进程当前状态,输出错误了,就不会改变当前进程的状态了

execvp(myargv[0],myargv);//进程替换,用子进程替换父进程,用execvp替换会带有当前文件的路径,并且进程的进程号(pid)不会发生改变,
//所以进程的pid一直都为0,虽然进程替换了,但是子进程包含父进程所有的信息,以为进程号一直都为0,所以每次都会有进程新的子进程替换原有的子进程。
//这样就变成了多线程(thread),所以同时很多个客户端向服务器申请执行不同命令时,服务器都会执行,
perror(“execlp error”);//如果进程替换发生错误,就输出到屏幕上
exit(0);//如果发生错误就退出
}
close(pipefd[1]);//关闭管道的写端
wait(NULL);

char readbuff[1024] = {“ok#”};
read(pipefd[0],readbuff+3,1020);//读取管道里面的命令,用readbuff保存,加3的原因是因为本来readbuff中本来就有"ok#"一个字符串只有一个’/0’所以不加4,
send(c,readbuff+3,strlen(readbuff),0);//向客户端发送和这个数组
}
}
printf(“one client over\n”);
}

void thread_start(int c)
{
pthread_t id;//线程的id等同于上面的进程
pthread_create(&id,NULL,work_thread,(void*)c);//创建一个线程
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<fcntl.h>

void recv_file(int sockfd,char* name)
{
char ser_status[32] ={0};
if(recv(sockfd,ser_status,31,0) <= 0)
{
printf(“recv error\n”);
return;
}
if(strncmp(ser_status,“ok#”,3)!=0)
{
printf("%s\n",ser_status);
return;
}
int size = 0;
sscanf(ser_status+3,"%d",&size);
printf(“File:%s Size:%d\n”,name,size);

int fd = open(name,O_WRONLY|O_CREAT,0600);
assert(fd != -1);

send(sockfd,“ok”,2,0);

char data[512] = {0};
int curr_size = 0;
int num = 0;

while((num = recv(sockfd,data,512,0)) > 0)
{
write(fd,data,num);
curr_size+=num;

float f =curr_size/size*100.0;
printf(“downloading %.2f%%\r”,f);
fflush(stdout);

if(curr_size >= size)
{
break;
}

}
printf("\n");
close(fd);
}

void send_file(int sockfd,char* name)
{
char flag[32] ={0};
int n;
if((n = recv(sockfd,flag,31,0)) <= 0)
{
return;
}
if(name == NULL) //如果文件名为空,退出
{
send(sockfd,“err: No filename!”,17,0);
return;
}

char* flags ={0};

int fd = open(name,O_RDONLY); //打开文件,fd拿到句柄
if (fd == -1)
{
printf(“Not found file!\n”);
return;
}

int size = lseek(fd,0,SEEK_END);//获取文件长度
lseek(fd,0,SEEK_SET);

char res_buff[64] = {0}; //客户端状态
sprintf(res_buff,“ok#%d”,size);

send(sockfd,res_buff,strlen(res_buff),0);

char cli_status[32] = {0};
if(recv(sockfd,cli_status,31,0) <= 0)//接收服务器状态
{
return;
}

if(strcmp(cli_status,“ok”) != 0)
{
return;
}

char data[512] = {0};
int num = 0;
int curr_size = 0;

while( (num = read(fd,data,512)) > 0)
{
send(sockfd,data,num,0);
curr_size+=num;
float f =curr_size/size*100.0;
printf(“Uploading %.2f%%\r”,f);
fflush(stdout);
}

close(fd);
printf(“finish\n”);
}

int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
assert( sockfd != -1 );

struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//发起连接,进行三次握手
assert( res != -1 );

while( 1 )
{
    char buff[128] = {0};
    printf("Connect ~]$");

fflush(stdout);

    fgets(buff,128,stdin);

buff[strlen(buff)-1] = 0;

    if ( strncmp(buff,"end",3) == 0 )
    {
        break;    
    }

char tmp[128] = {0};
strcpy(tmp,buff);

char* argv[10] = {0};
char* s = strtok(tmp," “);
int i = 0;
while(s != NULL)
{
argv[i++] = s;
s = strtok(NULL,” ");
}

if(argv[0] == NULL)
{
continue;
}
if(strcmp(argv[0],“end”) == 0)
{
break;
}
if(strcmp(argv[0],“get”) == 0)
{
if(argv[1] == NULL)
{
printf(“No file name!\n”);
continue;
}
send(sockfd,buff,strlen(buff),0);
recv_file(sockfd,argv[1]);
}
else if( strcmp(argv[0],“put”)==0)
{
if(argv[1] == NULL)
{
printf(“No file name!\n”);
continue;
}
send(sockfd,buff,strlen(buff),0);
send_file(sockfd,argv[1]);
}
else
{
send(sockfd,buff,strlen(buff),0);
char readbuff[1024] = {0};
if(recv(sockfd,readbuff,1023,0)<= 0)
{
printf(“Ser error\n”);
break;
}
printf("%s\n",readbuff+3);
}
}
close(sockfd);
}

猜你喜欢

转载自blog.csdn.net/weixin_43700671/article/details/88948665
今日推荐