20201220蔡笃俊《信息安全系统设计与实现》第十三章学习笔记

一、任务内容

  • 自学教材第13章,提交学习笔记(10分)
  • 知识点归纳以及自己最有收获的内容 (3分)
  • 问题与解决思路(2分)
  • 实践内容与截图,代码链接(3分)
  • ...(知识的结构化,知识的完整性等,提交markdown文档,使用openeuler系统等)(2分)

二、知识点归纳

本章论述了TCP/IP和网络编程,分为两个部分。
第一部分论述了TCP/IP协议及其应用,具体包括TCP/IP栈、IP地址、主机名、DNS、IP数据包和路由器;介绍了TCP/P网络中的UDP和TCP协议、端口号和数据流;阐述了服务器-客户机计算模型和套接字编程接口;通过使用UDP和TCP套接字的示例演示了网络编程。第一个编程项目可实现一对通过互联网执行文件操作的TCP服务器–客户机,可让用户定义其他通信协议来可靠地传输文件内容。
本章的第二部分介绍了Web和CGI编程,解释了HTTP编程模型、Web页面和Web浏览器;展示了如何配置Linux HTTPD服务器来支持用户Web页面、PHP和CGI编程;阐释了客户机和服务器端动态Web页面;演示了如何使用PHP和CGI创建服务器端动态Web页面。

(一)计算机网络知识

1. TCP/IP协议

TCP/IP协议包括ICMP、IP、telnet、udp等协议,是利用IP进行通信时所必须用到的协议群的统称。

2. IP主机和IP地址

主机是支持TCP/IP 协议的计算机或设备。每个主机由一个32位的IP地址来标识。为了方便起见,32位的P地址号通常用点记法表示,例如:134.121.64.1,其中各个字节用点号分开。主机也可以用主机名来表示,如dns1.eec.wsu.edu。IP地址分为两部分,即 NetworkID字段和HostID字段。根据划分,IP地址分为A~E类。例如,一个B类P地址被划分为一个16位NetworkID,其中前2位是10,然后是一个16位的HostID字段。发往P地址的数据包首先被发送到具有相同networkID 的路由器。路由器将通过HostID将数据包转发到网络中的特定主机。每个主机都有一个本地主机名localhost,默认P地址为127.0.0.1。本地主机的链路层是一个回送虚拟设备,它将每个数据包路由回同一个 localhost。

3. IP协议

用于在IP主机之间发送/接收数据包。IP尽最大努力运行。IP主机只向接收主机发送数据包,但它不能保证数据包会被发送到它们的目的地,也不能保证按顺序发送。

4. IP数据包

由IP头、发送方地址和接收方I地址以及数据组成。每个数据包的大小最大为64KB。IP头包含有关数据包的更多信息,例如数据包的总长度、数据包使用TCP还是UDP、生存时间(TTL)计数、错误检测的校验和等。

6. 路由器

是接收和转发数据包的特殊IP主机。一个IP数据包可能会经过许多路由器,或者跳跃到达某个目的地。每个IP包在IP报头中都有一个8位生存时间(TTL)计数,其最大值为255。在每个路由器上,TTL会减小1。如果TTL减小到0,而包仍然没有到达目的地,则会直接丢弃它。这可以防止任何数据包在IP网络中无限循环。

7. UDP

在IP上运行,用于发送/接收数据报。与IP类似,UDP不能保证可靠性,但是快速高效。ping是一个向目标主机发送带时间戳UDP包的应用程序。接收到一个pinging数据包后,目标主机将带有时间戳的UDP包回送给发送者,让发送者可以计算和显示往返时间。如果目标主机不存在或宕机,当TTL减小为0时,路由器将会丢弃pinging UDP数据包。在这种情况下,用户会发现目标主机没有任何响应。用户可以尝试再次ping,或者断定目标主机宕机。

8. TCP

是一种面向连接的协议,用于发送/接收数据流。TCP也可在IP上运行,但它保证了可靠的数据传输。通常,UDP类似于发送邮件的USPS,而TCP类似于电话连接。

9. 端口编号

端口号是分配给应用程序的唯一无符号短整数。要想使用UDP或TCP,应用程序(进程)必须先选择或获取一个端口号。前1024个端口号已被预留。其他端口号可供一般使用。应用程序可以选择一个可用端口号,也可以让操作系统内核分配端口号。

10. 网络和主机字节序

计算机可以使用大端字节序,也可以使用小端字节序。在互联网上,数据始终按网络序排列,这是大端。在小端机器上,例如基于Intel x86的PC,htons()、htonl()、ntohs()、ntohl()等库函数,可在主机序和网络序之间转换数据。例如,PC中的端口号1234按主机字节序(小端)是无符号短整数。必须先通过htons(1234)把它转换成网络序,才能使用。相反,从互联网收到的端口号必须先通过ntohs(port)转换为主机序。

(二)套接字API

在网络编程中,TCP/IP的用户界面是通过一系列C语言库函数和系统调用来实现的,这些函数和系统调用统称为套接字API((Rago1993;Stevens等2004)。为了使用套接字 API,我们需要套接字地址结构,它用于标识服务器和客户机。netdbh和sys/socketh中有套接字地址结构的定义。

1.套接字地址

struct sockaddr_in {
sa_family_t sin_family; // AF_INET for TCP/IP
in port_t sinport; // port number 
struct in_addr sin_addr; // IP address
};
struct in_addr{ //internet address 
uint32_t s_addr; // IP address in network byte order 
};


在套接字地址结构中

  • TCP/IP网络的sin family始终设置为AFINET。
  • sin_port包含按网络字节顺序排列的端口号。
  • sin_addr是按网络字节顺序排列的主机IP地址。

2. 套接字API

  • int套接字 (int域 ,int类型,int协议)
  • int bind(int sockfd,struct sockaddr *addr,socklen_t addrlen)
  • UDP套接字
    • UDP套接字使用sendto()/recvfrom0)来发送/接收数据报。
  • TCP套接字
    • 在创建套接字并将其绑定到服务器地址之后,TCP服务器使用listen()和accept()来接收来自客户机的连接

三、实践过程与截图

socket链接

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
    
#define MAXLINE 256
#define PORT 7777
void sys_err(char *msg)
{
    perror(msg);
    exit(-1);
}
int main(int argc , char **argv)
{
    int sockFd,n;
    char recvLine[MAXLINE];
    struct sockaddr_in servAddr;
    if (argc != 2) 
    {
        sys_err("usage: a.out <IPaddress>");
    }
    
    sockFd=socket(AF_INET,SOCK_STREAM,0);

    memset(&servAddr,0,sizeof(servAddr));
        
    servAddr.sin_family = AF_INET;
    servAddr.sin_port = htons(PORT);
    if (inet_pton(AF_INET,argv[1],&servAddr.sin_addr) <= 0) 
    {
        sys_err("inet_pton error");
    }
        
    connect(sockFd,(struct sockaddr *)&servAddr,sizeof(servAddr));
    
    
    while((n=read(sockFd,recvLine,MAXLINE)) >0 )
    {
        recvLine[n] = '\0';
        if(fputs(recvLine,stdout) == EOF)
        {
            sys_err("fputs error");
        }
    }
    if(n <0)
    {
            sys_err("read error");
    }
        return 0;
}


socket通信:

服务器端代码:

点击查看代码
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include "aes_options.h" //add

int main()
{
	int server_fd;
	int client_fd;
	int len;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	int sin_size;
	char buffer[BUFSIZ];
	// printf("%d",BUFSIZ);
	memset(&server_addr, 0, sizeof(server_addr)); // initialize struct
	memset(&server_addr, 0, sizeof(client_addr));

	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = INADDR_ANY;
	server_addr.sin_port = htons(9000);

	if ((server_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) // create server socket
	{
		perror("socket create failed");
		return 1;
	}

	if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) < 0) // bind info on server socket
	{
		perror("bind failed");
		return 1;
	}

	listen(server_fd, 5); // listen port 9000

	sin_size = sizeof(struct sockaddr_in);

	if ((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &sin_size)) < 0)
	{
		perror("accept failed");
		return 1;
	}

	printf("accept client %s\n", inet_ntoa(client_addr.sin_addr));
	len = send(client_fd, "Welcome to my server\n", 21, 0);

	while ((len = recv(client_fd, buffer, BUFSIZ, 0)) > 0)
	{

		char *decryto_string = NULL;		   // add
		decrypt(buffer, &decryto_string, len); // add
		printf("%s \n", decryto_string);
		if (send(client_fd, decryto_string, len, 0) < 0) // modified
		{
			perror("send failed");
			return 1;
		}
	}

	close(client_fd);
	close(server_fd);

	return 0;
}

客户端代码:
点击查看代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include "aes_options.h"           //add

int main()
{
    int len;
    int client_sockfd;  
    struct sockaddr_in server_addr;
    char buffer[BUFSIZ];
    char *encrypt_string = NULL;
    memset(&server_addr, 0, sizeof(server_addr));

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    server_addr.sin_port = htons(9000);

    if((client_sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket create failed");
        return 1;

    }
    int username;
    int password;
    printf("enter the username:");
    scanf("%d", &username);
    printf("enter the password:");
    scanf("%d", &password);
    if (username !=2020 || password != 1220)
    {
        printf("uncorrect name!\n");
        return 1;
    }
    
    

    if(connect(client_sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) < 0)
    {
        perror("connect failed");
        return 1;
    }

    printf("connect to server\n");
    len = recv(client_sockfd, buffer, BUFSIZ, 0);

    buffer[len] = '\0';
    printf("%s", buffer);

    while(1)
    {
        printf("enter a data:");
        scanf("%s", buffer);
        if(!strcmp(buffer,"quit"))
            break;
        int encrypt_length = encrypt(buffer, &encrypt_string);     //add
        len = send(client_sockfd, encrypt_string, encrypt_length, 0); //add
        len = recv(client_sockfd, buffer, BUFSIZ, 0);
        buffer[len] = '\0';
        printf("recived:%s \n", buffer);
    }

    close(client_sockfd);
    printf("bye");


    return 0;
}

/*char username;
    int password;
    printf("enter the username:");
    scanf("%s", &username);
    printf("enter the password:");
    scanf("%d", &password);
    if (username != "cdj" || password != 20201220)
    {
        printf("uncorrect name!\n");
        return 1;
    }
    */
aes128加密:
点击查看代码
#include <stdio.h>
#include <openssl/aes.h>
#include <stdlib.h>
#include <string.h>

int encrypt(char *input_string, char **encrypt_string)
{
    AES_KEY aes;
    unsigned char key[AES_BLOCK_SIZE]; // AES_BLOCK_SIZE = 16
    unsigned char iv[AES_BLOCK_SIZE];  // init vector
    unsigned int len;                  // encrypt length (in multiple of AES_BLOCK_SIZE)
    unsigned int i;
    // set the encryption length
    len = 0;
    if ((strlen(input_string) + 1) % AES_BLOCK_SIZE == 0)
    {
        len = strlen(input_string) + 1;
    }
    else
    {
        len = ((strlen(input_string) + 1) / AES_BLOCK_SIZE + 1) * AES_BLOCK_SIZE;
    }
    // Generate AES 128-bit key
    for (i = 0; i < 16; ++i)
    {
        key[i] = 32 + i;
    }
    // Set encryption key
    for (i = 0; i < AES_BLOCK_SIZE; ++i)
    {
        iv[i] = 0;
    }
    if (AES_set_encrypt_key(key, 128, &aes) < 0)
    {
        fprintf(stderr, "Unable to set encryption key in AES\n");
        exit(0);
    }
    // alloc encrypt_string
    *encrypt_string = (unsigned char *)calloc(len, sizeof(unsigned char));
    if (*encrypt_string == NULL)
    {
        fprintf(stderr, "Unable to allocate memory for encrypt_string\n");
        exit(-1);
    }
    // encrypt (iv will change)
    AES_cbc_encrypt(input_string, *encrypt_string, len, &aes, iv, AES_ENCRYPT);
    return len;
}
void decrypt(char *encrypt_string, char **decrypt_string, int len)
{
    unsigned char key[AES_BLOCK_SIZE]; // AES_BLOCK_SIZE = 16
    unsigned char iv[AES_BLOCK_SIZE];  // init vector
    AES_KEY aes;
    int i;
    // Generate AES 128-bit key
    for (i = 0; i < 16; ++i)
    {
        key[i] = 32 + i;
    }
    // alloc decrypt_string
    *decrypt_string = (unsigned char *)calloc(len, sizeof(unsigned char));
    if (*decrypt_string == NULL)
    {
        fprintf(stderr, "Unable to allocate memory for decrypt_string\n");
        exit(-1);
    }
    // Set decryption key
    for (i = 0; i < AES_BLOCK_SIZE; ++i)
    {
        iv[i] = 0;
    }
    if (AES_set_decrypt_key(key, 128, &aes) < 0)
    {
        fprintf(stderr, "Unable to set decryption key in AES\n");
        exit(-1);
    }
    // decrypt
    AES_cbc_encrypt(encrypt_string, *decrypt_string, len, &aes, iv,
                    AES_DECRYPT);
}

openssl加密所依赖的头文件:
点击查看代码
#ifndef _ASE_H_
#define _ASE_H_
int encrypt(char *input_string, char **encrypt_string);
void decrypt(char *encrypt_string, char **decrypt_string, int len);
#endif

四、问题与解决思路

问题:在网络编程过程中,服务器端和客户端的套接字有什么区别?
解决:据网上博客的答案,两种方式差别不大,并没有本质的区别,建议主控机器作为服务器,其它作为客户端。原因在于,监听需要一个固定的端口,相比之下在一台机器上保证一个端口不冲突,远比在众多机器当中保证端口不冲突来得容易。

posted @ 2022-11-01 12:10  acacacac  阅读(75)  评论(0编辑  收藏  举报