HTTP和简单服务器响应的实现(实现servlet功能)

http1.1

  • 长连接, keepalive, 允许持久连接, 减少每次进行tcp连接的开销

  • 支持流水线, 不必等待上一个响应, 就可以发送第二次请求

  • 允许响应数据分块, 响应数据太大时, 只要不注明Content-length, 客户端就无法断开连接, 直到收到EOF, 有利于传输大文件

  • 加入了host头, 针对同一个服务器有多个主机的问题, 即多个主机共享一个ip

  • 支持断点续传, range参数

  • 摘要算法进行身份认证

 

http2

  • 是二进制协议, 不再是纯文本, 数据更紧凑

  • http1.1的流水线还是有阻塞的情况, 需要等待前一个响应了, 后一个才能返回, 2采用了多路复用, 一个TCP连接发起多个请求

  • 服务端主动推送数据, 将html和html相关的js也一起推送出去, 而不是等待你请求js

  •  

 

 

UDP与TCP

UDP需要建立连接,但没有连接确认的过程,所以会比tcp快,所以当没有收到的包就没有收到就行了,应用:电视,直播,游戏(有的游戏会用TCP,王者荣耀:,tcp协议的网络抗性欠佳,对MOBA类或其他实时性要求较高的游戏,一般是不建议使用tcp作为协议载体。事实上,王者荣耀的PVP通信协议也确实是基于udp封装的;同样,最近大家喜闻乐见的《绝地求生》,也是基于udp的。)

TCP(网络聊天,看视频,HTTP(因为要收到请求才能有响应))

 

TCP 3次握手4次挥手

1.握手

image-20211129152314033

2.挥手

image-20211129152149426

 

 

 

游戏中使用TCP还是UDP

不同类型的游戏因为玩法、竞技程度不一样,采用的同步算法不一样,对网络延迟的要求也不一样。例如,MOBA类游戏多使用帧同步为主要同步算法,竞技性也较高,无论从流畅性,还是从公平性要求来说,对响应延迟的要求都最高,根据业内经验,当客户端与服务器的网络延迟超过150ms时,会开始出现卡顿,当延迟超过250ms时,会对玩家操作造成较大影响,游戏无法公平进行。类似地,“吃鸡”游戏(如《绝地求生》)玩法对玩家坐标、动作的同步要求极高,延迟稍大导致的数据不一致对体验都会造成较大影响,其实时性要求接近MOBA类游戏。而对于传统mmorpg来说,多采用状态同步算法,以属性养成和装备获取为关注点,也有一定竞技性,出于对游戏流畅性的要求,对延迟也有一定要求,同步算法的优化程度不一样,这一要求也不一样,一般情况下为保证游戏正常进行,需要响应延迟保持在300ms以下。相比之下,对于炉石传说、斗地主、梦幻西游等回合制游戏来说,同时只有一个玩家在操作双方数据,无数据竞争,且时间粒度较粗,甚至可通过特效掩盖延迟,因此对网络延迟的要求不高,即便延迟达到500ms~1000ms,游戏也能正常进行。

不同传输层协议在可靠性、流量控制等方面都有差别,而这些技术细节会对延迟造成影响。tcp追求的是完全可靠性和顺序性,丢包后会持续重传直至该包被确认,否则后续包也不会被上层接收,且重传采用指数避让策略,决定重传时间间隔的RTO(retransmission timeout)不可控制,linux内核实现中最低值为200ms,这样的机制会导致丢包率短暂升高的情况下应用层消息响应延迟急剧提高,并不适合实时性高、网络环境复杂的游戏。

请求和响应的格式

1.请求行/响应行:版本,状态

2.请求头/响应头:自身的描述、数据格式

3.请求体/响应体:数据内容

 

输入流和输出流

首先知道InputStream、OutputStream这两个是所有输入输出流的抽象父类,只是用来标记是流入还是流出,但是对流入流出的对象并没有要求

FileInputStream:他流入的对象是内存,从硬盘流向内存

FileOutputStream:他流出的对象也是内存,从内存流向硬盘

accept:

1.getInpuStream:接收对方发送的数据,返回结果用InputStream接收

2.getOutputStream:准备发出信息给对方,返回结果为OutputStream接收

而我们这里得到的InputStream和OutputStream流入流出的对象都是服务器,

【注意】这里我们只是用父类来接收,真实的类应该是OutputStream的子类,所以你往output里面写入就能够直接由tcp协议进行发送了

image-20211129162900502

 

java套接字

1.单纯连接

image-20211129152949841

2.读取客户端信息

image-20211129153126409

请求:就是一坨字符串(http协议的内容,也就是说采用get方式的话是没有进行编码的。直接可以读取),所以就算能解析协议但是你不知道传过来的意义,所以需要http处理。

 

第一行是请求行(GET /index HTTP/1.1 其中这里的index是请求默认跟的数据,你可以采用?name=xxx&list=222这种加在请求后面发给服务器),随后下面是请求头,是客户端想要告诉服务器的信息,对于自身的描述 ,请求体是传输的数据

image-20211129153144304

connection:keep-alive是为了告诉服务器你给我保持连接一段时间不要马上断开,因为http协议1.0版本本身就是请求一次就断开,但由于是tcp连接建立连接就会用很多时间,所以有点浪费

User-Agent:是客户端的自我描述

accept:是客户端能够接收的内容,超过这些类别我就没办法接收了

post

post

目前浏览器自己输入的连接只能是get请求,不能是post请求,post一般由返回回来的html表单构造

如何在本机发送post等请求呢? 可以使用软件 postman 可以发送任何请求

对于post请求是可以有请求体的,但是get把所有的uri参数都放在了请求行中,因为请求行中和请求头中的内容都是明文,所以get方式的话就没办法对传输参数进行加密,但是post能够把传输参数放在请求体中,而请求头中的内容是可以加密的,所以如果是用户名和密码的话一般才用post,并且post的加密还是可以设置加密方式的

也就是post:请求行+请求头+请求体

get:请求行+请求头

image-20211129155207171

请求体和请求头是有一个空行隔开的

get如图:我输入的url: hrrp://127.0.0.1/index?name=lisi&age=12

所有的参数信息全部明文出现在请求行中

image-20211129155623164

3.服务器带有回应的

首先输入请求:127.0.0.1:8888/home ,注意这里如果不加东西,默认是127.0.0.1:8888/index

image-20211129154651767

 

 

实例1:自己解析http协议

1.首先你收到的是纯string,你可以通过对string进行分隔

构造一个request类:

image-20211129160216228

2.收到报文之后将分隔出来的信息封装到request类中

image-20211129160247779

这里headerAndBody是分隔的请求体和请求行/请求头,因为请求体和上面这两个是多空出了一行的(注意传过来的http报文每一行都\r\n)

3.最终构造出的request

image-20211129170749325

实例2 :根据请求构造响应

image-20211129193852408

image-20211129193913098

我认为他写的这个有问题,因为在读取byte的时候虽然是utf-8编码,但是1024最后一字节并不一定包含了一个完整的字符就会导致出错,可以采用下面的方式

BufferedReader类从字符输入流中读取文本并缓冲字符,以便有效地读取字符,数组和行

可以通过构造函数指定缓冲区大小也可以使用默认大小。对于大多数用途,默认值足够大

public static String getContent(InputStream in) throws IOException {
       String result = "";
       if (null != in) {
           StringBuffer content = null;
           BufferedReader r = new BufferedReader(new InputStreamReader(in));
           content = new StringBuffer();
           String line = "";
           while ((line = r.readLine()) != null) {
               content.append(line);
          }
           result = content.toString();
      }
       return result;
  }

 

遇到的问题string与byte转换的编码问题

无论是运行在Tomcat上的Java程序,还是只运行在JVM上的普通Java程序,他们在进行字符串和字节数组的相互转换过程中,若没有显式指定字符集,那么它们会采用系统的默认字符集。 其中,运行在Tomcat容器的Java程序使用的系统字符集是GBK; 而只运行在JVM上的普通Java程序使用的系统字符集呢,与该Java项目的整体字符集保持一致。 重中之重:但不管该Java程序是运行在Tomcat还是只运行在JVM,可以确定的一 点就是,它使用的系统默认字符集,就是system.getProperty("file.encoding")的属性值! !!

 

【代码】

image-20211129193454925

1.构建响应行

image-20211129193528969

2.构建响应头

非常重要,大部分的乱码都是由于content-type错误导致的,CRLF是\r\n

image-20211129193552992

3.构建响应体

image-20211129193723030

4.构建响应与发送

image-20211129193733415

 

 

实例3 分发器

把上面根据请求url的if...else的内容卸载这里

image-20211129194109935

 

总结

1.浏览器输入http请求,通过DNS服务器找到IP(先查找自己电脑里的host文件)

2.建立socket连接 3次握手

3.服务器发送完数据

4.四次挥手

 

TCP连接长时间的连接浪费资源,但是断的太快也需要不停的建立也浪费资源,所以应该连接一段时间之后自动4次挥手

postman的使用

postman 可以发送任何请求,而普通浏览器只能发送get请求

image-20211129161147982

 

操作

1.构造请求

image-20211129161439975

 

2.服务器收到的请求(string)

image-20211129161415243

这里的content-type就是我们上面构造数据的时候选择的内容类型,服务器需要根据内容的类型解析内容,否则解析不了

并且服务器返回给客户端的响应也需要有content-type,让浏览器知道内容到底是什么

3.服务器构造响应

image-20211129170450803

 

 

HTTP状态码

状态码和描述状态的短语跟在协议版本的后面

image-20211129162008868

image-20211129161958683

 

 

 

 

 

 

 

 

 

 

posted @ 2021-11-29 20:03  JJJmk  阅读(366)  评论(0)    收藏  举报