zoukankan      html  css  js  c++  java
  • 2017-2018-1 《信息安全系统设计基础》实验三报告

    2017-2018-1 《信息安全系统设计基础》实验三报告

    本小组成员:2015530320155213

    ————————CONTENTS————————


    任务一 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
    ",optopt);
    				exit(0);
    		}
    	}	
    	if(count==0)
    	{
    		ism=isw=isl=1;
    	}
    	if(optind==argc)
    	{
    		printf("wc error: have no file!
    ");
    	}
    	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
    ",file);
    		return;
    	}
    	w=m=l=0;
    	while((t=fgetc(in))!=EOF)
    	{
    		/*if(t=='	'||t==' ')
    		{	
    			w++;
    		}
    		else if(t=='
    ')
    		{
    			l++;
    		}*/
    		if(t == '
    ') {
                		l++;
                		state = 0;
                		continue;
            	} else if(t == ' ') {
                		state = 0;
                		continue;
            	} else if(t == '
    ') {
                		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
    ",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
    ");
            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
    ");
            return -1;
        }
        //TCP
        if(SOCK_STREAM == type){
            //设置侦听
            if(listen(ss, LISTEN_SIZE) < 0){
                printf("listen error
    ");
                return -1;
            }
            printf("tcp server start
    ");
        }
        else
            printf("udp server start
    ");
        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
    ");
    }
           printf("accept success
    ");
    
    /*客户端:*/
    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
    ");
    	    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
    ");
    	    return -1;
    	}
    	
    	printf("connect server success
    ");
    	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:	");
        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:	%s Not Found
    ", 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
    ",file_block_length);
                    //发送buffer中的字符串到服务器
                    if(send(client_socket,buffer,file_block_length,0)<0)
                    {
                        printf("Send File:	%s Failed
    ", file_name);
                        break;
                    }
                    bzero(buffer, BUFFER_SIZE);
                }
        }
        
        printf("Send File:	 %s To Server[%s] Finished
    ",file_name, argv[1]);
    	
        printf("The File has %d words.
    ", 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:	%s Can Not Open To Write
    ", 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!
    ", argv[1]);
                break;
            }
    
            int write_length = fwrite(buffer,sizeof(char),len,fp);
            if (write_length<len)
            {
                printf("File:	%s Write Failed
    ", file_name);
                break;
            }
            bzero(buffer,BUFFER_SIZE);    
        }
        printf("File:	%s Transfer Finished!
    ",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
    ");
            }
            printf("accept success
    ");
    
            pthread_t pid;
            if(pthread_create(&pid, NULL, process_client, &client_sock) < 0){
                printf("pthread_create error
    ");
            }
        }
    

    创建了新的线程,接下来就可以传送文件了。过程与任务二类似。

    【注:完成代码已上传至码云

    返回目录


    任务四 使用PC机和实验箱模拟客户端服务器并测试(未完成)

    返回目录


    实验感想与体会

    • 此次实验首先在Linux下实现了客户端与服务器传送文本文件,并模拟wc命令统计文本文件中的单词数。但一次只能为一个客户端提供服务的迭代网络是不现实的,因此,在任务二中创建了一个并发服务器,它为每一个客户端创建一个单独的逻辑流。这就允许服务器同时为多个客户端服务,提高了其使用价值。
    • 在完成任务三的过程中遇到了重重困难。本打算在自己笔记本上完成,也方便后续学习,但参考指导书《实验开发环境使用说明-12.04》中“解决上网与本地网络调试冲突”的相关讲解完成配置后,实验箱与主机仍然无法ping通。想到之前实验一在实验室的PC机上完成得比较顺利,于是改用实验室的PC机,但找不到正确的端口,更换了PC机和实验箱后仍然解决不了,只能作罢。
    • 近期学习效率低下,每周一章的任务常常不能按时完成,总需要后期抽时间补充。回想上学期学习Java时,虽然也是一周学习一章,但并没有觉得如此吃力,花十几个小时就能完成当周的任务。尤其这次实验结束,花费大量时间也没有解决问题,心情失落的同时,不禁怀疑自己的自学能力。且不说与老师的要求相差甚远,这种学习效率与状态连自己都不能接受。课本内容较上学期更难更深是一方面,但更多的是自身的问题。比如没有合理分配时间,做到门门课程兼顾;比如将精力浪费在一些无关紧要的事情上,等等。
    • 课下了解到,不少同学也遇到了类似的问题。能及时赶上老师的进度还好,一旦某一阵略有松懈没跟上节奏,就容易产生恶性循环,落下越来越多的内容。老师一直以来采取的都是自学为主,教学为辅的方式,这一点无可厚非,我认为自学能力可以让人终生受益。不怕自学的过程中遇到困难,怕就怕在总是遇到无法解决的困难而逐渐放弃。
    • 前几天不慎扭伤了脚,不能在寝室、主楼和图书馆之间随意跑的日子还真不适应,只能躺着休息,因此对好多任务都心有余而力不足...多亏朋友们悉心的照顾还有及时涂药,脚在一天天康复,落下的事情也要一点点补回来。那么,就先从努力按时完成本周学习任务开始吧:)

    返回目录


    参考资料

    返回目录

  • 相关阅读:
    个人常用Git操作记录
    JavaScript引擎基本原理:Shapes和Inline Caches
    Promise.then(a, b)与Promise.then(a).catch(b)问题详解
    p标签中的文本换行
    Vue初始化
    关于vue的源码调试
    关于extjs表单布局的几种方式
    sublime text3安装、注册及常用插件
    chrome扩展程序开发
    关于Git的简单使用
  • 原文地址:https://www.cnblogs.com/Vivian517/p/7832469.html
Copyright © 2011-2022 走看看