2017-2018-1 《信息安全系统设计基础》实验三报告
2017-2018-1 《信息安全系统设计基础》实验三报告
本小组成员:20155303、20155213
————————CONTENTS————————
- 任务一 C语言模拟wc命令
- 任务二 实现传送文本文件的服务器和客户端
- 任务三 多线程实现传送文本文件的服务器和客户端
- 任务四 使用PC机和实验箱模拟客户端服务器并测试
- 实验感想与体会
- 参考资料
任务一 C语言模拟wc命令
使用man wc命令查看wc命令的基本用法:
可知wc命令的功能为:统计指定文件中的字节数、字数、行数等,并将统计结果显示输出。常用的参数为:
- -c:统计字节数
- -l:统计行数
- -m:统计字符数,且不能与-c参数一起使用
- -w:统计字数,一个字被定义为由空白、跳格或换行字符分割的字符串
- -L:打印最长行的长度
- ......
但是,如果我们想统计某文件中出现过某个特定单词的行数,只用wc命令是无法完成的。我们可以借助管道将wc命令与其他命令(如grep)串联起来:
grep and test.txt | wc -l
上面命令实现了查找test.txt中所有出现过“and”这个单词的行,并统计行数。
再进一步,如果想精确到个数(比如一行出现两次,算作2),可以加上参数-o选项(only),表示只选中那些匹配的地方,结果为:
基于以上分析,代码如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> void wc_func(char *file,int ism,int isw,int isl); int main(int argc ,char *argv[]){int ism,isw,isl,opt;ism = isw = isl = 0;int count = 0;while((opt=getopt(argc,argv,"mwl"))!=-1){count++;switch(opt){case 'm':ism=1;break;case 'w':isw=1;break;case 'l':isl=1;break;case '?':printf("请查看该指令说明文档 %c\n",optopt);exit(0);}} if(count==0){ism=isw=isl=1;}if(optind==argc){printf("wc error: have no file!\n");}for(;optind<argc;optind++){wc_func(argv[optind],ism,isw,isl);} } void wc_func(char *file,int ism,int isw,int isl) {int t,m,w,l;int state = 0;FILE *in;if((in = fopen(file,"r"))==NULL){printf("wc %s:no this file or dir\n",file);return;}w=m=l=0;while((t=fgetc(in))!=EOF){/*if(t=='\t'||t==' '){ w++;}else if(t=='\n'){l++;}*/if(t == '\n') {l++;state = 0;continue;} else if(t == ' ') {state = 0;continue;} else if(t == '\r') {state = 0;continue;} else {if(state == 0) {state = 1;w++;}continue;}m++; }if(isl)printf("%-5d",l);if(isw)printf("%-5d",w);if(ism)printf("%-5d",m);printf("%-10s\n",file); }运行结果如下:
返回目录
任务二 实现传送文本文件的服务器和客户端
虽然在网络安全编程基础课程上学习过网络编程的相关知识,但基于的是Windows。将其移植到Linux下时需要注意以下几个方面:
- 头文件
Windows下winsock.h或winsock2.h;
Linux下netinet/in.h(包括大部分),unistd.h(包括close函数),sys/socket.h。
- 初始化
windows下需要用WSAStartup启动Ws2_32.lib;
linux下不需要。
- 关闭socket
windows下使用closesocket();
linux下使用close()。
- 类型
windows下SOCKET;
linux下int。
- 多线程(下一个任务会用到)
windows下包含process.h,使用_beginthread和_endthread;
linux下包含pthread.h,使用pthread_create和pthread_exit。
- ......
以上是我在移植过程中遇到的问题,需格外注意。更多情况可参考Socket程序从windows移植到linux的注意事项等相关文章。
基于客户端与服务器的通信流程,可分别写出创建服务器和客户端,以及客户端和服务器连接的代码:
/*创建服务器:*/ int start_server(int port, int type){//建立服务器套接字int ss = socket(AF_INET, type, 0);if(ss < 0){printf("create socket error\n");return -1;}//设置服务器地址struct sockaddr_in server_addr; //服务器地址结构bzero(&server_addr, sizeof(struct sockaddr_in)); //清零server_addr.sin_family = AF_INET; //协议族server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //ip地址server_addr.sin_port = htons(port); //端口//绑定地址结构到套接字描述符if(bind(ss, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){printf("bind error\n");return -1;}//TCPif(SOCK_STREAM == type){//设置侦听if(listen(ss, LISTEN_SIZE) < 0){printf("listen error\n");return -1;}printf("tcp server start\n");}elseprintf("udp server start\n");return ss; }int create_tcp_server(int port){start_server(port, SOCK_STREAM); }int create_udp_server(int port){start_server(port, SOCK_DGRAM); } /*接受客户端连接:*/ socklen_t addrlen = sizeof(struct sockaddr); struct sockaddr_in client_addr; //客户端地址结构 client_sock = accept(ss, (struct sockaddr*)&client_addr, &addrlen); if(client_sock < 0){printf("accept error\n"); }printf("accept success\n"); /*客户端:*/ int connectsock(char* server_ip, int server_port, int type){int sock_fd = socket(AF_INET, type, 0);if(-1 == sock_fd){printf("create socket error\n");return -1;}struct sockaddr_in server_addr;//设置服务器地址bzero(&server_addr, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_port = htons(server_port);inet_pton(AF_INET, server_ip, &server_addr.sin_addr);//连接服务器if(-1 == connect(sock_fd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr_in))){printf("connect server error\n");return -1;}printf("connect server success\n");return sock_fd; } int connect_tcp(char* server_ip, int server_port){return connectsock(server_ip, server_port, SOCK_STREAM); } int connect_udp(char* server_ip, int server_port){return connectsock(server_ip, server_port, SOCK_DGRAM); }搭建好客户端和服务器,接下来考虑如何传输文件。
传输数据需要用到缓冲区。客户端首先输入文件名,并判断该文件是否存在。若不存在,则返回错误提示;存在,则发送至服务器。服务器接收到数据后,创建一个同样名称的文件。至此,完成了最基础的一步。
传输文件内容的思路上面类似,但需注意:文件名称一般很短,但文件内容大小并不确定。所以不要奢望一次性传输完所有的数据,而应设置循环,以固定大小传输数据,一直到传输结束。
传输数据部分的代码如下:
/*客户端:*/ char file_name[FILE_NAME_MAX_SIZE+1];bzero(file_name, FILE_NAME_MAX_SIZE+1);printf("Please Input File Name On Server:\t");scanf("%s", file_name);char buffer[BUFFER_SIZE];bzero(buffer,BUFFER_SIZE);strncpy(buffer, file_name, strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name));//向服务器发送buffer中的数据send(client_socket,buffer,BUFFER_SIZE,0);FILE * fp = fopen(file_name,"r");if(NULL == fp ){printf("File:\t%s Not Found\n", file_name);exit(1);}else{bzero(buffer, BUFFER_SIZE);int file_block_length = 0;while( (file_block_length = fread(buffer,sizeof(char),BUFFER_SIZE, fp))>0){//printf("file_block_length = %d\n",file_block_length);//发送buffer中的字符串到服务器if(send(client_socket,buffer,file_block_length,0)<0){printf("Send File:\t%s Failed\n", file_name);break;}bzero(buffer, BUFFER_SIZE);}}printf("Send File:\t %s To Server[%s] Finished\n",file_name, argv[1]);printf("The File has %d words.\n", wc_func(file_name));fclose(fp); /*服务器:*/char file_name[FILE_NAME_MAX_SIZE+1];bzero(file_name, FILE_NAME_MAX_SIZE+1);char buffer[BUFFER_SIZE];bzero(buffer,BUFFER_SIZE);recv(new_server_socket,file_name,BUFFER_SIZE,0);FILE * fp = fopen(file_name,"w");if(NULL == fp ){printf("File:\t%s Can Not Open To Write\n", file_name);exit(1);}//从客户端接收数据到buffer中bzero(buffer,BUFFER_SIZE);int len = 0;while( len = recv(new_server_socket,buffer,BUFFER_SIZE,0)){if(len < 0){printf("Recieve Data From Client %s Failed!\n", argv[1]);break;}int write_length = fwrite(buffer,sizeof(char),len,fp);if (write_length<len){printf("File:\t%s Write Failed\n", file_name);break;}bzero(buffer,BUFFER_SIZE); }printf("File:\t%s Transfer Finished!\n",file_name);fclose(fp);按照要求,在服务器端调用计算单词数的函数,并返回给客户端即可。
【注:完成代码已上传至码云】
返回目录
任务三 多线程实现传送文本文件的服务器和客户端
通过man -k命令查看与创建进程相关的函数:
我们可以找到“pthread_create()”函数,使用man 3 pthread_create命令可以得知其用法:
说明:
- thread:线程标识符;
- attr:线程属性设置;
- start_routine:线程函数的起始地址;
- arg:传递给start_routine的参数;
- 返回值:成功,返回0;出错,返回-1。
- 注意:pthread库不是Linux系统默认的库,连接时需要使用静态库libpthread.a,所以在线程函数在编译时,需要使用“-lpthread”链接库函数。
因此,服务器需要循环检测是否有新的连接。如果有,则调用pthread_create()函数创建新的进程,并执行相关代码。这部分的代码如下:
while(1){//接受客户端连接socklen_t addrlen = sizeof(struct sockaddr);struct sockaddr_in client_addr; //客户端地址结构int client_sock = accept(ss, (struct sockaddr*)&client_addr, &addrlen);if(client_sock < 0){printf("accept error\n");}printf("accept success\n");pthread_t pid;if(pthread_create(&pid, NULL, process_client, &client_sock) < 0){printf("pthread_create error\n");}}创建了新的线程,接下来就可以传送文件了。过程与任务二类似。
【注:完成代码已上传至码云】
返回目录
任务四 使用PC机和实验箱模拟客户端服务器并测试(未完成)
返回目录
实验感想与体会
- 此次实验首先在Linux下实现了客户端与服务器传送文本文件,并模拟wc命令统计文本文件中的单词数。但一次只能为一个客户端提供服务的迭代网络是不现实的,因此,在任务二中创建了一个并发服务器,它为每一个客户端创建一个单独的逻辑流。这就允许服务器同时为多个客户端服务,提高了其使用价值。
- 在完成任务三的过程中遇到了重重困难。本打算在自己笔记本上完成,也方便后续学习,但参考指导书《实验开发环境使用说明-12.04》中“解决上网与本地网络调试冲突”的相关讲解完成配置后,实验箱与主机仍然无法ping通。想到之前实验一在实验室的PC机上完成得比较顺利,于是改用实验室的PC机,但找不到正确的端口,更换了PC机和实验箱后仍然解决不了,只能作罢。
- 近期学习效率低下,每周一章的任务常常不能按时完成,总需要后期抽时间补充。回想上学期学习Java时,虽然也是一周学习一章,但并没有觉得如此吃力,花十几个小时就能完成当周的任务。尤其这次实验结束,花费大量时间也没有解决问题,心情失落的同时,不禁怀疑自己的自学能力。且不说与老师的要求相差甚远,这种学习效率与状态连自己都不能接受。课本内容较上学期更难更深是一方面,但更多的是自身的问题。比如没有合理分配时间,做到门门课程兼顾;比如将精力浪费在一些无关紧要的事情上,等等。
- 课下了解到,不少同学也遇到了类似的问题。能及时赶上老师的进度还好,一旦某一阵略有松懈没跟上节奏,就容易产生恶性循环,落下越来越多的内容。老师一直以来采取的都是自学为主,教学为辅的方式,这一点无可厚非,我认为自学能力可以让人终生受益。不怕自学的过程中遇到困难,怕就怕在总是遇到无法解决的困难而逐渐放弃。
- 前几天不慎扭伤了脚,不能在寝室、主楼和图书馆之间随意跑的日子还真不适应,只能躺着休息,因此对好多任务都心有余而力不足...多亏朋友们悉心的照顾还有及时涂药,脚在一天天康复,落下的事情也要一点点补回来。那么,就先从努力按时完成本周学习任务开始吧:)
返回目录
参考资料
- Linux C TCP Socket实现客户与服务器简单通信
- Linux C TCPSocket 传输文件简单实例-多线程实现
- wc:统计一个文件里出现某个单词出现的次数
- Socket程序从Windows移植到Linux下的一些注意事项
- Linux下getopt()函数的简单使用
- linux网络编程:用C语言实现的聊天程序(同步通信)
- Linux下C编写基本的多线程socket服务器
返回目录
转载于:https://www.cnblogs.com/Vivian517/p/7832469.html
总结
以上是生活随笔为你收集整理的2017-2018-1 《信息安全系统设计基础》实验三报告的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: css hover图片hover效果兼容
- 下一篇: Python Selenium + ph