ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

2021-2022-1-diocs-TCP/IP和网络编程

2021-11-22 19:02:27  阅读:103  来源: 互联网

标签:Web 字节 int IP TCP 2021 接字


一、任务详情

自学教材第13章,提交学习笔记(10分)

知识点归纳以及自己最有收获的内容 (3分)

问题与解决思路(2分)

实践内容与截图,代码链接(3分)

...(知识的结构化,知识的完整性等,提交markdown文档,使用openeuler系统等)(2分)

 

二、知识点总结

本章论述了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和CGI编程;阐释了客户机和服务器端动态 Web 页面;演示了如何使用PHP和 CGI创建服务器端动态Web 页面。

1.TCP/IP协议

TCP/IP(Comer 1988,2001;RFC1180 1991)是互联网的基础。TCP代表传输控制协议。IP 代表互联网协议。目前有两个版本的IP,即IPv4和IPv6。IPv4使用32位地址,IPv6则使用128位地址。本节围绕IPv4 进行讨论,它仍然是目前使用最多的IP版本。TCP/IP 的组织结构分为几个层级,通常称为TCP/IP堆栈。如图所示为 TCP/IP 的各个层级以及每一层级的代表性组件及其功能。

进程与主机之间的传输层或其上方的数据传输只是逻辑传输。实际数据传输发生在互联网(IP)和链路层,这些层将数据包分成数据帧,以便在物理网络之间传输。下图所示为 TCP/IP 网络中的数据流路径。

 2.IP主机和IP地址

IP地址分为两部分,即 NetworkID 字段和HostID字段。根据划分,IP 地址分为A~E 类。例如,一个B类IP地址被划分为一个16位NetworkID,其中前2位是10,然后是一个16位的 HostID字段。发往IP地址的数据包首先被发送到具有相同 networkID的路由器。路由器将通过 HostID 将数据包转发到网络中的特定主机。每个主机都有一个本地主机名localhost,默认 IP地址为 127.0.0.1。本地主机的链路层是一个回送虚拟设备,它将每个数据包路由回同一个localhost。这个特性可以让我们在同一台计算机上运行TCP/IP 应用程序,而不需要实际连接到互联网。

3.IP协议

IP协议用于在 IP主机之间发送/接收数据包。IP尽最大努力运行。IP 主机只向接收主机发送数据包,但它不能保证数据包会被发送到它们的目的地,也不能保证按顺序发送。这意味着IP 并非可靠的协议。必要时,必须在IP 层的上面实现可靠性。下图所示是IP头格式:

4.UDP/TCP

 UDP(用户数据报协议)(RFC768 1980;Comer 1988)在IP上运行,用于发送/接收数据报。与IP类似,UDP不能保证可靠性,但是快速高效。它可用于可靠性不重要的情况。

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

5.端口编号

应用程序 =(主机 IP,协议,端口号)
其中,协议是TCP或 UDP,端口号是分配给应用程序的唯一无符号短整数。要想使用UDP或 TCP,应用程序(进程)必须先选择或获取一个端口号。前1024个端口号已被预留。其他端口号可供一般使用。应用程序可以选择一个可用端口号,也可以让操作系统内核分配端口号。下图给出了在传输层中使用TCP 的一些应用程序及其默认端口号。

 6.TCP/Ip网络中的数据流

在图中,应用程序层的数据被传递到传输层,传输层给数据添加一个TCP或UDP 报头来标识使用的传输协议。合并后的数据被传递到IP 网络层,添加一个包含 IP地址的IP 报头来标识发送和接收主机。然后,合并后的数据再被传递到网络链路层,网络链路层将数据分成多个帧,并添加发送和接收网络的地址,用于在物理网络之间传输。IP地址到网络地址的映射由地址解析协议(ARP)执行(ARP1982)。在接收端,数据编码过程是相反的。每一层通过剥离数据头来解包接收到的数据、重新组装数据并将数据传递到上一层。发送主机上的应用程序原始数据最终会被传递到接收主机上的相应应用程序。

7.套接字编程

(1)套接字地址

struct sockaddr_in {
sa_family_t sin_family; // AF_INET for TCP/IP
// port number
in_port_t sin_port;
struct in_addr sin_addr;// IP address );
// internet address struct in_addr {
// IP address in network byte order
s_addr;
uint32_t
);

在套接字地址结构中,
● TCP/IP 网络的 sin_family 始终设置为 AF_INET。

● sin_port包含按网络字节顺序排列的端口号。

●sin addr是按网络字节顺序排列的主机IP地址。

(2)套接字API

服务器必须创建一个套接字,并将其与包含服务器IP 地址和端口号的套接字地址绑定。它可以使用一个固定端口号,或者让操作系统内核选择一个端口号(如果 sin port为0)。为了与服务器通信,客户机必须创建一个套接字。对于UPD套接字,可以将套接字绑定到服务器地址。如果套接字没有绑定到任何特定的服务器,那么它必须在后续的 sendto()/recvfrom()调用中提供一个包含服务器IP 和端口号的套接字地址。

(3)TCP/UDP套接字

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

ssize_t sendto(int soCkfd,const void *buf,size_t len,int flags,
const struct sockaddr *dest_addr,socklen_t addrlen);
ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,
struct sockaddr *src_addr,socklen_t *addrlen);

在创建套接字并将其绑定到服务器地址之后,TCP服务器使用listen()和 accept()来接收来自客户机的连接
int listen(int sockfd, int backlog);
listen()将 sockfd引用的套接字标记为将用于接收连人连接的套接字。backlog 参数定义了等待连接的最大队列长度。
int accept(int sockfd, struct sockaddr *addr, socklen t *addrlen);

(4)通用套接字地址结构

通用套接字地址结构:sockaddr

struct sockaddr
{
uint8_t           sa_len;
sa_family_t       sa_family;
char              sa_data[14];
};
IPv6套接字地址结构

IPv6套接字地址结构在<netinet/in.h>头文件中定义

struct in6_addr
{
unit8_t s6_add[16];

};

#define SIN6_LEN
struct sockaddr_in6
{ 
uint8_t           sin6_len;
sa_family_t       sin6_family;
in_port_t         sin6_port;
uint32_t          sin6_flowinfo;
struct in6_addr   sin6_addr;
uint32_t          sin6_scope_id;
};

新的struct sockaddr_storage足以容纳系统所支持的任何套接字地址结构。sockaddr_storage结构在<netinet/in.h>头文件中定义

struct sockaddr_storage
{
uint8_t       ss_len;
sa_family_t   ss_family;
};

8.字节排序函数

小端和大端(内存中存储两个字节有两种方法)

小端(little-endian):将低序字节存储在起始地址

大端(big-endian):将高序字节存储在起始地址

主机字节序:某个给定系统所用的字节序

输出字节序的程序:

#iclude"unp.h"
int main(int argc,char **argv)
{
  union{
     short   s;
     char    c[sizeof(short)];
        }un;
un.s=0x0102;
printf("%s:",CUP_VENDOR_OS);
if(sizeof(short)==2){
    if(un.c[0]==1&&un.c[1]==2)
            printf("big-endian\n");
    else if (un.c[0]==2&&un.c[1]==1)
            printf("little-endian\n");
    else
            printf("unknown\n");
}else
      printf("sizeof(short)=%d\n",sizeof(short));
exit(0);
}

9.字节操纵函数

bzero:bzero把目标字节串指定数目的字节置为0。我们常用该函数把一个套接字地址结构初始化为0.

bocpy:指定数目的字节从源字节串移动到目标字节串。

bcmp:比较两个任意的字节串,若相同返回值为0,否则返回值为非0.

memset:把目标字节串指定数目的字节置为c。

mencmp:比较两个任意的字符串,若相同为0,否则返回一个非0值,是大于0还是小于0则取决于第一个不等的字节。

支持IPv4的inet_pton函数的简单定义:

int inet_pton(int family,const char *strptr,void *addrptr)
{
    if(family==AF_INET)
    {
         struct in_addr  in_val;
    if(inet_aton(strptr,&in_val))
    {
        memcpy(addrptr,&in_val,sizeof(struct int_addr));
        return(1);
    }
    return(0);
    }
    errno=EAFNOSUPPROT;
    return(-1);
}

 

三、最有收获的内容

Web和CGI编程

万维网(WWW)或 Web 是互联网上的资源和用户组合,它使用超文本传输协议(HTTP)(RFC2616 1999)进行信息交换。自 20世纪 90年代初问世以来,随着互联网能力的不断扩展,Web 已经成为世界各地人们日常生活中不可或缺的一部分。因此,对于计算机科学的学生来说,了解这项技术非常重要。在本节中,我们将介绍 HTTP和Web编程的基础知识。Web 编程通常包括Web开发中涉及的编写、标记和编码,其中包括Web 内容、Web 客户机和服务器脚本以及网络安全。狭义上,Web编程指的是创建和维护 Web 页面。Web编程中最常用的语言是HTML、XHTML、JavaScript、Perl5和 PHP。

Http编程模型

HTTP是一种基于服务器-客户机的协议,用于互联网上的应用程序。它在TCP上运行,因为它需要可靠的文件传输。图13.10所示为HTTP编程模型。

在HTTP 中,客户机可发出多个URL,将请求发送到不同的HTTP服务器。客户机与特定服务器保持永久连接不但没有必要,也不可取。客户机连接到服务器只是为了发送请求,发送完毕后会关闭连接。同样,服务器连接到客户机也只是为了发送应答,发送完毕后会再次关闭连接。每个请求或应答都需要一个单独的连接。这意味着 HTTP是一种无状态协议,因为在连续的请求或应答之间不需要维护任何信息。自然,这将导致大量系统开销和效率低下。为弥补这一缺乏状态信息的问题,HTTP 服务器和客户机可使用 cookie 来提供和维护它们之间的一些状态信息。

Web界面

Web 页面是用HTML标记语言编写的文件。Web文件通过一系列HTML元素指定Web 页面的布局,可在 Web 浏览器上解释和显示。常用的Web 浏览器有Internet Explorer、Firefox、Google Chrome 等。创建 Web 页面相当于使用HTML 元素作为构建块创建文本文件。与其说它是编程,不如说是文书类工作。因此,我们不讨论如何创建Web 页面。相反,我们将只使用一个示例 HTML文件来说明Web 页面的本质。下面给出了一个简单的HTML Web 文件。

CGI编程

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

在 CGI编程模型中,客户机发送一个请求,该请求通常是一个HTML表单,包含供服务器执行的 CGI程序的输入和名称。在接收到请求后,httpd服务器会派生一个子进程来执行 CGI程序。CGI程序可以使用用户输入来查询数据库系统,如 MySQL,从而根据用户输入生成 HTML 文件。当子进程结束时,httpd服务器将生成的HTML 文件发送回客户机。CGI 程序可用任何编程语言编写,如 C语言、sh 脚本和Perl。

 

四、实践内容(截图、代码链接)

代码链接:

https://gitee.com/two_thousand_and_thirteen/zx-code/issues/I4J8R1

#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){

</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> sockFd,n;
</span><span style="color: #0000ff;">char</span><span style="color: #000000;"> recvLine[MAXLINE];
</span><span style="color: #0000ff;">struct</span><span style="color: #000000;"> sockaddr_in servAddr;

</span><span style="color: #0000ff;">if</span> (argc != <span style="color: #800080;">2</span><span style="color: #000000;">) {
    sys_err(</span><span style="color: #800000;">"</span><span style="color: #800000;">usage: a.out &lt;IPaddress&gt;</span><span style="color: #800000;">"</span><span style="color: #000000;">);
}

sockFd</span>=socket(AF_INET,SOCK_STREAM,<span style="color: #800080;">0</span><span style="color: #000000;">);


memset(</span>&amp;servAddr,<span style="color: #800080;">0</span>,<span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(servAddr));

servAddr.sin_family </span>=<span style="color: #000000;"> AF_INET;
servAddr.sin_port </span>=<span style="color: #000000;"> htons(PORT);
</span><span style="color: #0000ff;">if</span> (inet_pton(AF_INET,argv[<span style="color: #800080;">1</span>],&amp;servAddr.sin_addr) &lt;= <span style="color: #800080;">0</span><span style="color: #000000;">) {

    sys_err(</span><span style="color: #800000;">"</span><span style="color: #800000;">inet_pton error</span><span style="color: #800000;">"</span><span style="color: #000000;">);
}

connect(sockFd,(</span><span style="color: #0000ff;">struct</span> sockaddr *)&amp;servAddr,<span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(servAddr));


</span><span style="color: #0000ff;">while</span>((n=read(sockFd,recvLine,MAXLINE)) &gt;<span style="color: #800080;">0</span><span style="color: #000000;"> ){
    recvLine[n] </span>= <span style="color: #800000;">'</span><span style="color: #800000;">\0</span><span style="color: #800000;">'</span><span style="color: #000000;">;
    </span><span style="color: #0000ff;">if</span>(fputs(recvLine,stdout) ==<span style="color: #000000;"> EOF){
        sys_err(</span><span style="color: #800000;">"</span><span style="color: #800000;">fputs error</span><span style="color: #800000;">"</span><span style="color: #000000;">);
    }
}
</span><span style="color: #0000ff;">if</span>(n &lt;<span style="color: #800080;">0</span><span style="color: #000000;">){
    sys_err(</span><span style="color: #800000;">"</span><span style="color: #800000;">read error</span><span style="color: #800000;">"</span><span style="color: #000000;">);
}
</span><span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;

}

代码运行截图:

 

 

五、问题与解决思路

Linux系统下进行套接字编程中存在五大隐患都有什么?

隐患 1.忽略返回状态

第一个隐患很明显,但它是开发新手最容易犯的一个错误。如果您忽略函数的返回状态,当它们失败或部分成功的时候,您也许会迷失。反过来,这可能传播错误,使定位问题的源头变得困难。

捕获并检查每一个返回状态,而不是忽略它们。考虑清单 1 显示的例子,一个套接字 send 函数。

1. 忽略 API 函数返回状态

int status, sock, mode;

  / Create a new stream (TCP) socket /sock =

  socket( AF_INET, SOCK_STREAM, 0 );

  ...status = send( sock, buffer, buflen, MSG_DONTWAIT );

  if (status == -1) {/ send failed /printf( "send failed: %s\n",?

  strerror(errno) );

  } else {/ send succeeded -- or did it? /}

清单 1 探究一个函数片断,它完成套接字 send 操作(通过套接字发送数据)。函数的错误状态被捕获并测试,但这个例子忽略了 send 在无阻塞模式(由 MSG_DONTWAIT 标志启用)下的一个特性。

send API 函数有三类可能的返回值:如果数据成功地排到传输队列,则返回 0。 如果排队失败,则返回 -1(通过使用 errno 变量可以了解失败的原因)。 如果不是所有的字符都能够在函数调用时排队,则最终的返回值是发送的字符数。

由于 send 的 MSG_DONTWAIT 变量的无阻塞性质,函数调用在发送完所有的数据、一些数据或没有发送任何数据后返回。在这里忽略返回状态将导致不完全的发送和随后的数据丢失。

隐患 2.对等套接字闭包

UNIX 有趣的一面是您几乎可以把任何东西看成是一个文件。文件本身、目录、管道、设备和套接字都被当作文件。这是新颖的抽象,意味着一整套的 API 可以用在广泛的设备类型上。

考虑 read API 函数,它从文件读取一定数量的字节。read 函数返回读取的字节数(最高为您指定的最大值);或者 -1,表示错误;或者 0,如果已经到达文件末尾。

如果在一个套接字上完成一个 read 操作并得到一个为 0 的返回值,这表明远程套接字端的对等层调用了 close API 方法。该指示与文件读取相同 —— 没有多余的数据可以通过描述符读取(参见 清单 2)。

2.适当处理 read API 函数的返回值

 int sock, status;sock = socket( AF_INET, SOCK_STREAM, 0 );

  ...status = read( sock, buffer, buflen );

  if (status > 0) {/ Data read from the socket /} else if (status == -1)

  {/ Error, check errno, take action... /} else if (status ==

  0) {/ Peer closed the socket, finish the close /close( sock );

  / Further processing... /}

同样,可以用 write API 函数来探测对等套接字的闭包。在这种情况下,接收 SIGPIPE 信号,或如果该信号阻塞,write 函数将返回 -1 并设置 errno 为 EPIPE。

隐患 3.地址使用错误(EADDRINUSE)

 3.使用 SO_REUSEADDR 套接字选项避免地址使用错误

  int sock, ret, on;struct sockaddr_in servaddr;

  / Create a new stream (TCP) socket /sock =

  socket( AF_INET, SOCK_STREAM, 0 ):

  / Enable address reuse /on = 1;

  ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR,

  &on, sizeof(on) );/* Allow connections to

  port 8080 from any available interface

  */memset( &servaddr, 0, sizeof(servaddr) );

  servaddr.sin_family = AF_INET;

  servaddr.sin_addr.s_addr = htonl( INADDR_ANY );

  servaddr.sin_port = htons( 45000 );

  /* Bind to the address (interface/port)

  */ret = bind( sock, (struct sockaddr *)&servaddr, sizeof(servaddr) );

在应用了 SO_REUSEADDR 选项之后,bind API 函数将允许地址的立即重用。

隐患 4.发送结构化数据

套接字是发送无结构二进制字节流或 ASCII 数据流(比如 HTTP 上的 HTTP 页面,或 SMTP 上的电子邮件)的完美工具。但是如果试图在一个套接字上发送二进制数据,事情将会变得更加复杂。

比如说,您想要发送一个整数:您可以肯定,接收者将使用同样的方式来解释该整数吗?运行在同一架构上的应用程序可以依赖它们共同的平台来对该类型的数据做出相同的解释。但是,如果一个运行在高位优先的 IBM PowerPC 上的客户端发送一个 32 位的整数到一个低位优先的 Intel x86,那将会发生什么呢?字节排列将引起不正确的解释。

隐患 5.TCP 中的帧同步假定

TCP 不提供帧同步,这使得它对于面向字节流的协议是完美的。这是 TCP 与 UDP(User Datagram Protocol,用户数据报协议)的一个重要区别。UDP 是面向消息的协议,它保留发送者和接收者之间的消息边界。TCP 是一个面向流的协议,它假定正在通信的数据是无结构的。

5.tcpdump 工具的用法模式

  Display all traffic on the eth0 interface for

  the local host$ tcpdump -l -i eth0Show all traffic

  on the network coming from or going

  to host plato$ tcpdump host platoShow all HTTP traffic

  for host camus$ tcpdump host camus and (port http)View

  traffic coming from or going

  to TCP port 45000 on the local host$ tcpdump tcp port 45000

tcpdump 和 tcpflow 工具有大量的选项,包括创建复杂过滤表达式的能力。查阅下面的 参考资料 获取更多关于这些工具的信息。

标签:Web,字节,int,IP,TCP,2021,接字
来源: https://www.cnblogs.com/zhangxiaoblog/p/15588773.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有