(1)项目名称:基于C/S架构模型——微云盘
(2)项目环境:Linux,vim,gcc
(3)项目描述:
my.conf
#ip地址
ipstr=127.0.0.1
#端口号
port=6000
#监听队列
lismax=5
client.c
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #include<unistd.h>
5 #include<sys/socket.h>
6 #include<arpa/inet.h>
7 #include<netinet/in.h>
8
9 int main()
10 {
11 int sockfd=socket(AF_INET,SOCK_STREAM,0);
12 if(sockfd==-1)
13 {
14 exit(0);
15 }
16
17 struct sockaddr_in saddr;
18 memset(&saddr,0,sizeof(saddr));
19 saddr.sin_family=AF_INET;
20 saddr.sin_port=htons(6000);
21 saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
22
23 int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
24 if(res==-1)
25 {
26 close(sockfd);
27 exit(0);
28 }
29 while(1)
30 {
31 printf("connect>>");
32 fflush(stdout);
33
34
35 char buff[128]={0};
36 fgets(buff,128,stdin);
37 if(strncmp(buff,"end",3)==0)
38 {
39 break;
40 }
41 send(sockfd,buff,strlen(buff),0);
42 memset(buff,0,128);
43 recv(sockfd,buff,127,0);
44 printf("buff=%s\n",buff);
45 }
46 }
makefile(注意:gcc前面不是空格,是tab键,否则会报错)
1 all:ser
2
3 ser:ser.o socket.o work_thread.o
4 gcc -o ser ser.o socket.o work_thread.o -lpthread
5
6 ser.o:ser.c
7 gcc -c ser.c
8
9 socket.o:socket.c
10 gcc -c socket.c
11
12 work_thread.o:work_thread.c
13 gcc -c work_thread.c
14 clean:
15 rm -f *.o ser
ser.c
1 #include"socket.h"
2 #include"work_thread.h"
3 int main()
4 {
5 int sockfd=socket_init();
6 if(sockfd==-1)
7 {
8 printf("create socket err\n");
9 exit(0);
10 }
11
12 struct sockaddr_in caddr;
13 while(1)
14 {
15 int len=sizeof(caddr);
16 int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
17 if(c<0)
18 {
19 continue;
20 }
21 printf("accept c=%d\n",c);
22 start_thread(c);//启动线程
23 }
24 }
socket.h
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #include<unistd.h>
5 #include<sys/socket.h>
6 #include<arpa/inet.h>
7 #include<netinet/in.h>
8
9 //创建套接字
10 int socket_init();
socket.c
1 #include"socket.h"
2
3 #define IPS "ipstr="
4 #define PORT "port="
5 #define LISMAX "lismax="
6
7 //存储用户信息(为监听套接字准备好需要需要的环境)
8 struct sock_info
9 {
10 char ips[32];//ip地址
11 short port;//port端口号
12 short lismax;//监听队列大小
13 };
14
15 //读取配置文件
16 int read_conf(struct sock_info*pa)
17 {
18 if(pa==NULL)
19 {
20 return -1;
21 }
22 FILE*fp=fopen("my.conf","r");//文件名,打开方式
23 if(fp==NULL)
24 {
25 printf("open my.conf failed\n");
26 return -1;
27 }
28 //记录错误行
29 int index =0;
30 char buff[128]={0};
31 while(fgets(buff,128,fp)!=NULL)//fgets(存放读取的数据,指针大小,从哪里读取)
32 {
33 index++;
34 //异常处理:跳过注释
35 if(strncmp(buff,"#",1)==0)
36 {
37 continue;
38 }
39 if(strncmp(buff,"\n",1)==0)
40 {
41 continue;
42 }
43 //记录错误信息
44 buff[strlen(buff)-1]='\0';
45
46 if(strncmp(buff,IPS,strlen(IPS))==0)
47 {
48 strcpy(pa->ips,buff+strlen(IPS));
49 }
50 else if(strncmp(buff,PORT,strlen(PORT))==0)
51 {
52 pa->port=atoi(buff+strlen(PORT));
53 }
54 else if(strncmp(buff,LISMAX,strlen(LISMAX))==0)
55 {
56 pa->lismax=atoi(buff+strlen(LISMAX));
57 }
58 else
59 {
60 printf("未识别的配置项在%d行:%s\n",index,buff);
61 }
62 }
63 fclose(fp);
64 }
65
66 //套接字初始化
67 int socket_init()
68 {
69 //读取文件:获取ip,port,lismax
70 struct sock_info a;
71 if(read_conf(&a)==-1)
72 {
73 printf("read conf err\n");
74 return -1;
75 }
76 printf("ip:%s\n",a.ips);
77 printf("port:%d\n",a.port);
78 printf("lismax:%d\n",a.lismax);
79
80 int sockfd=socket(AF_INET,SOCK_STREAM,0);
81 if(sockfd==-1)
82 {
83 return sockfd;
84 }
85 struct sockaddr_in saddr;
86 memset(&saddr,0,sizeof(saddr));
87 saddr.sin_family=AF_INET;
88 saddr.sin_port=htons(a.port);
89 saddr.sin_addr.s_addr=inet_addr(a.ips);
90
91 int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
92 if(res==-1)
93 {
94 return -1;
95 }
96 res=listen(sockfd,a.lismax);
97 if(res==-1)
98 {
99 return -1;
100 }
101 return sockfd;
102 }
work_thread.h
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<string.h>
5 #include<pthread.h>
6 #include<sys/socket.h>
7 #include<arpa/inet.h>
8 #include<netinet/in.h>
9
10 void start_thread(int c);
work_thread.c
1 #include"work_thread.h"
2
3 void*work_thread(void*arg)
4 {
5 int c=(int)arg;
6 while(1)
7 {
8 char buff[128]={0};
9 int n=recv(c,buff,127,0);
10 if(n<=0)
11 {
12 break;
13 }
14 printf("read:%s\n",buff);//测试
15 send(c,"ok",2,0);
16 }
17 close(c);
18 printf("client close\n");
19 }
20
21 void start_thread(int c)
22 {
23 pthread_t id;
24 pthread_create(&id,NULL,work_thread,(void*)c);
25 }
运行结果
测试错误代码看程序是否可以识别
问题提出:如何实现命令?
方法一:自己实现获取当前目录下有哪些文件:实现灵活
方法二:调用已有的命令如ls,mv等实现获取当前目录下有哪些文件:可扩展性比较高,实现接口通用,代码可移植性高。
本项目采用多线程编程,用创建监听套接字 socket, 用TCP协议实现交互方式,在父进程中先创建无名管道,然后fork产生子进程,在子进程中用exec函数进行替换命令,用管道写端dup2覆盖替换标准输出,然后父进程在管道中将数据读出,进而用管道实现父子间通信,比如命令ls执行结果返回给父进程,父进程发送给客户端,客户端收到后打印出来,然后close关闭。实现下载方式,用自定义协议,服务器端给客户端发送两次接收两次,用 sendfile 函数零拷贝发送,进度条显示下载进度,断点续传,上传文件过程中实现秒传。
协议
问题提出
首先需要区分文件是否需要断点续传?
(1)通过后缀区分:下载结束的a.txt,没下载结束的a.txt.tmp
(2)修改协议:get a.txt 0代表从起始位置下载,get a.txt size(size代表偏移量)偏移量的大小代表上次传输量,然后与文件进行校验,将其余部分进行上传填充到字符串后,将整体发送。
(3)将当前已经下载文件的部分大小获取,发送给服务器,服务器打开文件后进行大小偏移,然后从当前位置进行数据传输。
问题:如何让服务器可以兼容所有的客户端版本? 答:使用协议版本,产生一个接口去进行兼容。 |
MD5校验可以应用在多个领域,比如说机密资料的检验,下载文件的检验,明文密码的加密等。
如果要增加链接客户端数量,一个客户端要用一个套接字进行连接
ulimit -a
可以查看一个服务器最多连接文件数量
结果调试阻塞
运行结果