Java面试题(七)网络编程系列

1.简单说一下http协议

http(Hypertext transfer protocol)超文本传输协议,通过浏览器和服务器进行数据交互,进行超文本(文本、图片、视频等)传输的规定。也就是说,http协议规定了超文本传输所要遵守的规则

2.http协议的特点

  • 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。

  • 无状态:HTTP协议是无状态协议。对于事务处理没有记忆能力。多个请求之间没有必然的联系

  • 基于TCP,http只是规定客户端和服务端数据传输的格式和数据交互行为,并不负责数据传输的细节,底层是基于TCP实现的。

3.http由哪几部分组成

1.请求报文

  • 请求行:方法字段、URL字段和HTTP协议版本
  • 请求头:例如Accept:/,Host: baidu.com ,User-Agent: HTTPie/1.0.2
  • 请求消息:POST请求的参数组成了消息主体
    2.响应报文
  • 状态行:响应状态行,包括HTTP版本、状态码200、OK表示成功
  • 响应头:包含服务器类型,日期,长度,内容类型等
  • 响应正文:服务器返回的数据

4.https和 http有什么区别和特点

经由HTTP进行通信,利用SSL/TLS建立全信道,加密数据包。HTTPS使用的主要目的是提供对网站服务器的身份认证,同时保护交换数据的隐私与完整性。

  • 内容加密:采用混合加密技术,中间者无法直接查看明文内容
  • 验证身份:通过认证能够验证发送方的身份
  • 保护数据完整性:防止传输的内容被中间人冒充或者篡改

5.说一下TCP 的三次握手

所谓的三次握手即TCP连接的建立。这个连接必须是一方主动打开,另一方被动打开的。握手之前主动打开连接的客户端结束CLOSED阶段,被动打开的服务器端也结束CLOSED阶段,并进入LISTEN阶段。随后开始“三次握手”:
Java面试题(七)网络编程系列_第1张图片

  • 首先客户端向服务器端发送一段TCP报文,意思就是请求和你建立连接,此时服务端还是LISTEN状态
  • 当服务器收到了客户端发出的TCP报文,就有LISTEN变为SYN-RCVD状态,并返回给客户端数据,告诉客户端,我收到了你发的信息,我们可以建立连接。
  • 客户端接收到来自服务器端的确认收到数据的TCP报文之后,明确了从客户端到服务器的数据传输是正常的,结束SYN-SENT阶段。并返回最后一段TCP报文,状态变为ESTABLISHED。
  • 服务器收到来自客户端的“确认收到服务器数据”的TCP报文之后,明确了从服务器到客户端的数据传输是正常的。结束SYN-SENT阶段,进入ESTABLISHED阶段。此时连接建立成功,开始数据传输

在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性。一旦出现某一方发出的TCP报文丢失,便无法继续"握手",以此确保了"三次握手"的顺利完成。

6.说一下TCP和UDP的区别

TCP:是面向连接的协议,可靠,稳定,确保传输数据的正确性,有验证重发机制,三次握手机制,因此不会出现丢失或乱序。所有TCP的网络开销更大,速度更慢。

UDP:是无连接的数据报服务,不对数据报进行检查与修改,无须等待对方的应答,速度快,可能会丢包,UDP段结构比TCP的段结构简单,因此网络开销也小。

7.说一下同步和异步

  • 同步:
    如果有多个任务或者事件要发生,这些任务或者事件必须逐个地进行,排队,只有上一个任务完成了才能执行下一个任务,一个事件或者任务的执行会导致整个流程的暂时等待,这些事件没有办法并发地执行;

  • 异步:
    如果有多个任务或者事件发生,这些事件可以并发地执行,一个事件或者任务的执行不会导致整个流程的暂时等待。

8.说一下7层网络协议

  • 应用层:文件传输,电子邮件, TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet等等
  • 表示层: 数据格式化,代码转换,数据加密 没有协议
  • 会话层:它定义了如何开始、控制和结束一个会话,包括对多个双向消息的控制和管理,以便在只完成连续消息的一部分时可以通知应用,没有协议
  • 传输层:TCP,UDP
  • 网络层:IP,ICMP,RIP,OSPF,BGP,IGMP
  • 数据链路层:SDLC、HDLC、PPP、STP、帧中继等
  • 物理层:RJ45,IEEE802等等

9.常见的TCP/UDP有哪些协议

  • TCP: FTP、HTTP、Telnet、SMTP、POP3、HTTPS
  • UDP:DNS、SNMP、NFS

10.分别写出tcp/upd 的socket服务端,客户端连接步骤

socket tcp 服务端

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
 
public class TcpSocketServer {
  public static void main(String[] args) throws Exception {
    // 监听指定的端口
    int port = 8888;
    // 创建socket服务
    ServerSocket server = new ServerSocket(port);
    // server将一直等待连接的到来
    Socket socket = server.accept();
    // 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
    InputStream inputStream = socket.getInputStream();
    byte[] bytes = new byte[1024];
    int len;
    StringBuilder sb = new StringBuilder();
    while ((len = inputStream.read(bytes)) != -1) {
      //指定编码格式,发送方和接收方必须统一
      sb.append(new String(bytes, 0, len,"UTF-8"));
    }
        System.out.println("get message from client: " + sb);
	//关闭输入流
    inputStream.close();
    //关闭socket
    socket.close();
    //关闭服务
    server.close();
  }
}

socket tcp 客户端

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
 
public class TcpSocketClient {
  public static void main(String args[]) throws Exception {
    // 要连接的服务端IP地址和端口
    String host = "127.0.0.1";
    int port = 8888;
    // 与服务端建立连接
    Socket socket = new Socket(host, port);
    // 建立连接后获得输出流
    OutputStream outputStream = socket.getOutputStream();
    String message = "hello,this is a socket client";
    //输出流发送数据
    socket.getOutputStream().write(message.getBytes("UTF-8"));
    //通过shutdownOutput高速服务器已经发送完数据,后续只能接受数据
    socket.shutdownOutput();
    //获取输入流
    InputStream inputStream = socket.getInputStream();
    //建立缓冲区
    byte[] bytes = new byte[1024];
    int len;
    StringBuilder sb = new StringBuilder();
    while ((len = inputStream.read(bytes)) != -1) {
      //指定编码格式,发送方和接收方必须统一
      sb.append(new String(bytes, 0, len,"UTF-8"));
    }
    System.out.println("get message from server: " + sb);
    
    //关闭输入流
    inputStream.close();
    //关闭输出流
    outputStream.close();
    //关闭socket连接
    socket.close();
  }
}

socket udp 服务端

public class UDPserver {
  public static void main(String[] args) throws IOException{
  	DatagramSocket ds=null;
    DatagramPacket dpReceive=null;
    int port=8800;
    try
	{
	    ds=new DatagramSocket(port);
	    System.out.println("UDP服务器已启动。。。");
	    byte[] b=new byte[1024];
	    while(ds.isClosed()==false)
	    {
	        dpReceive=new DatagramPacket(b, b.length);
	        try
	        {
	            ds.receive(dpReceive);
	            byte[] Data=dpReceive.getData();
	            int len=Data.length;
	            System.out.println("UDP客户端发送的内容是:" + new String(Data, 0, len).trim());
	            System.out.println("UDP客户端IP:" + dpReceive.getAddress());
	            System.out.println("UDP客户端端口:" + dpReceive.getPort());
	        }
	        catch(IOException e)
	        {
	            e.printStackTrace();
	        }
	    }
	    }
	    catch(SocketException e1)
	    {
	        // TODO 自动生成的 catch 块
	        e1.printStackTrace();
	    }
	}
  }
}

socket udp 客户端

public class UDPClient {
  public static void main(String[] args) throws IOException{
    //定义服务器的地址,端口号,数据
    DatagramSocket ds=null;
    DatagramPacket dpSend=null;
    InetAddress ia=InetAddress.getByName("127.0.0.1");
    int port=8800;
	try
	{
	    ds=new DatagramSocket();
	    for(int i=0;i<5;i++)
	    {
	        byte[] data=("我是 UDP 客户端"+i).getBytes();
	        dpSend=new DatagramPacket(data,data.length,ia,port);
	        ds.send(dpSend);
	        Thread.sleep(1000);
	    }
	    ds.close();
	}
	catch(IOException | InterruptedException e)
	{
	    // TODO 自动生成的 catch 块
	    e.printStackTrace();
	}  
  }
}

11.socket和websocket的区别

  • Socket是传输控制层协议,WebSocket是应bai用层协议。
  • Socket是应用层与TCP/IP协议族通信bai的中间软件抽象du层,它是一组接口(不是协议,为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口)
  • WebSocket protocol 是HTML5一种新的协议。WebSocket同HTTP一样也是应用层的协议,但是它是一种双向通信协议,是建立在TCP之上的。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成

12.说一说哪些情况会造成跨域

  • 同一域名,不同端口,例如http://localhost:8000/a.html访问http://localhost:8888/b.js
  • 同一域名,不同协议,例如http资源访问https资源
  • 不同域名,例如192.168.1.11访问192.168.1.22

13.跨域如何解决

  • 前端:利用JSONP
  • 后端:利用拦截器允许跨域资源共享Access-Control-Allow-Origin
  • nginx:代理跨域,实质和CORS跨域原理一样,通过配置文件设置请求响应头Access-Control-Allow-Origin…等字段

14.简单说一下netty,以及特点

  • 高并发:Netty 是一款基于 NIO(Nonblocking IO,非阻塞IO)开发的网络通信框架,对比于 BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。
  • 传输快:Netty 的传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了更高效率的传输。
  • 封装好:Netty 封装了 NIO 操作的很多细节,提供了易于使用调用接口。它极大地简化并优化了 TCP 和 UDP 套接字服务器等网络编程,并且性能以及安全性等很多方面甚至都要更好。
  • 支持多种协议 如 FTP,SMTP,HTTP 以及各种二进制和基于文本的传统协议。很多开源项目比如我们常用的 Dubbo、RocketMQ、Elasticsearch、gRPC 等等都用到了 Netty

15.netty应用场景

  • RPC 框架,dubbo就是用的netty
  • HTTP 服务器
  • 即时通讯服务
  • 消息推送

16.Netty 核心组件有哪些?

  • Bootstrap:通过设置bootstrap引导类来完成,该类提供了一个用于应用程序网络层配置的容器
  • Channel:Channel 接口是 Netty 对网络操作抽象类,启动连接关闭,读写等操作
  • EventLoop:实际就是负责监听网络事件并调用事件处理器进行相关 I/O 操作的处理
  • ChannelFuture:可以 通过sync()方法让异步的操作变成同步的
  • ChannelHandler:消息的具体处理器。他负责处理读写操作、客户端连接等事情
  • ChannelPipeline:当 Channel 被创建时,它会被自动地分配到它专属的 ChannelPipeline

17.Bootstrap 和 ServerBootstrap的区别

  • Bootstrap 通常使用 connet() 方法连接到远程的主机和端口,作为一个 Netty TCP 协议通信中的客户端。另外,Bootstrap 也可以通过 bind() 方法绑定本地的一个端口,作为 UDP 协议通信中的一端。
  • ServerBootstrap通常使用 bind() 方法绑定本地的端口上,然后等待客户端的连接。
  • Bootstrap 只需要配置一个线程组— EventLoopGroup ,而 ServerBootstrap需要配置两个线程组— EventLoopGroup ,一个用于接收连接,一个用于具体的处理。

18.说一说IO中的零拷贝

一般我们的数据如果需要从IO读取到堆内存,中间需要经过Socket缓冲区,也就是说一个数据会被拷贝两次才能到达他的的终点,如果数据量大,就会造成不必要的资源浪费。
Netty针对这种情况,使用了NIO中的另一大特性——零拷贝,当他需要接收数据的时候,他会在堆内存之外开辟一块内存,数据就直接从IO读到了那块内存中去,在netty里面通过ByteBuf可以直接对这些数据进行直接操作,从而加快了传输速度。

netty中零拷贝实现:

  • 使用 Netty 提供的 CompositeByteBuf 类, 可以将多个ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝。
  • ByteBuf 支持 slice 操作, 因此可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf, 避免了内存的拷贝。
  • 通过 FileRegion 包装的FileChannel.tranferTo 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel, 避免了传统通过循环 write 方式导致的内存拷贝问题.

19.长连接和短链接区别

  • 短链接:比如HTTP是无状态的的短链接,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。因为连接后接收了数据就断开了,下一次连接又需要重新建立连接。
  • 长连接:服务端和客户端连接后,即使数据处理完成,也会保持连接,这样下次数据请求就不需要重新建立连接了

20.说一下netty的长连接机制

当客户端和服务端建立连接后,就会分配一个Channel,当数据接收发送完成后,这个通道处于空闲状态,但是不会关闭连接,等到下次连接时就不用重新连接了,直接用之前的通道。除非是客户端主动关闭连接,这个通道才会关闭。客户端和服务端建建立心跳,例如5分钟客户端向服务端发送一次心跳包,那么如果超过5分钟,没有心跳,就视为无效连接,服务端就会关闭这个连接。

21.说一下TCP 粘包/拆包?

  • 粘包就是2次发送的数据粘在一起了,变成一个数据包
  • 拆包就是1次发送的数据被拆开成2个数据包

造成粘包拆包的原因

1、要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包。
2、待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。
3、要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包。
4、接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。

Netty中如何解决粘包拆包

  • LineBasedFrameDecoder : 发送端发送数据包的时候,每个数据包之间以换行符作为分隔,
  • LineBasedFrameDecoder 的工作原理是它依次遍历 ByteBuf 中的可读字节,判断是否有换行符,然后进行相应的截取。
  • DelimiterBasedFrameDecoder : 可以自定义分隔符解码器,LineBasedFrameDecoder 实际上是一种特殊的 DelimiterBasedFrameDecoder 解码器。
  • FixedLengthFrameDecoder: 固定长度解码器,它能够按照指定的长度对消息进行相应的拆包。

22.说一下BIO,NIO,AIO

  • BIO(阻塞I/O):在操作IO时,在没有获取到数据的情况下,会一直等待。等待的过程会导致整个程序阻塞,无法去做其他的事情,只有等待完成之后才能去做其他的事情
  • NIO(非阻塞I/O):在JDK1.4以前,Java的IO模型一直是BIO,但从JDK1.4开始,JDK引入的新的IO模型NIO。在操作IO时,如果不能立马读写,调用会马上返回,当IO事件分发器通知可读写时再进行读写,不断循环直到读写完成,这个过程不会影响程序做其他事,不会阻塞
  • AIO(异步非阻塞I/O):JDK1.7发布了NIO2.0,这就是真正意义上的异步非阻塞。当程序执行asynchronous read之后,首先它会立刻返回,然后就去做其他的事情,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了

你可能感兴趣的:(面试题)