网络编程
1,IO流:
IO流用来处理设备之间的数据传输。
java对数据的操作是通过流的方式。
java用于操作流的对象都在IO包中。
流按照操作数据分为两种:
字节流和字符流。
流按流向分为:
输入流,输出流。
输入流和输出流是相对于内存设备而言。
将外设中的数据读取到内存中是输入,将内存的数据写入到外设中,是输出。
字符流的由来:
其实就是,字节流读取文字字节数据后,不直接操作而是先查指定的编码表,获取对应的文字,再对这个文字进行 操作,简单的说:字节流+编码表。
字节流的抽象基类: InputStream(输入) OutputStream
字符流的抽象基类: Reader(输入) Writer
这些体系的子类名都以父类名作为后缀,而子类名的前缀就是该对象的功能。
注:由这四个类派生出来的子类名称都是以其父类名称作为子类名的后缀。
如:InputStream的子类FileInputStream......
字符流的缓冲区:缓冲区的出现提高了对数据的读写效率。
对应类:BufferedWriter:中有个方法newline()可以换行。
BufferedReader:中有个方法readline()可以读取文字...(换行符)缓冲区要结合流才可以使用。在流的基础上对流的功能进行了增强,其实就是用到了装饰设计模式。
序列化:就是将对象转成字节数据存储在内存中。
2,网络编程
网络编程就是基于信息传输协议编写一些通信的程序。
网络模型:OSI(Open System Interconnection)开放系统互连。
应用层 表示层 会话层 传输层 网络层 数据链路层 物理层
TCP/IP : 应用层 传输层 网际层 主机至网络层
网络通讯要素: IP地址 端口号 传输协议
传输协议常见协议:TCP,UDP
进程/应用的协议:Telnet,FTP,HTTP,DNS等。
主机-主机的协议:TCP,UDP。
Internet协议:IP,ICMP,ARP等。
TCP和UDP区别:
TCP:
1,建立连接,形成传输数据的通道。
2,在连接中进行大数据量传输。
3,通过三次握手完成连接,是可靠协议。
4,必须建立连接,效率会稍低。
UDP:
1,将数据及源和目的封装成数据包中,不需要建立连接。
2,每个数据包的大小都限制在64K内。
3,因为没有连接,是不可靠协议。
4,不需要建立连接,速度快。
----UDP传输:
DatagramSocket与DatagramPacket 建立发送端,接收端。建立数据包,调用Socket的发送接收方法,关闭Socket。 发送端和接收端是两个独立的运行程序。
Socket:
1,Socket就是为网络服务提供的一种机制。
2,通信的两端都有Socket。
3,网络通信其实就是Socket间的通信。
4,数据在两个Socket间通过IO传输。
1,三次握手(重点)?
TCP 提供面向有连接的通信传输。面向有连接是指在数据通信开始之前先做好两端之间的准备工作。 所谓三次握手是指建立一个 TCP连接时需要客户端和服务器端总共发送三个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发。
第一次握手:客户端将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给服务器端,客户端进入SYN_SENT状态,等待服务器端确认。
第二次握手:服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给客户端以确认连接请求,服务器端进 入SYN_RCVD状态。 第三次握手:客户端收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务器端,服务器端 检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。
2,四次挥手?
四次挥手即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。由于TCP连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关 闭,而另一方则执行被动关闭。
1. 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的 后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
2.服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CL OSE-WAIT状态 持续的时间。
3. 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送 的后的数据)。
4.服务器将后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的 序列号为seq=w,此时,服务器就进入了LAST-ACK(后确认)状态,等待客户端的确认。
5. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自 己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(长报文段寿命)的时间后,当客户端撤销相 应的TCB后,才进入CLOSED状态。
6. 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结 束TCP连接的时间要比客户端早一些。
3,同步,异步,阻塞,非阻塞?
同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。
例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事
异步: 异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
例如 ajax请求(异步): 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕
阻塞: 阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。 有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回,它还会抢占cpu去执 行其他逻辑,也会主动检测io是否准备好。
非阻塞 非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
再简单点理解就是:
1. 同步,就是我调用一个功能,该功能没有结束前,我死等结果。
2. 异步,就是我调用一个功能,不需要知道该功能结果,该功能有结果后通知我(回调通知)
3. 阻塞,就是调用我(函数),我(函数)没有接收完数据或者没有得到结果之前,我不会返回。
4. 非阻塞,就是调用我(函数),我(函数)立即返回,通过select通知调用者
4,Linux的五种IO模型?
1)阻塞I/O(blocking I/O) 应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。
如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。 当调用recv()函数时,系统首先查是否有准备好的数据。如果数据没有准备好,那么系统就处于等待状态。当数据准备好后,将数据从系统缓冲区复制到用户空间,然后该函数返回。在套接应用程序中,当调用recv()函数时,未必用户空间就已经存在数据,那么此时recv()函数就会处于等待状态。
2)非阻塞I/O (nonblocking I/O)
我们把一个SOCKET接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们 的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间。上述模型绝不被推荐。把SOCKET设置为非阻塞模式,即通知系统内核:在调用Windows Sockets API时,不要让线程睡眠,而应该让函数立即返回。在返回时,该函数返回一个错误代码。图所示,一个非阻塞模式套接字多次调用recv()函数的过程。前三次调用recv()函数时,内核数据还没有准备好。因此,该函数立即返回WSAEWOULDBLOCK错误代码。第四次调用recv()函数时,数据已经准备好,被复制到应用程序的缓冲区中,recv()函数返回成功指示,应用程序开始处理数据。
3)I/O复用(select 和poll) (I/O multiplexing)
简介:主要是select和epoll;对一个IO端口,两次调用,两次返回,比阻塞IO并没有什么优越性;关键是能实现同时对多个IO端口进行监听; I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。当用户进程调用了select,那么整个进程会被block;而同时,kernel会“监视”所 有select负责的socket;当任何一个socket中的数据准备好了,select就会返回。这个时候,用户进程再调用read操作,将数据从kernel拷贝到用户进程。这个图和blockingIO的图其实并没有太大的不同,事实上还更差一些。因为这里需要使用两个系统调用(select和recvfrom),而blockingIO只调用了一个系统调用(recvfrom)。但是,用select的优势在于它可以同时处理多个connection。(select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)
4)信号驱动I/O (signal driven I/O (SIGIO))
简介:两次调用,两次返回; 首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作 函数处理数据。
5)异步I/O (asynchronous I/O (the POSIX aio_functions))
当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状 态、通知和回调来通知调用者的输入输出操作,linux下的异步IO其实不是完全实现了异步,真正完全实现了异步IO的是windows下的IOCP。netty5使用了异步IO,但是现在 停止开发了,都是用netty4.
6) 同步IO和异步IO的区别就在于:数据拷贝的时候进程是否阻塞
阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回
NIO和IO的区别?
1、面向流与面向缓冲,Java IO和NIO之间第一个大的区别是,IO是面向流的,NIO是面向缓冲区的。JavaIO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。JavaNIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
2、阻塞与非阻塞IO
Java IO的各种流是阻塞的。这意味着,当一个线程调用read()或write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。JavaNIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。
非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
3、选择器(Selectors)
Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
5,AIO,BIO,NIO?
1,同步阻塞IO(BIO)
我们熟知的Socket编程就是BIO,一个socket连接一个处理线程(这个线程负责这个Socket连接的一系列数据传输操作)。
阻塞的原因在于:操作系 统允许的线程数量是有限的,多个socket申请与服务端建立连接时,服务端不能提供相应数量的处理线程,没有分配到处理线程的连接就会阻塞等待或被拒绝。
2,同步非阻塞IO(NIO)
New IO是对BIO的改进,基于Reactor模型。我们知道,一个socket连接只有在特点时候才会发生数据传输IO操作,大部分时间这个“数据通道”是空闲的,但还是占用着线程。NIO作出的改进就是“一个请求一个线程”,在连接到服务端的众多socket中,只有需要进行IO操作的才能获取服务端的处理线程进行IO。这样就不会因为线程不够用而限制了socket的接入。客户端的socket连接到服务端时,就会在事件分离器注册一个IO请求事件和IO事件处理器。在该连接发生IO请求时,IO事件处理器就会启动一个线程来处理这个IO请求,不断尝试获取系统的IO的使用权限,一旦成功(即:可以进行IO),则通知这个socket进行IO数据传输。
3,异步阻塞IO(AIO)
NIO是同步的IO,是因为程序需要IO操作时,必须获得了IO权限后亲自进行IO操作才能进行下一步操作。AIO是对NIO的改进(所以AIO又 叫NIO.2),它是基于Proactor模型的。每个socket连接在事件分离器注册IO完成事件和IO完成事件处理器。程序需要进行IO时,向分离器发出IO请求并把所用的Buffer区域告知分离器,分离器通知操作系统进行IO操作,操作系统自己不断尝试获取IO权限并进行IO操作(数据保存在Buffer区),操作完成后通知分离器;分离器检测到IO完成事件,则激活 IO完成事件处理器,处理器会通知程序说“IO已完成”,程序知道后就直接从Buffer区进行数据的读写。
异步IO:客户端的连接,发送数据,接收数据 都是异步的,服务端的连接,接收数据,发送数据都是异步的。
注意:AIO是发出IO请求后,由操作系统自己去获取IO权限并进行IO操作;NIO则是发出IO请求后,由线程不断尝试获取IO权限,获取到后通知应用程序自己进行IO操作。当时 客户端应用可以使用BIO,如果是服务端建议使用BIO或者AIO。
6,selector,poll和epoll?
1、支持一个进程所能打开的大连接数
select:
单个进程所能打开的大连接数有FD_SETSIZE宏定义,其大小是32个整数的大 小(在32位的机器上,大小就是3232,同理64位机器上FD_SETSIZE为3264),可以对进行修改,然后重新编译内核,但是性能可能会受到影响。
poll:poll本质上 和select没有区别,但是它没有大连接数的限制,原因是它是基于链表来存储的
epoll:连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接 2、FD剧增后带来的IO效率问题
select:因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。
poll:同上 epoll:因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。
3、 消息传递方式 select:内核需要将消息传递到用户空间,都需要内核拷贝动作
poll:同上
epoll:epoll通过内核和用户空间共享一块内存来实现的。
7,原生JDK网络编程- NIO之Reactor模式?
有点类似于观察者模式(一个数据源,即是一个被观察者,多个观察者,一对多),NIO之Reactor模式(反应器),观察多个客 户端连接,即是多对多的关系,所以又有点不同。Netty服务端使用了“多Reactor线程模式”。
8,buffer缓冲区?
Buffer用于和NIO通道进行交互。数据是从通道读入缓冲区,从缓冲区写入到通道中的。以写为例,应用程序都是将数据写入缓冲,再通过通道把缓冲的数据发送出去,读也是一 样,数据总是先从通道读到缓冲,应用程序再读缓冲的数据。 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供 了一组方法,用来方便的访问该块内存。
重要属性:
1,capacity 作为一个内存块,Buffer有一个固定的大小值,也叫“capacity”.你只能往里写capacity个byte、long,char等类型。一旦Buffer满了,需要将其清 空(通过读数据或者清除数据)才能继续写数据往里写数据。
2,position 当你写数据到Buffer中时,position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position大可为capacity–1.当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0.当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
3,limit在写模式下,Buffer的limit表示你多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity。 当切换Buffer到读模式时,limit表示你多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)
注意:buffer可以在堆内存上分配,也可以在直接内存上分配,一般建议在堆内存上分配。
9,java原生NIO模型?
1、reactor(反应器)模式
使用单线程模拟多线程,提高资源利用率和程序的效率,增加系统吞吐量。
2、NIO中的重要概念 通道、缓冲区、选择器
通道(Chnannle):类似于流,但是可以异步读写数据(流只能同步读写),通道是双向的,(流是单向的),通道的数据总是要先读到一个buffer或者 从一个buffer写入,即通道与buffer进行数据交互。
缓冲区(Buffer):本质上是一块可以存储数据的内存,被封装成了buffer对象而已!
选择器也叫多路复用器(Selector):相当于一个观察者,用来监听通道感兴趣的事件,一个选择器可以绑定多个通道;
操作类型(SelectionKey):提供读、写、连接、接收连接的操作。注意:一般注册OP_READ,不注册OP_WRITE,OP_WRITE用于表示底层缓冲区是否有空间,是则返回TRUE。
10,为什么要用Netty?
1、虽然JAVA NIO框架提供了 多路复用IO的支持,但是并没有提供上层“信息格式”的良好封装。例如前两者并没有提供针对 Protocol Buffer、JSON这些信息格式的封 装,但是Netty框架提供了这些数据格式封装(基于责任链模式的编码和解码功能);
2、直接使用NIO需要需要额外的技能,例如Java多线程,网络编程;
3、要编写一个可 靠的、易维护的、高性能的NIO服务器应用。除了框架本身要兼容实现各类操作系统的实现外。更重要的是它应该还要处理很多上层特有服务,例如:客户端的权限、还有上面提到的信息格式封装、简单的数据读取,断连重连,半包读写,心跳等等,这些Netty框架都提供了响应的支持。
4、JAVA NIO框架存在一个poll/epollbug:Selectordoesn’tblockonSelector.select(timeout),不能block意味着CPU的使用率会变成100%(这是底层JNI的问题,上层要处理这个异常实际上也好办)。当然这个bug只有在Linux内核上才能重现。 这个问题在JDK 1.7版本中还没有被完全解决,但是Netty已经将这个bug进行了处理。这个Bug与操作系统机制有关系的,JDK虽然仅仅是一个兼容各个操作系统平台的软件,但在JDK5和JDK6初的版本中(严格意义上来将,JDK部分版本都是),这个问题并没有解决,而将这个帽子抛给了操作系统方,这也就是这个bug终一直到2013年才终修复的原因(JDK7和JDK8之间)。
11,为什么不用Netty5
- netty5 中使用了 ForkJoinPool,增加了代码的复杂度,但是对性能的改善却不明显
- 多个分支的代码同步工作量很大
- 个人觉得当下还不到发布一个新版本的时候
- 在发布版本之前,还有更多问题需要调查一下,比如是否应该废弃exceptionCaught,是否暴露EventExecutorChooser等等。
12,为什么Netty使用NIO而不是AIO?
Netty不看重Windows上的使用,在Linux系统上,AIO的底层实现仍使用EPOLL,没有很好实现AIO,因此在性能上没有明显的优势,而且被JDK封装了一层不容易深度优化。AIO还有个缺点是接收数据需要预先分配缓存,而不是NIO那种需要接收时才需要分配缓存, 所以对连接数量非常大但流量小的情况,内存浪费很多。据说Linux上AIO不够成熟,处理回调结果速度跟不上处理需求,有点像外卖员太少,顾客太多,供不应求,造成处理速度有瓶颈。
小结
区分同步,异步与阻塞与非阻塞的区别,和io的几种流,如常用对字符流,字节流,或者缓冲流等,对于网络编程要了解常见的TCP/IP协议,与URL、UDP与Socket的知识点!当然里面还有一些细节的知识点,例如不同流的区别!这就是网络编程的常见问题!最后感谢你的阅读,希望你有所收获,对于Java入门知识点我会在九月下旬开始把我的博客搬过来,如果现在看不懂的Java入门童鞋不要紧张!这个有利于Java面试的童鞋!!
棒!
这一部分还没有完全理解,可能有错误!!!hhh1 有没有评审团来检查一下错误!!