面试总结之网络编程-08

1、 nio和bio、AIO的区别

  1. BIO的话,每次网络请求过来之后,服务器都是会为这个请求创建一个线程,这个线程会一直等待这个请求后续的数据,等处理完成后才会销毁这个线程;
  2. 而NIO,当每次网络请求过来时,服务器不会马上创建一个线程去处理这个请求,而是会交给一个Selector线程,只有这个请求后续的数据全部传输过来后,Selector才会去通知其他其他线程或者创建一个线程来处理这个请求。
  3. 从编程模式上来看AIO相对于NIO的区别在于,NIO需要使用者线程不停的轮询IO对象,来确定是否有数据准备好可以读了,而AIO则是在数据准备好之后,才会通知数据使用者,这样使用者就不需要不停地轮询了。当然AIO的异步特性并不是Java实现的伪异步,而是使用了系统底层API的支持,在Unix系统下,采用了epoll IO模型,而windows便是使用了IOCP模型。

2、 黏包和半包的区别

2.1、TCP粘包是什么?

粘包发生在发送或接收缓冲区中;应用程序从缓冲区中取数据是整个缓冲区中有多少取多少;那么就有可能第一个数据的尾部和第二个数据的头部同时存在缓冲区,而TCP是流式的,数据无边界,这时发生粘包。

20200217213233246.png

2.2、例如原始数据有3条为:

Hello, world\n
I’am zhangsan\n
How are you?\n

变成了两个ByteBuffer(黏包,半包)

Hello, world\nI’am zhangsan\nHo
w are you?\n

2.3. 解决粘包问题

20201109142548270.png

2.4. 半包

指接受方没有接受到一个完整的包,只接受了部分,这种情况主要是由于TCP为提高传输效率,将一个包分配的足够大,导致接受方并不能一次接受完。(在长连接和短连接中都会出现)。

3、 tcp编程注意的事项

TCP的Socket编程,要做到质量稳定可靠效率高,对市场上90%的开发人员来说,是一项难度极高的工作。笔者有多年的socket开发经验,今天主要介绍socket编程中常见问题及注意事项:

(1) 对于可变包长,必须定义包头,并在包头中定义包的总长度

 由于socket是字节流,就像流水一样,对于传输多个包的socket数据流来说,从中间无法得知一个包的起始位置,从中间位置观察数据包的特征也是一个不靠谱的做法。因此,只能从包的第一个字节起,一个一个计算每一个包的位置。

 通常的做法是,对每一个传输的数据包,定义一个包头和包体。包头为固定长度,通常在其中定义版本号、包体的长度(或总包的长度)、以及包类型字段。包体通常是可变长度的内容。收到包的一方,解析socket数据流时,先解析固定长度的包头,从包头中得到整个包的长度,然后取得包体内容,紧接着取下一个包的包头和包体,如此循环下去,就能顺序收到每一个包。
复制代码

(2) socket接收调用一次不保证收到一个完整数据包

调用socket的接收函数时,调用一次,可能会收到一个完整的对方发来的数据包,也可能只收到1/2个数据包,也可能1.5个数据包,也可能收到任何长度的包,或收到0个字节,这些情况都可能发生,这是正常情况,不是错误。当然,由于socket是全双工的字节流,你只可能收到一个一个字节,不可能收到半个字节,或2.5个字节。原因还是在于TCP是一个字节流,无头无尾,你只能根据socket收到的字节,自己来拼装成一个一个顺序的应用层的数据包。socket无法给你一个完整的数据包,只能给你一个一个收到的字节。

接收分为阻塞和非阻塞,默认是阻塞模式。在调用接收函数时,如果是阻塞模式,将造成程序阻塞在该函数中,长时间不响应,程序不能处理其他工作,造成用户体验差;如果是非阻塞模式,一次调用无论成功或是失败,函数立即返回,如果长时间无数据可接收,又会导致CPU为100%的问题。所以,这里一般都需要使用多线程处理,小心地处理各种正常和异常情况。
复制代码

此外,网上一般都只有讨论阻塞的情况,对非阻塞的情况下的各种返回值,哪种是正常,哪种是阻塞,哪种是已经断开,资料非常少,需要自己的测试和验证去保证质量。

(3) 同理,socket发送调用一次也不保证整个包都发送出去 调用socket的发送函数时,调用一次,也不保证整个包发送完成。通常对于阻塞而已,只有发送完成或失败才返回;对于非阻塞类型,发送一部分就可能返回,因此,需要检查返回值,如果只发送了一部分,需要再次调用send函数,以便把剩下的数据包全部发送出去,才能发送下一个数据包。 与接收函数的调用类似,在调用发送函数时,如果是阻塞模式,将造成程序阻塞在该函数中,长时间不响应,程序不能处理其他工作,造成用户体验差;如果是非阻塞模式,一次调用无论成功或是失败,函数立即返回,如果长时间发送不出去,又会导致CPU为100%的问题。所以,这里一般也是需要使用多线程处理,小心地处理各种正常和异常的情况。

此外,网上一般都只有讨论阻塞的情况,对非阻塞的情况下的各种返回值,哪种是正常,哪种是阻塞,哪种是已经断开,资料非常少,需要自己的测试和验证去保证质量。

(4) socket收发可以同时进行 由于socket是全双工的字节流,全双工意味着接收的时候也可以同时发送,发送的时候也可以同时接收。因此,如果软件要求高性能,一般采用多线程处理,才能达到收和发同时进行的效果。

但是,需要注意,对于SSL而言,并不是全双工的,而是单工的,意味着SSL在发送的时候不能接收,接收的时候也不能发送。因此,SSL的效率,即使不考虑加密的影响,理论效率也只是普通socket的一半,如果考虑加密等因素,效率下降的就会更明显了。

(5) 大小字节序转换 世界上的电脑CPU分为大端法和小端法两种。通常网络传输时都采用大端对齐法。对于AIX等系统,是大端对齐;而Windows、Linux等系统则是小端对齐。对于超过一个字节的short、int、int64及相应的unsigned数据类型,都需要进行大小字节序的转换。操作系统本身提供了ntohs和ntohl对16位和32位字节的整形进行转换,但并没有提供64位的字节序转换函数,此外,也没有提供float和double类型的数据类型进行转换的函数,怎么办呢?可以参考我写的另外一篇文章《socket编程必备函数:字节序转换C++模板函数,一劳永逸地代替ntoh或hton等函数》,这篇文章中,使用一个C++模板函数对所有的数据类型的转换进行了统一,使用简单方便,一劳永逸。

总之,socket编程的难度还是非常高的,必须有相当多的积累才可能编出质量过硬的软件。如果大家觉得难度过大,或者时间精力不足以从头开始编码,建议使用第三方的socket封装库来实现,专业的事情留给专业的人来做,质量更有保证,比如waisock就是一个非常优秀的socket封装库,官网是waisock.szxunxun.com/ ,大家不妨试试。

4、 netty

4.1 Netty和Tomcat有什么区别?

Netty和Tomcat最大的区别就在于通信协议,Tomcat是基于Http协议的,他的实质是一个基于http协议的web容器,但是Netty不一样,他能通过编程自定义各种协议,因为netty能够通过codec自己来编码/解码字节流,完成类似redis访问的功能,这就是netty和tomcat最大的不同。

有人说netty的性能就一定比tomcat性能高,其实不然,tomcat从6.x开始就支持了nio模式,并且后续还有APR模式——一种通过jni调用apache网络库的模式,相比于旧的bio模式,并发性能得到了很大提高,特别是APR模式,而netty是否比tomcat性能更高,则要取决于netty程序作者的技术实力了。

4.2 为什么Netty受欢迎?

如第一部分所述,netty是一款收到大公司青睐的框架,在我看来,netty能够受到青睐的原因有三:

  1. 并发高
  2. 传输快
  3. 封装好

4.3 Netty为什么并发高

Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,对比于BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高,两张图让你了解BIO和NIO的区别:
www.jianshu.com/p/b9f3f6a16…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值