2017-2018-1 20155330 《信息安全系统设计基础》第8周学习总结

2017-2018-1 20155330 《信息安全系统设计基础》第8周学习总结

教材学习内容总结

  • 客户端一服务器模型中的基本操作是事务。一个客户端一服务器事务由以下四步组成。

    1. 当一个客户端需要服务时,它向服务器发送一个请求,发起一个事务。例如,当Web浏览器需要一个文件时,它就发送一个请求给Web服务器。
    2. 服务器收到请求后,解释它,并以适当的方式操作它的资源。例如,当Web服务器收到浏览器发出的请求后,它就读一个磁盘文件。
    3. 服务器给客户端发送一个响应,并等待下一个请求。例如,Web服务器将文件发送回客户端。
    4. 客户端收到响应并处理它。例如,当Web浏览器收到来自服务器的一页后,就在屏幕上显示此页。
  • 对主机而言,网络市一中I/O设备,是数据源和数据接收方。

  • 一个以太网段(Ethernet segment)包括一些电缆(通常是双绞线)和一个叫做集线器的小盒子,以太网段通常跨越一些小的区域,例如某建筑物的一个房间或者一个楼层。集线器一端接到主机的适配器,而另一端则连接到集线器的一个端口上。集线器不加分辨地将从一个端口上收到的每个位复制到其他所有的端口上。因此,每台主机都能看到每个位。

  • 每个以太网适配器都有一个全球唯一的48位地址,它存储在这个适配器的非易失性存储器上。一台主机可以发送一段位(帧)到这个网段内的其他任何主机。

  • TCP/IP实际是一个协议族,其中每一个都提供不同的功能。例如,IP协议提供基本的命名方法和递送机制,这种递送机制能够从一台因特网主机往其他主机发送包,也叫做数据报(datagram) o IP机制从某种意义上而言是不可靠的,因为,如果数据报在网络中丢失或者重复,它并不会试图恢复。UDP ( Unreliable Datagram Protocol,不可靠数据报协议)稍微扩展了IP协议,这样一来,包可以在进程间而不是在主机间传送。TCP是一个构建在IP之上的复杂协议,提供了进程间可靠的全双工(双向的)连接。

  • 使用命令hostname -i可查询主机的点分十进制地址:

  • 使用命令naslookup localhost查看域名:

  • www.baidu.com为例,查看对应IP地址:

  • 进程的优劣:

    • 优点:一个进程不会不小心覆盖另一个进程的虚拟内存。
    • 缺点:独立的地址空间使进程共享状态信息变得更加困难,为共享信息必须使用显式的IPC(进程间通信)机制。
  • 基于I/O多路复用的并发编程:

    • 编写一个echo服务器,它能对用户从标准输入键人的交互命令做出响应。在这种情况下,服务器必须响应两个互相独立的I/O事件:1>网络客户端发起连接请求,2)用户在键盘上键人命令行。先等待哪个事件呢?没有哪个选择是理想的。如果在accept中等待一个连接请求,就不能响应输人的命令。类似地,如果在read中等待一个输人命令,我们就不能响应任何连接请求。
    • 针对这种困境的一个解决办法就是I/O多路复用(I/O multiplexing)技术。基本的思路就是使用select函数,要求内核挂起进程,只有在一个或多个I/O事件发生后,才将控制返回给应用程序。
  • I/O多路复用可以用做并发事件驱动程序的基础,在事件驱动程序中,某些事件会导致流向前推进。一般的思路是将逻辑流模型化为状态机。

  • 线程执行模型

    • 多线程的执行模型在某些方面和多进程的执行模型是相似的。思考图12-12中的示例。每个进程开始生命周期时都是单一线程,这个线程称为主线程(main thread) o在某一时刻,主线程创建一个对等线程(peer thread),从这个时间点开始,两个线程就并发地运行。最后,因为主线程执行一个慢速系统调用,例如read或者sleep,或者因为被系统的间隔计时器中断,控制就会通过上下文切换传递到对等线程。对等线程会执行一段时间,然后控制传递回主线程,依次类推。
    • 在一些重要的方面,线程执行是不同于进程的。因为一个线程的上下文要比一个进程的上下文小得多,线程的上下文切换要比进程的上下文切换快得多。另一个不同就是线程不像进程那样,不是按照严格的父子层次来组织的。和一个进程相关的线程组成一个对等(线程)池,独立于其他线程创建的线程。主线程和其他线程的区别仅在于它总是进程中第一个运行的线程。对等(线程)池概念的主要影响是,一个线程可以杀死它的任何对等线程,或者等待它的任意对等线程终止。另外,每个对等线程都能读写相同的共享数据。

课堂测试及课下作业

测试3

基于socket 使用教材的csapp.h csapp.c,实现daytime(13)服务器(端口我们使用13+后三位学号)和客户端
服务器响应消息格式是
“客户端IP:XXXX
服务器实现者学号:XXXXXXXX
当前时间: XX:XX:XX”

一个客户端至少查询三次时间的截图测试截图

至少两个客户端查询时间的截图测试截图

课下作业

把课上练习3的daytime服务器分别用多进程和多线程实现成并发服务器并测试
多进程代码:

#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<strings.h>
#include<ctype.h>
#include<errno.h>
#include<sys/wait.h>
#include<errno.h>
#include <time.h>
#define PORT 13330
#define BACKLOG 1
#define MAXRECVLEN 1024
int main(int argc, char *argv[])
{
    char buf[MAXRECVLEN];
    int listenfd, connectfd;  /* socket descriptors */
    pid_t pid; 
    pid_t pids[10]; 
    struct sockaddr_in server; /* server's address information */
    struct sockaddr_in client; /* client's address information */
    socklen_t addrlen;
    /* Create TCP socket */
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        /* handle exception */
        perror("socket() error. Failed to initiate a socket");
        exit(1);
    }
 
    /* set socket option */
    int opt = SO_REUSEADDR;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    if(bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1)
    {
        /* handle exception */
        perror("Bind() error.");
        exit(1);
    }
    
    if(listen(listenfd, BACKLOG) == -1)
    {
        perror("listen() error. 
");
        exit(1);
    }
    addrlen = sizeof(client);
int i;
    for(i = 0; i< 10;i++){
        pid = fork();
        if(pid > 0){
            continue;
        }
        pids[i] = pid;
}
    while(1){
        if((connectfd=accept(listenfd,(struct sockaddr *)&client, &addrlen))==-1)
          {
            perror("accept() error. 
");
            exit(1);
          }
        struct timeval tv;
        gettimeofday(&tv, NULL);
	time_t t;
	t=time(NULL);
	printf("客户端IP: %s.  
",inet_ntoa(client.sin_addr));
	printf("服务器实现者学号:%d
",20155330);
	printf("当前时间: %s
",ctime(&t));
    }    
        int iret=-1;
        while(1)
        {
            iret = recv(connectfd, buf, MAXRECVLEN, 0);
            if(iret>0)
            {
                //printf("%s
", buf);
            }else
            {
                close(connectfd);
                break;
            }
            /* print client's ip and port */
            send(connectfd, buf, iret, 0); /* send to the client welcome message */
        }
    
    close(listenfd); /* close listenfd */
    return 0;
}

测试结果:

多线程代码:

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<sys/socket.h>
#include<sys/types.h>       //pthread_t , pthread_attr_t and so on.
#include<netinet/in.h>      //structure sockaddr_in
#include<arpa/inet.h>       //Func : htonl; htons; ntohl; ntohs
#include<assert.h>          //Func :assert
#include<string.h>          //Func :memset
#include<unistd.h>          //Func :close,write,read
#include<ctype.h>
#include<arpa/inet.h>
#include <time.h>
#define SOCK_PORT 13330
#define BUFFER_LENGTH 1024
#define MAX_CONN_LIMIT 5    //MAX connection limit

static void Data_handle(void * sock_fd);   

    struct sockaddr_in s_addr_in;
    struct sockaddr_in s_addr_client;
   int sockfd;
int main()
{
    int sockfd_server;
 
    int fd_temp;
    int client_length;

    sockfd_server = socket(AF_INET,SOCK_STREAM,0);  //ipv4,TCP
    assert(sockfd_server != -1);
    memset(&s_addr_in,0,sizeof(s_addr_in));
    s_addr_in.sin_family = AF_INET;
    s_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);  //trans addr from uint32_t host byte order to network byte order.
    s_addr_in.sin_port = htons(SOCK_PORT);          //trans port from uint16_t host byte order to network byte order.

    fd_temp = bind(sockfd_server,(struct scokaddr*)(&s_addr_in),sizeof(s_addr_in));

    if(fd_temp == -1)
    {
        fprintf(stderr,"bind error!
");
        exit(1);
    }

    fd_temp = listen(sockfd_server,MAX_CONN_LIMIT);

    if(fd_temp == -1)
    {
        fprintf(stderr,"listen error!
");
        exit(1);
    }

   while(1)
    {
        pthread_t thread_id;
        client_length = sizeof(s_addr_client);
        sockfd = accept(sockfd_server,(struct sockaddr*)(&s_addr_client),&client_length);

	time_t t = time(0); 
        char tmp[64]; 
        strftime( tmp, sizeof(tmp), "%Y/%m/%d %X %A
	", localtime(&t) ); 
	send(sockfd,tmp,strlen(tmp),0);	
	close(sockfd);	//close a file descriptor.
        if(sockfd == -1)
        {
            fprintf(stderr,"Accept error!
");
            continue;                               //ignore current socket ,continue while loop.
        }

        if(pthread_create(&thread_id,NULL,(void *)(&Data_handle),(void *)(&sockfd)) == -1)
        {
            fprintf(stderr,"pthread_create error!
");
            break;                                  //break while loop
        }
    }

    //Clear
    int ret = shutdown(sockfd_server,SHUT_WR); //shut down the all or part of a full-duplex connection.
    assert(ret != -1);

    return 0;
}

static void Data_handle(void * sock_fd)
{
    int fd = *((int *)sock_fd);
    int i_recvBytes;
    char data_recv[BUFFER_LENGTH];
    printf("服务器实现者:20155330
");
	printf("客户端IP:%s
",inet_ntoa(s_addr_client.sin_addr));	
    pthread_exit(NULL);   //terminate calling thread!
}

测试结果:

教材学习中的问题和解决过程

  • 问题1:多进程和多线程的区别
  • 问题1解决方案:进程是资源分配的最小单位,线程是CPU调度的最小单位;进程编程调试简单可靠性高,但是创建销毁开销大;线程正相反,开销小,切换速度快,但是编程调试相对复杂。

代码调试中的问题和解决过程

  • 问题1:编译多线程代码失败。
  • 问题1解决方案:输入gcc XXX.c -lpthread -o XXX进行编译。

上周考试错题总结

  • 错题1:假设用ADD指令完成C表达式t=a+b的功能,有关条件码寄存器的说法正确的是()

A.若t==0则ZF=1

B.若t<0,则CF=1

C.若t<0,则SF=1

D.若(a<0==b<0)&&(t<0 != a<0), 则OF=1

E.若(a<0==b<0)&&(t<0 != a<0), 则CF=1

F.leaq指令不影响条件码寄存器

G.cmp指令不影响条件码寄存器

  • 理解情况:ZF为零标志,当最近操作得出的结果为0时,ZF=1。当最近操作导致一个补码溢出时,OF=1。CF为进位标志,当最高位产生进位时,CF=1。t<0,SF=0。
  • 错题2:假设%rax中的值为x, %rcx中的值为y,关于leaq指令,下面正确的()

A.leaq 6(%rax), %rdx; %rdx中值为6+x

B.leaq 6(%rax), %rdx; %rdx中值为6x

C.leaq 7(%rax, %rax,8), %rdx; %rdx中值为9x

D.leaq 7(%rax, %rax,8), %rdx; %rdx中值为63x

E.leaq 7(%rax, %rax,8), %rdx; %rdx中值为15x

  • 理解情况:指令leaq 7(%rax, %rax,8), %rdx中%rdx的值为7+9x
  • 错题3:有关exec系列函数,下面说法正确的是()

A.可以用char[][] 来传递argv

B.进程调用了exec系列函数后,pid会变

C.进程调用了exec系列函数后,代码会改变。

D.system()和exec系列等价。

E.exec系列函数中带e的要传入环境变量参数

F.exec系列函数中带v的要传入环境变量参数

  • 理解情况:不能用char[][] 来传递argv,结尾的0(null)无法处理;system=fork+exec+wait;
  • 错题4:Unix/Linux中通过调用( )可以获取子进程PID。

A.getpid()

B.getppid()

C.getcpid()

D.fork()

  • 理解情况:在父进程中,fork返回子进程的PID。
  • 错题5:对于图中内存地址和寄存器的值,下面说法正确的是()
    A.%rax的值是0x100

B.(%rax)的值是0x100

C.(%rax)的值是0x104

D.(%rax)的值是0xFF

E.4(%rax)的值是0xAB

F.(%rax,%rcx,4)的值是0xAB

G.(%rax,%rcx,4)的值是0x104

  • 理解情况:(rb,ri,s) 操作数值M[R[rb]+R[ri]·s]

代码托管

结对及互评

本周结对学习情况

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 0/0 1/1 10/10
第二周 63/63 1/2 8/18
第三周 31/94 1/3 18/36
第四周 265/329 1/4 17/53
第五周 106/435 2/6 18/71
第六周 211/646 2/8 21/92
第七周 1420/2066 2/10 17/109
第八周 1061/3127 1/11 17/126

尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。

参考:软件工程软件的估计为什么这么难软件工程 估计方法

  • 计划学习时间:18小时

  • 实际学习时间:17小时

参考资料

原文地址:https://www.cnblogs.com/ashin-kl/p/7823188.html