2017-2018-1 20155312 实验三 实时系统
2017-11-15 13:19
507 查看
2017-2018-1 20155312 实验三 实时系统
实验目的
任务一
学习使用Linux命令wc(1)基于Linux Socket程序设计实现wc(1)服务器(端口号是你学号的后6位)和客户端
客户端传一个文本文件给服务器
服务器返加文本文件中的单词数
任务二
使用多线程实现wc服务器并使用同步互斥机制保证计数正确实验步骤
任务一
任务一实现过程一共分为三个阶段:实现wc功能
实现客户端给服务器传文件功能
在客户端调用wc函数统计传过来的文件的单词个数
1.实现wc功能
为了正确实现统计单词个数的功能,找到其与wc命令的关系,先用「man 1 wc」查看wc命令的manpages,如下所示:wc指令功能:统计指定文件中的字节数、字数、行数,并将统计结果显示输出。
wc指令格式:wc [选项] 文件...
wc指令描述:其中word字长是由空格分隔的非零长度序列。
wc命令参数:
-c 统计字节数。
-l 统计行数。
-m 统计字符数。这个标志不能与 -c 标志一起使用。
-w 统计字数。一个字被定义为由空白、跳格或换行字符分隔的字符串。
-L 打印最长行的长度。
这里和我们的目标:统计单词数最相近的功能参数是『-w』。wc -w 命令统计字数时,要注意:字的定义是“空白、跳格或换行字符分隔的字符串”,和单词数不完全相同。
以test3.txt为例,其文件内容如下图所示,其包含的单词数有8个:
但是通过wc -w命令划分为以下6个字,所以显示的结果为6:
所以本人编写了两个mywc,分别实现wc -w功能和统计单词数的功能,其主要的区别在于划分单元时的分隔符范围:
wc -w命令实现时:由' ','\n','\t','\r'作为分隔符
单词数统计功能实现时:除上述分隔符,还加入了文本文件中常见的符号:'!','"','?','.',',','(',')',':',';','-'。
两个功能实现的过程类似,描述如下:
逐个字符的访问文件,直到读到文件结尾
如果当前字符是分割符,则将flag置0;
如果非分隔符,且flag=0,则将flag置1,将计数器加1;否则,不进行任何操作
mywc版本1实现
以下代码实现了单词个数统计功能:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define BUFFERSIZE 1024 /*int num=0; void wc(char buffer[],int size) { int i,flag=0; for(i=0;i<size;i++) { if(buffer[i]==' ' || buffer[i]=='\n' || buffer[i]=='\t' || buffer[i]=='\0' || buffer[i]=='!' || buffer[i]=='?' || buffer[i]=='"' || buffer[i]=='.' || buffer[i]== ',' || buffer[i]==':' || buffer[i]=='(' || buffer[i]==')' || buffer[i]==';' || buffer[i]=='■ ' || buffer[i]=='•' ) { flag=0; } else { if(flag==0) { num++; } flag=1; } } return num; }*/ int main() { FILE *fp; char ch; char filename[100]; int flag=0,num=0; printf("input filename: "); scanf("%s",filename); if((fp = fopen(filename,"r"))==NULL) { printf("Failure to open %s\n",filename); exit(0); } while((ch=fgetc(fp))!=EOF) { if(ch==' ' || ch=='\n' || ch=='\t' || ch=='\!' || ch=='\?' || ch=='\"' || ch=='\.' || ch== '\,' || ch=='\:' || ch=='\(' || ch=='\)' || ch=='\;' || ch=='\-') { flag=0; } else { if(flag==0) { flag=1; num++; } } } printf("%d\n",num); fclose(fp); return 0; }
运行结果如下图所示:
mywc版本2
实现了wc -w功能
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define BUFFERSIZE 1024 int main() { FILE *fp; char ch; char filename[100]; int flag=0,num=0; printf("input filename: "); scanf("%s",filename); if((fp = fopen(filename,"r"))==NULL) { printf("Failure to open %s\n",filename); exit(0); } while((ch=fgetc(fp))!=EOF) { if(ch==' ' || ch=='\n' || ch=='\t' || ch=='\r') { flag=0; } else { if(flag==0) { flag=1; num++; } } } printf("%c\n",num+48); fclose(fp); return 0; }
运行结果如下所示:
感谢惟佳告诉我用gedit打开文件,让我开启新世界的大门
2.文件传输功能
实现文件传输功能之前,首先要实现客户端和服务器的通信,结合我们在网络安全编程基础中学习的内容,我们知道客户端和服务器的通信过程如下:客户端:socket()→bind()→connect()→send()→recv()→close()
服务器:socket()→bind()→accept()→recv()→send()→close()
搭建好几本框架后,客户端文件发送思路如下:
打开待发送文件,并按字符读取文件内容;
将读取到的内容存到数组buffer中
将buffer发送到服务器
这里涉及到定义buffer数组大小的问题,因为我们并不确定文件大小,又无法将buffer定义成无限大,所以步骤2、3的具体实现思路是:
将字符存入数组,若达到数组最大值,则将buffer数组发送到客户端;
每发送一次buffer,都将其清零,并把控制变量修改为0
文件读取完毕退出后,判断最后一次读取的buffer是否达到最大值,若未达到最大值,还需要将最后一次写入的buffer发送给服务器。
至此完成了客户端发送文件的操作。
服务器端接收文件思路如下:
以"w"写方式打开接收文件“recvfile.txt”
不断接收客户端传送的数据,并存入buffer,直到接收数据长度为0
每接收一个buffer,就将其写入接收文件“recvfile.txt”
最后关闭文件
3.统计单词个数
实现思路:在客户端调用mywc函数,用两种方式分别计算文件的“字数”和“单词数”,在服务器端调用mywc函数,通过输入,选择一种方式计算文件中“字数”或“单词数”
客户端mywc函数的参数为文件名和模式标号,调用时,分别用“1”,“2”两种模式调用两次;服务器端mywc函数的参数只有文件名,在函数内部接收代表模式的输入。
实现了以上功能后,任务一就完成了。其中客户端代码如下:
#include<netinet/in.h> // for sockaddr_in #include<sys/types.h> // for socket #include<sys/socket.h> // for socket #include<stdio.h> // for printf #include<stdlib.h> // for exit #include<string.h> // for bzero #define HELLO_WORLD_SERVER_PORT 155312 #define BUFFER_SIZE 1024 #define FILE_NAME_MAX_SIZE 512 int mywc(char file_name[],int choose); int main(int argc, char **argv) { FILE *fp; if (argc != 2) { printf("Usage: ./%s ServerIPAddress\n", argv[0]); exit(1); } // 设置一个socket地址结构client_addr, 代表客户机的internet地址和端口 struct sockaddr_in client_addr; bzero(&client_addr, sizeof(client_addr)); client_addr.sin_family = AF_INET; // internet协议族 client_addr.sin_addr.s_addr = htons(INADDR_ANY); // INADDR_ANY表示自动获取本机地址 client_addr.sin_port = htons(0); // auto allocated, 让系统自动分配一个空闲端口 // 创建用于internet的流协议(TCP)类型socket,用client_socket代表客户端socket int client_socket = socket(AF_INET, SOCK_STREAM, 0); if (client_socket < 0) { printf("Create Socket Failed!\n"); exit(1); } // 把客户端的socket和客户端的socket地址结构绑定 if (bind(client_socket, (struct sockaddr*)&client_addr, sizeof(client_addr))) { printf("Client Bind Port Failed!\n"); exit(1); } // 设置一个socket地址结构server_addr,代表服务器的internet地址和端口 struct sockaddr_in server_addr; bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; // 服务器的IP地址来自程序的参数 if (inet_aton(argv[1], &server_addr.sin_addr) == 0) { printf("Server IP Address Error!\n"); exit(1); } server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT); socklen_t server_addr_length = sizeof(server_addr); // 向服务器发起连接请求,连接成功后client_socket代表客户端和服务器端的一个socket连接 if (connect(client_socket, (struct sockaddr*)&server_addr, server_addr_length) < 0) { printf("Can Not Connect To %s!\n", argv[1]); exit(1); } char file_name[FILE_NAME_MAX_SIZE + 1]; bzero(file_name, sizeof(file_name)); printf("Please Input File Name.\t"); scanf("%s", file_name); if((fp = fopen(file_name,"r"))==NULL) { printf("Failure to open %s\n",file_name); exit(0); } char buffer[BUFFER_SIZE]; bzero(buffer, sizeof(buffer)); char ch; int i=0; while((ch=fgetc(fp))!=EOF) { buffer[i++]=ch; if(i>=BUFFER_SIZE) { if((send(client_socket, buffer, BUFFER_SIZE, 0))==-1) { printf("发送文件失败\n"); } bzero(buffer, sizeof(buffer)); i=0; } } if(i<BUFFER_SIZE) { if((send(client_socket, buffer, i, 0))==-1) { printf("发送文件失败\n"); } } printf("发送%s完毕\n",file_name); mywc(file_name,1); mywc(file_name,2); // 向服务器发送buffer中的数据,此时buffer中存放的是客户端需要接收的文件 //以下接收服务器发来的单词个数 bzero(buffer, sizeof(buffer)); // int length = 0; /* int length = recv(client_socket, buffer, sizeof(buffer), 0); if (length < 0) { printf("Recieve Data From Server %s Failed!\n", argv[1]); } else { printf("Recieve words number %c From Server[%s] Finished!\n",buffer[0],argv[1]); } bzero(buffer, BUFFER_SIZE); */ // 传输完毕,关闭socket fclose(fp); close(client_socket); return 0; } int mywc(char file_name[],int choose) { FILE *fp; char ch; int flag=0,num=0; // int choose; // printf("统计单词个数还是实现“wc -w”?(1or2)\n"); // scanf("%d",&choose); if((fp = fopen(file_name,"r"))==NULL) { printf("Failure to open %s\n",file_name); exit(0); } if(choose==1) { while((ch=fgetc(fp))!=EOF) { if(ch==' ' || ch=='\n' || ch=='\t' || ch=='\!' || ch=='\?' || ch=='\"' || ch=='\.' || ch== '\,' || ch=='\:' || ch=='\(' || ch=='\)' || ch=='\;' || ch=='\-') { flag=0; } else { if(flag==0) { flag=1; num++; } } } } else if(choose==2) { while((ch=fgetc(fp))!=EOF) { if(ch==' ' || ch=='\n' || ch=='\t' || ch=='\r') flag=0; else { if(flag==0) { flag=1; num++; } } } } printf("单词个数为:%d_用方式%d计算\n",num,choose); fclose(fp); return num; }
服务器端代码如下:
#include<netinet/in.h> #include<sys/types.h> #include<sys/socket.h> #include<stdio.h> #include<stdlib.h> #include<string.h> #define HELLO_WORLD_SERVER_PORT 155312 #define LENGTH_OF_LISTEN_QUEUE 20 #define BUFFER_SIZE 1024 #define FILE_NAME_MAX_SIZE 512 int mywc(char file_name[]) { char ch; int flag=0,num=0; int choose; FILE *fp; printf("统计单词个数还是实现“wc -w”?(1or2)\n"); scanf("%d",&choose); if((fp = fopen(file_name,"r"))==NULL) { printf("Failure to open %s\n",file_name); exit(0); } if(choose==1) { while((ch=fgetc(fp))!=EOF) { if(ch==' ' || ch=='\n' || ch=='\t' || ch=='\!' || ch=='\?' || ch=='\"' || ch=='\.' || ch== '\,' || ch=='\:' || ch=='\(' || ch=='\)' || ch=='\;' || ch=='\-') { flag=0; } else { if(flag==0) { flag=1; num++; } } } } else if(choose==2) { while((ch=fgetc(fp))!=EOF) { if(ch==' ' || ch=='\n' || ch=='\t' || ch=='\r') flag=0; else { if(flag==0) { flag=1; num++; } } } } printf("单词个数为:%d\n",num); fclose(fp); return num; } int main(int argc, char **argv) { // set socket's address information // 设置一个socket地址结构server_addr,代表服务器internet的地址和端口 struct sockaddr_in server_addr; bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htons(INADDR_ANY); server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT); // create a stream socket // 创建用于internet的流协议(TCP)socket,用server_socket代表服务器向客户端提供服务的接口 int server_socket = socket(PF_INET, SOCK_STREAM, 0); if (server_socket < 0) { printf("Create Socket Failed!\n"); exit(1); } // 把socket和socket地址结构绑定 if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr))) { printf("Server Bind Port: %d Failed!\n", HELLO_WORLD_SERVER_PORT); exit(1); } // server_socket用于监听 if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE)) { printf("Server Listen Failed!\n"); exit(1); } // 服务器端一直运行用以持续为客户端提供服务 // 定义客户端的socket地址结构client_addr,当收到来自客户端的请求后,调用accept // 接受此请求,同时将client端的地址和端口等信息写入client_addr中 struct sockaddr_in client_addr; int length = sizeof(client_addr); // 接受一个从client端到达server端的连接请求,将客户端的信息保存在client_addr中 // 如果没有连接请求,则一直等待直到有连接请求为止,这是accept函数的特性,可以 // 用select()来实现超时检测 // accpet返回一个新的socket,这个socket用来与此次连接到server的client进行通信 // 这里的new_server_socket代表了这个通信通道 int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length); printf("连接到客户端\n"); if (new_server_socket < 0) { printf("Server Accept Failed!\n"); } FILE *fp; if((fp = fopen("recvfile.txt","w"))==NULL) { printf("Failure to open recvfile\n"); exit(0); } //接受来自客户端的文件 char buffer[BUFFER_SIZE]; bzero(buffer, sizeof(buffer)); length=0; while( length = recv(new_server_socket, buffer, BUFFER_SIZE, 0) ) { if(length<0) { printf("接受文件出错\n"); exit(0); } if(fwrite(buffer,sizeof(char),length,fp)<length) { printf("写文件失败\n"); } bzero(buffer, BUFFER_SIZE); } fclose(fp); printf("接受文件完毕\n"); int number=0; char file_name[512]="recvfile.txt"; number=mywc(file_name); printf("%d\n",number); bzero(buffer, BUFFER_SIZE); buffer[0]=number+48; // 发送buffer中的字符串到new_server_socket,实际上就是发送给客户端 if (send(new_server_socket, buffer, sizeof(buffer), 0) < 0) { printf("Send number Failed!\n"); } printf("发送单词个数完毕\n"); bzero(buffer, sizeof(buffer)); // fclose(fp); printf("File Transfer Finished!\n"); close(new_server_socket); close(server_socket); return 0; }
运行截图如下所示:
任务二
多线程实现wc服务器时,会出现多个客户端同时像服务器传送文件的情况,所以仅仅在服务器端创建一个recvfile.txt用于接收文件是远远不够的,需要根据发送的不同文件名创建新的接收文件。为此,修改了客户端代码,在发送文件之前先将文件名发送至服务器;服务器端就收文件名后,在该文件后面加上“-server”作为接收文件的名字,这样就避免了多个文件写入一个recvfile的冲突。
此外,在服务器端实现多线程的思路如下:
建立一个while循环,不断接收来自客户端的连接;
在循环内创建一个线程
将接收文件、调用mywc函数等过程抽象为一个万能函数,void process_client(void newnew_server_socket)
在线程中调用这个函数,并将用于接收客户端连接的accept函数返回值newnew_server_socket的地址作为参数。
客户端添加文件名传输后的代码client.c如下:
#include<netinet/in.h> // for sockaddr_in #include<sys/types.h> // for socket #include<sys/socket.h> // for socket #include<stdio.h> // for printf #include<stdlib.h> // for exit #include<string.h> // for bzero #define HELLO_WORLD_SERVER_PORT 155312 #define BUFFER_SIZE 1024 #define FILE_NAME_MAX_SIZE 512 int mywc(char file_name[],int choose); int main(int argc, char **argv) { FILE *fp; if (argc != 2) { printf("Usage: ./%s ServerIPAddress\n", argv[0]); exit(1); } // 设置一个socket地址结构client_addr, 代表客户机的internet地址和端口 struct sockaddr_in client_addr; bzero(&client_addr, sizeof(client_addr)); client_addr.sin_family = AF_INET; // internet协议族 client_addr.sin_addr.s_addr = htons(INADDR_ANY); // INADDR_ANY表示自动获取本机地址 client_addr.sin_port = htons(0); // auto allocated, 让系统自动分配一个空闲端口 // 创建用于internet的流协议(TCP)类型socket,用client_socket代表客户端socket int client_socket = socket(AF_INET, SOCK_STREAM, 0); if (client_socket < 0) { printf("Create Socket Failed!\n"); exit(1); } // 把客户端的socket和客户端的socket地址结构绑定 if (bind(client_socket, (struct sockaddr*)&client_addr, sizeof(client_addr))) { printf("Client Bind Port Failed!\n"); exit(1); } // 设置一个socket地址结构server_addr,代表服务器的internet地址和端口 struct sockaddr_in server_addr; bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; // 服务器的IP地址来自程序的参数 if (inet_aton(argv[1], &server_addr.sin_addr) == 0) { printf("Server IP Address Error!\n"); exit(1); } server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT); socklen_t server_addr_length = sizeof(server_addr); // 向服务器发起连接请求,连接成功后client_socket代表客户端和服务器端的一个socket连接 if (connect(client_socket, (struct sockaddr*)&server_addr, server_addr_length) < 0) { printf("Can Not Connect To %s!\n", argv[1]); exit(1); } char file_name[FILE_NAME_MAX_SIZE + 1]; bzero(file_name, sizeof(file_name)); printf("Please Input File Name.\t"); scanf("%s", file_name); if((fp = fopen(file_name,"r"))==NULL) { printf("Failure to open %s\n",file_name); exit(0); } char buffer[BUFFER_SIZE]; bzero(buffer, sizeof(buffer)); strcpy(buffer,file_name); if(send(client_socket,buffer,BUFFER_SIZE,0)==-1) { printf("发送文件名失败\n"); } char ch; int i=0; while((ch=fgetc(fp))!=EOF) { buffer[i++]=ch; if(i>=BUFFER_SIZE) { if((send(client_socket, buffer, BUFFER_SIZE, 0))==-1) { printf("发送文件失败\n"); } bzero(buffer, sizeof(buffer)); i=0; } } if(i<BUFFER_SIZE) { if((send(client_socket, buffer, i, 0))==-1) { printf("发送文件失败\n"); } } printf("发送%s完毕\n",file_name); mywc(file_name,1); mywc(file_name,2); // 向服务器发送buffer中的数据,此时buffer中存放的是客户端需要接收的文件 //以下接收服务器发来的单词个数 bzero(buffer, sizeof(buffer)); // int length = 0; /* int length = recv(client_socket, buffer, sizeof(buffer), 0); if (length < 0) { printf("Recieve Data From Server %s Failed!\n", argv[1]); } else { printf("Recieve words number %c From Server[%s] Finished!\n",buffer[0],argv[1]); } bzero(buffer, BUFFER_SIZE); */ // 传输完毕,关闭socket fclose(fp); close(client_socket); return 0; } int mywc(char file_name[],int choose) { FILE *fp; char ch; int flag=0,num=0; // int choose; // printf("统计单词个数还是实现“wc -w”?(1or2)\n"); // scanf("%d",&choose); if((fp = fopen(file_name,"r"))==NULL) { printf("Failure to open %s\n",file_name); exit(0); } if(choose==1) { while((ch=fgetc(fp))!=EOF) { if(ch==' ' || ch=='\n' || ch=='\t' || ch=='\!' || ch=='\?' || ch=='\"' || ch=='\.' || ch== '\,' || ch=='\:' || ch=='\(' || ch=='\)' || ch=='\;' || ch=='\-') { flag=0; } else { if(flag==0) { flag=1; num++; } } } } else if(choose==2) { while((ch=fgetc(fp))!=EOF) { if(ch==' ' || ch=='\n' || ch=='\t' || ch=='\r') flag=0; else { if(flag==0) { flag=1; num++; } } } } printf("单词个数为:%d_用方式%d计算\n",num,choose); fclose(fp); return num; }
服务器端实现多线程后的代码newfile.c如下:
#include<netinet/in.h> #include<sys/types.h> #include<sys/socket.h> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<pthread.h> #define HELLO_WORLD_SERVER_PORT 155312 #define LENGTH_OF_LISTEN_QUEUE 20 #define BUFFER_SIZE 1024 #define FILE_NAME_MAX_SIZE 512 void *process_client(void *new_server_socket); int mywc(char file_name[]) { char ch; int flag=0,num=0; int choose; FILE *fp; printf("统计单词个数还是实现“wc -w”?(1or2)\n"); scanf("%d",&choose); if((fp = fopen(file_name,"r"))==NULL) { printf("Failure to open %s\n",file_name); exit(0); } if(choose==1) { while((ch=fgetc(fp))!=EOF) { if(ch==' ' || ch=='\n' || ch=='\t' || ch=='\!' || ch=='\?' || ch=='\"' || ch=='\.' || ch== '\,' || ch=='\:' || ch=='\(' || ch=='\)' || ch=='\;' || ch=='\-') { flag=0; } else { if(flag==0) { flag=1; num++; } } } } else if(choose==2) { while((ch=fgetc(fp))!=EOF) { if(ch==' ' || ch=='\n' || ch=='\t' || ch=='\r') flag=0; else { if(flag==0) { flag=1; num++; } } } } printf("单词个数为:%d\n",num); fclose(fp); return num; } int main(int argc, char **argv) { // set socket's address information // 设置一个socket地址结构server_addr,代表服务器internet的地址和端口 struct sockaddr_in server_addr; bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htons(INADDR_ANY); server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT); // create a stream socket // 创建用于internet的流协议(TCP)socket,用server_socket代表服务器向客户端提供服务的接口 int server_socket = socket(PF_INET, SOCK_STREAM, 0); if (server_socket < 0) { printf("Create Socket Failed!\n"); exit(1); } // 把socket和socket地址结构绑定 if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr))) { printf("Server Bind Port: %d Failed!\n", HELLO_WORLD_SERVER_PORT); exit(1); } // server_socket用于监听 if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE)) { printf("Server Listen Failed!\n"); exit(1); } // 服务器端一直运行用以持续为客户端提供服务 while(1) { // 定义客户端的socket地址结构client_addr,当收到来自客户端的请求后,调用accept // 接受此请求,同时将client端的地址和端口等信息写入client_addr中 struct sockaddr_in client_addr; socklen_t length = sizeof(client_addr); // 接受一个从client端到达server端的连接请求,将客户端的信息保存在client_addr中 // 如果没有连接请求,则一直等待直到有连接请求为止,这是accept函数的特性,可以 // 用select()来实现超时检测 // accpet返回一个新的socket,这个socket用来与此次连接到server的client进行通信 // 这里的new_server_socket代表了这个通信通道 int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length); printf("连接到客户端\n"); if (new_server_socket < 0) { printf("Server Accept Failed!\n"); } //添加进程相关代码 pthread_t pid; if(pthread_create(&pid, NULL, process_client,(void *) &new_server_socket) < 0){ printf("pthread_create error\n"); } } // close(server_socket); } void *process_client(void *new_server_socket) { int sockid=*(int *)new_server_socket; FILE *fp; //接受来自客户端的文件 char buffer[BUFFER_SIZE]; char file_name[FILE_NAME_MAX_SIZE]; bzero(buffer, sizeof(buffer)); int length=0; if(recv(sockid,buffer,BUFFER_SIZE, 0)==-1) { printf("接受文件名%s失败\n",buffer); } strcpy(file_name,buffer); strcat(file_name,"-server"); if((fp = fopen(file_name,"w"))==NULL) { printf("Failure to open %s\n",file_name); exit(0); } while( length = recv(sockid, buffer, BUFFER_SIZE, 0)) { if(length<0) { printf("接受文件出错\n"); exit(0); } if(fwrite(buffer,sizeof(char),length,fp)<length) { printf("写文件失败\n"); } bzero(buffer, BUFFER_SIZE); } fclose(fp); printf("接受文件完毕\n"); int number=0; number=mywc(file_name); bzero(buffer, BUFFER_SIZE); buffer[0]=number+48; // 发送buffer中的字符串到new_server_socket,实际上就是发送给客户端 /*if (send(new_server_socket, buffer, sizeof(buffer), 0) < 0) { printf("Send number Failed!\n"); } printf("发送单词个数完毕\n");*/ bzero(buffer, sizeof(buffer)); // fclose(fp); printf("File Transfer Finished!\n"); close(new_server_socket); }
运行时可以同时多个客户端一起给服务器传文件效率更高,如下所示:
实验中的问题及解决过程
问题1:完成任务一时,服务器无法显示读取文件完毕,只有关闭了客户端,服务器才能进行下一步计算个数的操作,总是显示单词数为0.问题1解决过程:
首先确定文件是否传输成功,发现执行程序后,可以发现在服务器所在文件夹可以生成相应的recvfile.txt,这说明发送文件和接收文件没有问题;
最开始调用mywc之前,不会关闭文件,而是将文件指针作为参数传递给mywc函数,执行完函数返回主函数再关闭文件。经过分析,发现原因是写文件结束后,文件指针的位置指向文件结尾,所以此时传递给mywc函数的指针跟打开文件时指向文件头的指针是完全不同的。
发现问题后,修改了mywc函数参数,从文件指针变为文件名;在服务器主函数中写入文件后就关闭文件,然后在mywc函数中再次打开这个文件,统计完个数就在该函数中关闭文件。这样修改后,解决了问题。
问题2:改写服务器和客户端的代码实现多线程时,服务器显示“接收文件失败”并退出。
问题2解决过程:
首先分析代码,确定设定的循环包含的内容正确,不缺少关键环节;
其次检查进程函数process_client参数,参数类型传递正确;
最后检查proces_client函数内部,发现自己误将void *类型的指针变量new_server_socket当成了主函数中的int型变量new_server_socket,导致执行recv(new_server_socket,buffer,BUFFER_SIZE, 0)时,由于第一个参数类型不匹配而接收文件失败;
定义一个int 型变量,int sockid=(int )new_server_socket;用于取指针new_server_socket中的值,然后在后续操作中执行recv函数时,使用这个变量sockid作为参数,最后解决了问题。
新学到的知识点
学会了sock传文件的相关知识学习了如何动态读、写文件
学习了如何实现wc指令、如何统计一个文本的单词个数
学习了多线程编程方法,了解了pthread_creat等函数的用法
参考资料
Linux下使用socket传输文件的C语言简单实现Linux C TCPSocket 传输文件简单实例-多线程实现
linux中wc命令用法
相关文章推荐
- 2017-2018-1 20155210 实验三 实时系统
- 2017-2018-1 20155205 实验三 实时系统
- 2017-2018-1 20155236 实验三 实时系统
- 2017-2018-1 20155202 实验三 实时系统
- 2017-2018-1 20155339 实验三 实时系统
- 2017-2018-1 20155311 实验三 实时系统
- 2017-2018-1 20155320 实验三——实时系统
- 2017-2018-1 20155206 《信息安全系统设计基础》实验三 实时系统
- 2017-2018-1 20155208 实验三 实时系统
- 2017-2018-1 20155321 《信息安全系统设计基础》实验五——实时系统
- 2017-2018-1 20155323 实验三 实时系统
- # 2017-2018-1 20155224 实验三-实时系统
- 2017-2018-11 20155307刘浩 20155338常胜杰 20155335俞昆 实验三 实时系统实验报告
- 2017-2018-1 20155222实验三 实时系统
- 2017-2018-1 20155313 实验三 实时系统
- 2017-2018-1 20155218 实验三 实时系统
- 2017-2018-1 20155315 《信息安全系统设计基础》实验三 实时系统
- 2017-2018-1 20155203 实验三 实时系统
- 2017-2018-1 20155211 实验三 实时系统
- 2017-2018-1 20155310 《信息安全系统设计基础》实验三——实时系统