• 20191325第十三章学习笔记


    第十三章 TCP/IP和网络编程

    一、梗概

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

    二、知识点归纳

    1、计算机网络知识

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

    • 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。

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

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

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

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

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

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

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

    • TCP/IP网络中的数据流 img

    2、套接字编程

    套接字编程

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

    struct sockaddr_in ( sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr;
    );
    struct in_addr {
    uint32_t s_addr;
    • 在套接字地址结构中,TCP/IP 网络的 sin_family 始终设置为 AF_INET,sm_port包含按网络字节顺序排列的端口号,sin_addr是按网络字节顺序排列的主机IP地址。

    • 服务器必须创建一个套接字,并将其与包含服务器IP地址和端口号的套接字地址绑 定。它可以使用一个固定端口号,或者让操作系统内核选择一个端口号(如果sin_port为 0)o为了与服务器通信,客户机必须创建〜个套接字。对于UPD套接字,可以将套接字绑定到服务器地址。如果套接字没有绑定到任何特定的服务器,那么它必须在后续的sendto()/ recvfromO调用中提供一个包含服务器IP和端口号的套接字地址。下面给出了socket。系统调用,它创建一个套接字并返回一个文件描述符。

    • UDP套接字使用scndto()/recvfrom()来发送/接收数据报。

    aendto(int aockfdr const void *bufr size.t len, lot flags,
    const struct sockaddr •de8t_addrf socklen_t addrlen)|
    asize_t recvfrora(int sock£d, void *buf, aiza_t len, int flags, struct sockaddr *Btc_addr, aocklen_t *addrlen};
    • 在创建套接字并将其绑定到服务器地址之后,TCP服务器使用listen()和acccpt()来接 收来自客户机的连接int Iistcn(int sockfd, int backlog);listen()将sockfd引用的套接字标记为将用于接收连入连接的套接字。backlog参数定义了等待连接的最大队列长度。

    • 建立连接后,两个TCP主机都可以使用send()/write()发送数据,并使用recv()/read。接收数据。它们唯一的区别是send和recv()中的nag参数不同,通常情况下可以将其设置为0。

    ssize_t send(int Bockfd, const void *bufr size.t len« int flags);
    write(sockfd/ void *buf, aize_t, l«n)
    S0izo_t recv(int sockfd, void *buf# size_t len, int flags);
    ssize_t read(sockfd, void *buf, size_t len);
    • 库函数gethostname(char *name, sizeof(name))在name数组中返回计算机的主机名字符串,但它可能不是用点记法表示的完整正式名称, 也不是其IP地址。库函数struct hostent *gethostbyname(void *addr, socklen_t len, int typo)可以用来获取计算机的全名及其IP地址。它会返回一个指向<netdb.h>中hostent结构体的指针:

    struct hostent {
    char *h_name;
    char **h_aliases;
    int h_addrtype;
    int h_length;
    char **h_addr_list;
    }
    #define h_addr h_addr_list[0]
    • 下面的代码段展示了如何使用gethostbyname()和getsockname()来获取服务器1P地址 和端口号(若是动态分配)。服务器必须发布其主机名或IP地址和端口号,以便客户机连接。

    char myname[64];
    struct sockaddr_in server_addr, sock_addr;
    // gethostname(), gethostbyname() gethostname(myname,64);
    struct hostent *hp = gethostbyname(myname); if (hp == 0)(
    printf("unknown host %s\n", myname); exit(1);
    // initialize the server_addr structure server_addr.sin_family = AF.INET; // for TCP/IP
    server_addr.Bin_addr・ s_addr = *(long *)hp->h_addr; server_addr.sin_port = 0; // let kernel assign port number
    // create a TCP socket
    int mysock = socket(AF_INET, SOCK_STREAM, 0);
    bind socket with server_addr bind(mysock,(struct
    to show port number assigned by kernel
    (struct sockaddr *)&name_addr, &length)/
    // show server host name and port number
    printf("hostname=%s IP=%s port=%d\n", hp->h_name,
    inet_ntoa(*(long *)hp->h_addr), ntohs(name_addr.sin_port));
    struct sockaddr_in server_addr, sock_addr;
    // 1. get server IP by name
    struct hostent *hp = gethostbyname(argv[l]);
    SERVER_IP = *(long *)hp->h_addr;
    SERVER_PORT = atoi(argv[2]);
    // 2. create TCP socket
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    // 3. fill server_addr with server IP and PORT#
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = SERVER_IP;
    server_addr.sin_port = htons(SERVER_PORT);
    // 4. connect to server
    connect(sock,(struct sockaddr *)&server_addr, sizeof(server_addr));
    • 在命令行中,根据命令的不同,路径名可以是文件或目录。有效命令有:

    mkdir:创建一个带路径名的目录。 rmdir:删除名为路径名的冃录。 rm:删除名为路径名的文件。 cd:将当前工作目录(CWD)更改为路径名。 pwd:显示当前工作目录的绝对路径名。 Is:按照与Linux的Is -1相同的格式列出当前工作目录或路径名。 get:从服务器下载路径名文件。 put:将路径名文件上传到服务器。

    • 对于传输文件内容的get/put命令,不能使用特殊ASCII字符作为文件的开始和结束标记。这是因为二进制文件可能包含ASCII代码。

    • 用标准HTML编写的Web页面都是静态的。当从服务器获取并用浏览器显示时,Web 页面的内容不会变化。要显示包含不同内容的Web页面,必须再次从服务器获取不同的 Web页面文件。

    • 动态Web页面有两种,分别称为 客户机端动态Web页面和服务器端动态Web页面。客户机端动态Web页面文件包含JavaScript写的代码,这些代码由JavaScript解释器在客户机上执行。它可以响应用户输入、时间事件等来对Web页面进行本地修改,而不需要与服务器进行任何交互。服务器端动态Web页面是真正的动态页面,因为它们是根据URL请求中的用户输入动态生成的。服务器 端动态Web页面的核心在于服务器在HTML文件中执行PHP代码,或CGI程序通过用户输入生成HTML文件的能力。

    • CGI代表通用网关接口,它是一种协议,允许Web服务器执行程序, 根据用户输入动态生成Web页面。使用CGI, Web服务器不必维护数百万个静态Web页面文件来满足客户机请求。相反,它通过动态生成Web页面来满足客户机请求。

     

    三、实践

        #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;
        }

  • 相关阅读:
    ERP/MIS开发 30道ORM问题与解答 LLBL Gen 3.x Adapter
    升级LLBL Gen 2.x项目到3.x
    软件公司为什么要加密源代码,而且是前前后后,反反复复
    ERP/MIS系统中集成命令行式的功能调用
    工作多年后才明白的.NET底层开发技术
    OSGI:从面向接口编程来理解OSGI
    幸福框架:如何阅读幸福框架的代码
    OSGI:C#如何实现简单的OSGI
    技术人生:技术之路,需要的是热情和梦想
    EHR:对人力资源信息系统的认识
  • 原文地址:https://www.cnblogs.com/2902480848sy/p/15609430.html
Copyright © 2020-2023  润新知