CASPP Chapter 11 Network Programming 学习笔记(不定期更新)

CASPP Chapter 11 Network Programming 学习笔记(不定期更新)

学习进度

  • Socket 套接字: 11.3.3,11.4
  • Web Servers: 11.5

Socket套接字 (11.3.3, 11.4)

学习契机

学习Java内存马之前,想再看看Tomcat服务器的总体框架,其中有提到socket的内容,之前学nc的时候,也提到了socket的方法,感觉不是特别熟悉,看来还得是CSAPP!

什么是socket套接字?

什么是socket套接字?我们经常能在网络编程,客户端与服务器的通信这样的话题中聊到socket套接字,以及其相关的方法,那么存在即有意义,我们就一起来看看什么是scoket套接字以及为什么需要socket套接字。

我们都熟悉OSI七层模型或者TCP/IP模型,知道在双方通信的过程中,数据在应用层或者高层发出之后,需要经过层层打包封装后再通过物理层发送给对方,之后再层层解析拆包,最终使接收方获取到真实的数据。以上场景是非常宏观的以及抽象的描述,简单来说,socket套接字就是其中一个环节的具体实现!

我们知道在Internet通信的双方是可以建立连接的,这样的连接是 point-to-point 点对点的,而这个点 endpoint 就是 socket ,所以我们现在可以说socket就是用来帮助通信的双方建立连接,接收,以及发送数据的。

那我们刚才将socket是通信的具体实现又是连接的端点,那么每一个socket是首先是由 socket address 即套接字地址唯一标识的(待确认),其中:

socket address又两部分组成(address:port):

  1. IP address
  2. Port

那么一个连接,就是由一对 socket address 组成:(cliaddr:cliport, servaddr:servport) (客户端地址:客户端端口,服务端地址:服务端端口)

其中客户端的端口是由 kernel 自动分配的,我们叫做 ephemeral port (临时端口?);同时服务端的端口就是预留端口用来表明特定的服务,比如Web服务器使用80端口,邮箱服务器使用25端口,相信大家应该对此都已经非常熟悉了。

Figure 1: 一对socket address (address:port)唯一标识一个连接

Figure 1: 一对socket address (address:port)唯一标识一个连接

socket套接字具体的功能

在上一小节中我们提到了socket可以帮助建立连接以及接收和发送数据,那么具体是如何实现的呢?

说到实现,我们就可以想到Java中的实现接口,在这里也是一样,socket套接字其实就是一个Interface 接口,那么在不同的编程语言之间都用不同的实现以及API,我们在这里就从socket interface来介绍一下socket的功能

Figure 2: Socket Interface

Figure 2: Socket Interface

观察上图我们可以发现,通信的双方被定义为 Client 客户端以及 Server 服务端,同时

  1. open_clientfd被定义为:getaddrinfo -> socket -> connect
  2. open_listenfd被定义为:getaddrinfo -> socket -> bind -listen

在Section 10.2的学习中,我们知道了 socket 也是Linux中的一种文件类型,我们也知道了打开文件意味着分配到一个 descriptor 描述符,由此我们可以理解为,打开一个可以用于通信的 socket 文件需要满足以上步骤,接下来我们就来具体看看这些方法

getaddrinfo(… …)
用于帮助转换hostnames, host addresses, service names, and port numbers等参数的结构,以供后面的方法使用,这里暂时不展开了
socket(… …)
无论是客户端还是服务端,都需要先用 socket 方法来完成打开一个文件(详见10.1的opening files操作),同时返回一个 socket decriptor socket描述符来供应用使用
  • 客户端socket

    完成一个客户端socket需要执行 connect(int sockfd, const struct scokaddr *addr, ...) 方法,该方法会尝试与服务端socket的地址建立连接,并且该方法会block阻塞直到连接成功或者发生错误。

  • 服务端socket

    剩下的所有 socket 方法都是给服务端socket用的,包括 bind, listen, accept

    bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) : - bind() 方法会将我们之前用socket()方法创建的 socket descriptor 与 socket address地址关联起来 - 有一个问题就是为什么client socket不需要呢? - 因为不使用 bind() 方法socket address的(IP address : port number)就会由kernel自动分配,也就是我们常见的客户端了 - 而使用了 bind() 方法就意味着我们可以指定其 socket address,也就是我们所说的服务端了

    我们说Client socket是active主动的,是主动发起来连接的(connect());而Server socket则是被动的,是被动地等待有client等待连接的。

    Kernel默认将由 socket() 方法创建的socket descriptor当作主动的socket(client),直到listen方法的出现,则会帮助kernel认出server socket

    listen(int sockfd, int backlog) : - listen()方法将有一个active的socket转换为passive的socket(sockfd)来监听来自客户端的连接,而backlog则代表可以连接的数量(这里我猜测nc中如果开启了-k持续连接可能与这个参数也有关系?) - 也就是将原本的 socket() 方法返回的 sockfd 转化为 listening socket, 以此来等待来自client socket的连接

    int accept(int listenfd, struct sockaddr *addr, int *addrlen) : - 当server socket来自client socket发起的连接(connect())时,就会触发accept()方法 - accept() 方法将会接收之前 listen() 方法返回的listenfd,并且绑定发起连接的client socket的地址,最后返回 connected descriptor 来与client socket之间使用I/O操作进行通信

  • 各个file descriptors之间的关系

    上面的各种方法,都会使用以及返回各种file descriptor,是不是已经有点绕了,最后我们就一起来通过一个例子辨析一下吧

    Figure 3: 各个file descriptors之间的关系

    Figure 3: 各个file descriptors之间的关系

    1. 通过socket方法创建Server socket,得到sockfd,在完成了listen()方法之后,返回得到listenfd,之后进入阻塞,等待客户端连接
    2. 通过socket方法创建Client socket,得到clientfd,使用connect()方法发起连接请求
    3. Server socket收到连接,调用 accept 方法,返回 connfd
    4. 最终建立起 clientfd 与 connfd 两个文件之间的连接以及通信,所有的数据的传递都通过双方对于clientfd以及connfd两个文件的读写来完成

    接着我们再来辨析一下由listen()方法所返回的 listening descriptor listenfd 与 accept()方法返回得到的connected descriptor connfd 之间的关系:

    • listening descriptor是由server建立,为了client的connect()方法所服务的,一旦创建就会一直存在
    • connected descriptor是由server建立了,是为了直接与client的clientfd之间通过I/O操作进行通信的,每当server接受一个连接就会创建直到服务结束

    最后我们以一个问题结束这一小节的学习,为什么要区分listening以及connected descriptors? 为什么不直接用listenfd来监听连接,同时建立连接一步到位呢?

    乍一看connected descriptor多次一举,但是这个分离的设计却使一个多线程并行的服务器成为可能,server只需要使用一个listenfd来处理所有client发起的连接请求,再独立的开启 connected descriptor来进行通信即可。

TODO Web Servers Web服务器

Reference

Randal E. Bryant, David R. O’Hallaron - Computer Systems. A Programmer’s Perspective [3rd ed.] (2016, Pearson)

Licensed under CC BY-NC-SA 4.0
Last updated on Oct 10, 2022 23:09 CST
comments powered by Disqus
Cogito, ergo sum
Built with Hugo
Theme Stack designed by Jimmy