计算机网络自顶向下——应用层

发布于:2023-01-07 ⋅ 阅读:(400) ⋅ 点赞:(0)

第二章 应用层

目录:

  • 目标:
    • 网络应用层原理:网络应用协议的概念和实现方面
      • 传输层的服务模型
      • 客户——服务模型
      • 对等模型
      • 内容分发网络
    • 网络应用实例:互联网留校的应用层协议
      • HTTP
      • FTP
      • SMTP/POP3/IMAP
      • DNS
    • 网络编程Socket API
  • 提纲:
    • 应用层协议原理
    • Web and HTTP
    • FTP*
    • Email、SMTP、POP3、IMAP
    • DNS
    • P2P应用
    • CDN
    • TCP套接字编程
    • UDP套接字编程

1 应用层协议原理

1.1 网络应用的体系结构

  • C/S模式:客户端——服务器模式
    • 服务器:
      • 一直运行
      • 固定的IP地址和周知的端口号(约定)
      • 扩展性:数据中心进行扩展、扩展性差
    • 客户端:
      • 主动与服务器通信
      • 与互联网有间歇性的连接
      • 可能是动态IP 地址
      • 不直接与其它客户端通信

在这里插入图片描述

  • P2P模式:对等体模式
    • (几乎)没有一直运行的服务器
    • 任意端系统之间可以进行通信
    • 每一个节点既是客户端又是服务器(自扩展性强)
    • 参与的主机间歇性连接且可以改变IP 地址(难以管理)

在这里插入图片描述

  • C/S和P2P体系结构的混合体
    • Napster
      • 文件搜索:集中
        • 主机在中心服务器上注册其资源
        • 主机向中心服务器查询资源位置
      • 文件传输:P2P
        • 任意Peer节点之间
    • 即时通信
      • 在线检测:集中
        • 当用户上线时,向中心服务器注册其IP地址
        • 用户与中心服务器联系,以找到其在线好友的位置
      • 两个用户之间聊天:P2P

1.2 进程通信

进程:在主机上运行的应用程序

同一个主机内,使用进程间通信机制通信(操作系统定义)

不同主机,通过交换报文(Message)来通信

客户端进程:发起通信的进程

服务器进程:等待连接的进程

进程间通信形式:应用程序接口API (TCP/IP :socket API

在这里插入图片描述

层间接口必须要携带的信息:

  • 要传输的报文(对于本层来说:SDU)
  • 谁传的:对方的应用进程的标示:IP+TCP(UDP) 端口
  • 传给谁:对方的应用进程的标示:对方的IP+TCP(UDP)端口号

如果Socket API 每次传输报文,都携带如此多的信息,太繁琐易错,不便于管理,用个代号标示通信的双方或者单方:socket

  • TCP socket:

    • 对于使用面向连接服务(TCP)的应用而言,套接字是4元组的一个具有本地意义的标示
    • TCP服务,两个进程之间的通信需要之前要建立连接,两个进程通信会持续一段时间,通信关系稳定
    • 可以用一个整数表示两个应用实体之间的通信关系,本地标示
    • 穿过层间接口的信息量最小
    • TCP socket:源IP,源端口,目标IP,目标端口
    • 唯一的指定了一个会话(2个进程之间的会话关系)
  • UDP socket:

    • 对于使用无连接服务(UDP)的应用而言,套接字是2元组的一个具有本地意义的标示
    • UDP服务,两个进程之间的通信需要之前无需建立连接
      • 每个报文都是独立传输的
      • 前后报文可能给不同的分布式进程
    • 因此,只能用一个整数表示本应用实体的标示,因为这个报文可能传给另外一个分布式进程
    • 穿过层间接口的信息大小最小
    • UDP套接字指定了应用所在的一个端节点(end point)
    • 在发送数据报时,采用创建好的本地套接字(标示ID),就不必在发送每个报文中指明自己所采用的ip和port
    • UDP socket:本IP,本端口
    • 但是传输报文时:必须要提供对方IP,port,接收报文时: 传输层需要上传对方的IP,port

2 Web and HTTP

Web页:由一些对象组成,对象可以是HTML文件、JPEG图像、Java小程序、声音剪辑文件等。

Web页含有一个基本的HTML文件,该基本HTML文件又包含若干对象的引用(链接),通过URL对每个对象进行引用

2.1 HTTP概况

HTTP: 超文本传输协议

  • Web的应用层协议
  • 基于TCP
  • 客户/服务器模式
    • 客户: 请求、接收和显示Web对象的浏览器
    • 服务器: 对请求进行响应,发送对象的Web服务器
  • 无状态协议

2.2 HTTP连接

2.2.1 非持久HTTP

  • HTTP1.0
  • 最多只有一个对象在TCP连接上发送
  • 下载多个对象需要多个TCP连接
  • 每次连接都要关掉
  • HTTP/1.0使用非持久连接

2.2.2 持久HTTP

  • 多个对象可以在一个(在客户端和服务器之间的)TCP连接上传输
  • 每次连接都会持久一段时间
  • HTTP/1.1 默认使用持久连接

2.3 HTTP请求报文

在这里插入图片描述

在这里插入图片描述

  • 请求行:
    • 方法:GET、POST、HEAD、PUT、DELETE等
    • URL:请求对象标识
    • 版本:HTTP版本
  • 首部行:
    • Host:对象所在的主机
    • User-agent:指明用户代理,即向服务器发送请求的浏览器的类型
    • Connection:标识服务器发送完被请求对象后是否关闭这条连接
    • Accept-language:浏览器支持的语言

2.4 HTTP响应报文

在这里插入图片描述

在这里插入图片描述

  • 状态行:
    • 版本:HTTP协议版本
    • 状态码:HTTP响应状态码
    • 短语:状态码对应状态信息
  • 首部行:
    • Connection:标识服务器发送完被请求对象后是否关闭这条连接
    • Date:服务器产生并发送该响应报文的日期时间(服务器搜索到对象的时刻)
    • Server:服务器的类型(类似User-agent)
    • last-Modified:对象创建或者最后修改的时间(对象存储时要用到)
    • Content-Length:被发送对象的字节数
    • Content-Type:指示实体中对象的类型如text/html、image/webp

2.5 HTTP响应状态码

  • 200 OK:请求成功,请求对象包含在响应报文的后续部分
  • 301 Moved Permanently:请求的对象已经被永久转移了;新的URL在响应报文的Location:首部行中指定。客户端软件自动用新的URL去获取对象
  • 400 Bad Request:一个通用的差错代码,表示该请求不能被服务器解读
  • 404 Not Found:请求的文档在该服务上没有找到
  • 505 HTTP Version Not Supported:服务器不支持请求报文使用的HTTP协议版本

2.6 用户-服务器状态:cookies

HTTP协议是无状态协议,故HTTP1.1 提供了cookies来维护状态

维护状态的协议很复杂!

  • 必须维护历史信息(状态)
  • 如果服务器/客户端死机,它们的状态信息可能不一致,二者的信息必须是一致
  • 无状态的服务器能够支持更多的客户端

在这里插入图片描述

cookie组件:

  1. HTTP响应报文中的一个cookie首部
  2. HTTP请求报文中的一个cookie首部
  3. 客户端系统中保留一个cookie文件
  4. 位于web站点的一个后端数据库

2.7 Web缓存

  • 缓存既是客户端又是服务器

  • 通常缓存是由ISP安装(大学、公司、居民区ISP)

  • 作用:

    • 降低客户端的请求响应时间
    • 可以大大减少一个机构内部网络与Internent接入链路上的流量
    • 互联网大量采用了缓存:可以使较弱的ICP也能够有效提供内容

在这里插入图片描述

2.8 条件GET方法

目标:如果缓存器中的对象拷贝是最新的,就不要发送对象

缓存器: 在HTTP请求中指定缓存拷贝的日期
If-modified-since:<date>
服务器: 如果缓存拷贝陈旧,则响应报文没包含对象:
HTTP/1.0 304 Not
Modified

在这里插入图片描述

3 FTP: 文件传输协议

在这里插入图片描述

  • 向远程主机上传输文件或从远程主机接收文件
  • 客户/服务器模式
    • 客户端:发起传输的一方
    • 服务器:远程主机
  • ftp: RFC 959
  • ftp服务器:端口号为21

4 EMail

3个主要组成部分:

  • 用户代理
  • 邮件服务器
  • 简单邮件传输协议:SMTP

4.1 用户代理

  • 又名“邮件阅读器”
  • 撰写、编辑和阅读邮件
  • 如Outlook、Foxmail
  • 输出和输入邮件保存在服务器上

4.2 邮件服务器

  • 邮箱中管理和维护发送给用户的邮件
  • 输出报文队列保持待发送邮件报文
  • 邮件服务器之间的SMTP协议:发送email报文
    • 客户:发送方邮件服务器
    • 服务器:接收端邮件服务器

4.3 SMTP

  • 使用TCP在客户端和服务器之间传送报文,端口号为25
  • 直接传输:从发送方服务器到接收方服务器
  • 传输的3个阶段
    • 握手
    • 传输报文
    • 关闭
  • 命令/响应交互
    • 命令:ASCII文本
    • 响应:状态码和状态信息
  • 报文必须为7位ASCII码

工作流程:

在这里插入图片描述

  1. Alice使用用户代理撰写邮件并发送给bob@someschool.edu
  2. Alice的用户代理将邮件发送到她的邮件服务器;邮件放在报文队列中
  3. SMTP的客户端打开到Bob邮件服务器的TCP连接
  4. SMTP客户端通过TCP连接发送Alice的邮件
  5. Bob的邮件服务器将邮件放到Bob的邮箱
  6. Bob调用他的用户代理阅读邮件

与HTTP比较:

  • 相同点:

    • 都用于一台主机向另一台主机传送文件:HTTP从Web服务器向Web客户传送文件;SMTP从一个邮件服务器向另一个邮件服务器传送文件。
    • 当进行文件传送时,持续的HTTP和SMTP都是同持续TCP连接。
  • 不同点:

    • HTTP是一个拉协议,用户使用HTTP从服务器拉取信息;SMTP是一个推协议,一个邮件服务器把文件推向另一个邮件服务器。
    • SMTP要求每个报文使用7比特ASCII格式,HTTP不受这种限制。
    • 在处理包含图像等某提类型的文件,HTTP把每个对象封装到它自己的HTTP响应报文中,而SMTP则把所有报文对象放在一个报文中。

4.4 邮件报文格式

在这里插入图片描述

4.5 邮件访问协议

在这里插入图片描述

  • SMTP: 传送到接收方的邮件服务器
  • 邮件访问协议:从服务器访问邮件
    • POP3:邮局访问协议(Post Office Protocol)[RFC 1939]
      • 用户身份确认(代理<–>服务器) 并下载
    • IMAP:Internet邮件访问协议(Internet Mail AccessProtocol)[RFC 1730]
      • 更多特性(更复杂)
      • 在服务器上处理存储的报文
    • HTTP:Hotmail , Yahoo! Mail等
      • 方便

4.5.1 POP3(第三版邮局协议)

当用户代理打开一个到邮件服务器端口上的TCP连接后,POP3就开始工作了。工作流程分为三个阶段:特许、事务处理和更新。

  • 特许:用户代理发送用户名和口令(明文形式)以鉴别用户。
  • 事务处理:用户代理取回报文。还可以做以下操作:报文删除标记、取消报文删除标记以及获取邮件的统计信息。
  • 更新:出现在用户发出quit命令后,目的是结束POP3会话。这时邮件服务器会删除那些被标记的报文。

4.5.2 IMAP(因特网邮件访问协议)

POP3协议只能将文件和报文下载存放在本地主机上管理;IMAP协议可以在服务器上创建文件夹并把报文存放在文件夹中。

  • 当报文第一次到达服务器时,它与收件人的INBOX文件夹相关联。收件人可以将其移动到自己创建的文件夹进行阅读或删除。此外IMAP还提了远程文件夹中查询邮件的命令,IMAP服务器维护IMAP会话的用户状态信息。
  • IMAP另一个重要特性是它具有允许用户代理获取报文组件的命令。只获取报文的某一部分。

4.5.3 基于Web(HTTP)的电子邮件

  • 浏览器作为用户代理。
  • 用户从邮件服务器获取邮件通过HTTP协议进行。
  • 用户发送邮件到自己的邮件服务器时用的时HTTP协议。
  • 发送邮件服务器发送邮件给接受邮件服务器用SMTP协议。

5 DNS(Domain Name System)

DNS协议则是用来将域名转换为IP地址(也可以将IP地址转换为相应的域名地址)。

  • 问题1:如何命名设备
    • 用有意义的字符串:好记,便于人类用使用
    • 解决一个平面命名的重名问题:层次化命名
  • 问题2:如何完成名字到IP地址的转换
    • 分布式的数据库维护和响应名字查询
  • 问题3:如何维护
    • 增加或者删除一个域,需要在域名系统中做哪些工作

5.1 DNS(Domain Name System)总体思路和目标

  • DNS的主要思路
  • 分层的、基于域的命名机制
  • 若干分布式的数据库完成名字到IP地址的转换
  • 运行在UDP之上端口号为53的应用服务(事务性强,没必要TCP)
  • 核心的Internet功能,但以应用层协议实现
    • 在网络边缘处理复杂性
  • DNS主要目的:
    • 实现主机名-IP地址的转换(name/IP translate)
    • 其它目的
      • 主机别名规范名字的转换:Host aliasing
      • 邮件服务器别名到邮件服务器的正规名字的转换:Mail server aliasing
      • 负载均衡:Load Distribution

5.2 DNS 工作机制

最简单的实现方式:集中式地使用一个DNS服务器

问题:

  • 单点故障:如果这个服务器崩溃,整个互联网崩溃
  • 通信容量:单个服务器不得不处理所有DNS查询
  • 远距离的集中式数据库:如果只有一个服务器那么其他地方的主机离着就很远
  • 维护:单个服务器存放着所有的数据,数据库庞大,而且新增数据时还得频繁更新

解决方式:

  1. 分布式、层次数据库
  2. DNS缓存

5.2.1 分布式、层次数据库

在这里插入图片描述

大致来说分为三类DNS服务器:

  • 根DNS服务器:根名字服务器提供顶级域服务器的IP地址
  • 顶级域DNS服务器:顶级域DNS服务器提供了权威服务器的IP地址。负责顶级域名(如com, org, net,edu和gov)和所有国家级的顶级域名(如cn, uk, fr, ca,jp )
  • 权威服务器:组织机构的DNS服务器, 提供组织机构服务器(如Web和mail)可访问的主机和IP之间的映射。组织机构可以选择实现自己维护或由某个服务提供商来维护

本地DNS服务器:严格来说本地DNS服务器不属于服务器层次结构;由ISP提供;当主机与某个ISP连接时,该ISP提供一台DNS服务器的IP地址。

5.2.2 解析域名

主要有两种方式:

  1. 递归查询
  2. 迭代查询
5.2.2.1 递归查询

名字解析负担都放在当前联络的名字服务器上

问题:根服务器的负担太重

解决: 迭代查询(iterated queries)

在这里插入图片描述

5.2.2.2 迭代查询

根(及各级域名)服务器返回的不是查询结果,而是下一个NS的地址,最后由权威名字服务器给出解析结果
当前联络的服务器给出可以联系的服务器的名字
我不知道这个名字,但可以向这个服务器请求

在这里插入图片描述

5.2.3 DNS缓存

核心:缓存时为了性能,删除是为了一致

  • 一旦名字服务器学到了一个映射,就将该映射缓存起来
  • 根服务器通常都在本地服务器中缓存着,使得根服务器不用经常被访问
  • 目的:提高效率
  • 可能存在的问题:如果情况变化,缓存结果和权威资源记录不一致
  • 解决方案:TTL(默认2天)

5.3 DNS记录和报文

  • 资源记录(resource records)

    • 作用:维护域名-IP地址(其它)的映射关系
    • 位置:Name Server的分布式数据库中
  • RR格式: (domain_name, ttl, type,class,Value)

    • Domain_name: 域名
    • TTL: time to live : 生存时间(权威,缓冲记录)
    • Class 类别:对于Internet,值为IN
    • Value 值:可以是数字,域名或ASCII串
    • Type 类别:资源记录的类型
      • Type=A:域名到IP
        • Name为主机
        • Value为IP地址
      • Type=NS:域名到权威服务器域名
        • Name域名(如foo.com)
        • Value为该域名的权威服务器的域名
      • Type=CNAME
        • Name为规范名字的别名
          www.ibm.com 的规范名字为
          servereast.backup2.ibm.com
        • value 为规范名字
      • Type=MX
        • Value为name对应的邮件服务器的名字
  • DNS协议:查询和响应报文的报文格式相同

在这里插入图片描述

在这里插入图片描述

5.4 维护问题:新增一个域

  • 在上级域的名字服务器中增加两条记录,指向这个新增的子域的域名和域名服务器的地址,例:
    (networkutopia.com, dns1.networkutopia.com, NS)
    (dns1.networkutopia.com, 212.212.212.1, A)
  • 在新增子域的名字服务器上运行名字服务器,负责本域的名字解析: 名字==>IP地址

6 P2P 应用

没有(或极少)一直运行的服务器
任意端系统都可以直接通信
利用peer的服务能力
Peer节点间歇上网,每次IP地址都有可能变化

6.1 文件分发: C/S vs P2P

在这里插入图片描述

6.2 P2P文件分发: BitTorrent

文件被分为一个个块256KB
网络中的这些peers发送接收文件块,相互服务

在这里插入图片描述

6.2.1 工作过程

假设有一个新的对等方Alice加入一个洪流

  1. 追踪器(tracker)随机地从参与对等方的集合中选择对等方的一个子集(比如50个)并将这50个对等方的IP地址发送给Alice,Alice拥有了一个IP地址的列表
  2. Alice与列表上的所有对等方创建TCP连接,成为邻近对等方
  3. Alice周期性的询问每个邻近对等方他们所具有的块列表。
  4. 对当前自身没有的块信息,Alice发送请求获取(最稀缺的块,优先级最高请求)
  5. 向那些向她请求的块的邻居发送邻居没有的而自己有的块信息。

两个问题:

  1. 应该向邻居请求哪些块呢?
    最稀缺优先
  2. 如何决定响应哪个邻居的请求?
    BitTorrent使用了一种对换算法(一报还一报)。该对换算法的基本思想是Alice根据当前能够以最高速率向她提供数据的邻居,给出其优先权。

操作过程如下:

  1. Alice对于她的每个邻居持续的测试接收到比特的速率,并确定以最高速率流入的4个邻居
  2. 每过10秒,她重新计算该速率并可能修改这4个对等方的集合
  3. 每隔30秒,Alice要随机选择另外一个邻居并向其发送块,也即Alie随机选择一名新的对换伴侣。
  4. 这种效果是对等方能够趋于找到彼此的协调的速率上载。随机选择邻居也允许新的对等方得到块,因此他们能够具有对换的东西,除了上诉5个对等方,其它对等方均被阻塞。

7 视频流和内容分发

7.1 因特网视频流

  • 视频流量:占据着互联网大部分的带宽
  • 挑战:规模性-如何服务者~1B 用户?不同用户拥有不同的能力(例如:有线接入和移动用户;带宽丰富和受限用户)
  • 解决方案: 分布式的,应用层面的基础设施
  • 编码:使用图像内和图像间的冗余来降低编码的比特数
    • 空间冗余(图像内)
    • 时间冗余(相邻的图像间)

7.2 DASH

DASH: Dynamic, Adaptive Streaming over HTTP

  • 服务器:
    • 将视频文件分割成多个块
    • 每个块独立存储,编码于不同码率(8-10种)
    • 告示文件(manifest file): 提供不同块的URL
  • 客户端:
    • 先获取告示文件
    • 周期性地测量服务器到客户端的带宽
    • 查询告示文件,在一个时刻请求一个块,HTTP头部指定字节范围
      • 如果带宽足够,选择最大码率的视频块
      • 会话中的不同时刻,可以切换请求不同的编码块(取决于当时的可用带宽)

7.3 CDN

Content Distribution Networks内容分发网络

通过CDN,全网部署缓存节点,存储服务内容,就近为用户提供服务,提高用户体验

通常CDN采取两种服务器安置原则:

  1. 深入(enter deep):将CDN服务器深入到许多接入网,更接近用户,数量多,离用户近,管理困难
  2. 邀请做客(bring home):部署在少数(10个左右)关键位置,如将服务器簇安装于IXP附近

7.3.1 操作过程

在这里插入图片描述

  1. 用户访问位于NetCinema的视频网站www.NetCinema.com)。当用户点击视频链接http://video.netcinema.com/6Y7B23V时,该用户主机发送了一个对于 video.netcinema.comDNS 请求
  2. DNS请求先请求本地DNS服务器LDNS
  3. 然后找到NetCinema权威DNS服务器,此时权威服务器并不会返回ip而是KingCDN域的主机名al105.kingcdn.com
  4. 此时LDNS向al105.kingcdn.com发出DNS请求,KingCDN权威服务器再返回KingCDN内容服务器的IP地址给LDNS
  5. LDNS告诉主机要访问的IP地址
  6. 主机与这个IP地址创建TCP连接

8 TCP套接字编程

8.1 请求流程

在这里插入图片描述

在这里插入图片描述

8.1.1 服务器端流程:

  1. 创建服务器套接字(ServerSocket)
  2. 将套接字绑定到一个本地地址和端口上(bind)
  3. 将套接字设定为监听模式,准备接受客户端请求(listen)
  4. 阻塞等待客户端请求到来。当请求到来后,接受连接请求,返迥-个新的对应于此客户端连接的套接字socketClient (accept)
  5. 用返回的套接字socketClient和客户端进行通信(I0流操作)
  6. 返回,等待另一个客户端请求(accept)
  7. 关闭套接字(close)

8.1.2 客户端流程:

  1. 创建客户端套接字(Socket)
  2. 向服务器发出连接请求(connect)
  3. 和服务器进行通信(I0流操作)
  4. 关闭套接字(close)

8.1.3 代码

package com.suo.webServer.tcp;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

public class MySocketClient {
    public static void main(String[] args) {
        if (args.length < 2) {
            System.out.println("Usage: SimpleSocketClientExample <server> <path>");
            System.exit(0);
        }
        String server = args[0];
        String path = args[1];

        System.out.println("Loading contents of URL: " + server);

        try {
            // Connect to the server
            Socket socket = new Socket(server, 80);

            // Create input and output streams to read from and write to the server
            PrintStream out = new PrintStream(socket.getOutputStream());
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            // Follow the HTTP protocol of GET <path> HTTP/1.0 followed by an empty line
            // 发送请求
            out.println("GET " + path + " HTTP/1.0");
            out.println();

            // Read data from the server until we finish reading the document
            // 得到服务器响应的请求
            String line = in.readLine();
            while (line != null) {
                System.out.println(line);
                line = in.readLine();
            }

            // Close our streams
            in.close();
            out.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
package com.suo.webServer.tcp;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class MyWebServer extends Thread {

    private ServerSocket serverSocket;
    private int port;
    private boolean running = false;

    public MyWebServer(int port) {
        this.port = port;
    }

    public void startServer() {
        try {
            //绑定ServerSocket也就是连接套接字
            serverSocket = new ServerSocket(port);
            this.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void stopServer() {
        running = false;
        this.interrupt();
    }

    @Override
    public void run() {
        running = true;
        while (running) {
            try {
                System.out.println("Listening for a connection");

                // Call accept() to receive the next connection
                // 等待接受客户端的请求
                Socket socket = serverSocket.accept();

                // Pass the socket to the RequestHandler thread for processing
                // 响应客户端的请求
                RequestHandler requestHandler = new RequestHandler(socket);
                requestHandler.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("Usage: SimpleSocketServer <port>");
            System.exit(0);
        }
        int port = Integer.parseInt(args[0]);
        System.out.println("Start server on port: " + port);

        MyWebServer server = new MyWebServer(port);
        // 开启服务器
        server.startServer();

        // Automatically shutdown in 1 minute
        try {
            Thread.sleep(60000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        server.stopServer();
    }
}
package com.suo.webServer.tcp;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

class RequestHandler extends Thread {
    private Socket socket;

    RequestHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            System.out.println("Received a connection");

            // Get input and output streams
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(socket.getOutputStream());

            // Write out our header to the client
            out.println("Echo Server 1.0");
            out.flush();

            // Echo lines back to the client until the client closes the connection or we receive an empty line
            String line = in.readLine();
            // 规定只有”/“路径有资源其他路径没有资源
            if(line != null && line.length() > 0) {
                String[] s = line.split(" ");
                if (!s[1].equals("/")) {
                    out.println("Echo: 404 Not Found");
                    out.flush();
                    line = null;
                }
            }
            while (line != null && line.length() > 0) {
                out.println("Echo: " + line);
                out.flush();
                line = in.readLine();
            }

            // Close our connection
            in.close();
            out.close();
            socket.close();

            System.out.println("Connection closed");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

8.1.4 效果图

在这里插入图片描述

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到