Java Socket
1. 分类
   流套接字(stream socket,基于TCP)和数据报套接字(datagram socket,基于UDP)
2. InetAddress
   代表网络目标地址(network destination address)
   两个子类:Inet4Address,Inet6Address代表IPV4和IPV6地址;
3. NetworkInterface
   获取本地网络信息以及InetAddress类实例;

4. Socket和ServerSocket(TCP/IP套接字)
   ServerSocket负责监听客户端的Socket,当有Socket请求到来时,服务端创建一个Socket实例与客户端Socket进行通信;
   故服务器端要同时维护ServerSocket和Socket,而客户端则只维护Socket;
   对于需要多线程处理的连接,通常即每个连接产生一个Socket,然后一个线程池中的一个线程持有这个Socket来处理该请求;

5. BIO/NIO/AIO区别
   HTTP的请求模型:
      HTTP的请求模型分为几步:建立连接->传输数据发送请求->处理->发送响应返回数据->断开连接          
   BIO即通常的请求模式(ServerSocket,Socket),当建立连接时,服务器分配一个线程处理该连接的请求;
      该线程负责(传输数据发送请求->处理->发送响应返回数据)过程;
   而NIO则将请求与连接分开,用SocketChannel来处理所有的连接,使用selector来对SocketChannel的请求进行轮询,如果有请求,则分配一个线程进行处理;
      该线程仅负责(处理)这一步,(传输数据发送请求->发送响应返回数据)则通过selector轮询SocketChannel来处理;线程与SocketChannel之间需要有关联机制;
   AIO则依赖操作系统来处理请求数据和返回数据,以及实现关联机制,实现真正的异步机制;   
   故可知,
       因为BIO每个连接由对应的线程CPU来处理请求及响应,故会在I/O处浪费CPU资源,有可能会造成CPU瓶颈;                                 而NIO则由一个线程selector来处理所有的请求及响应,而专门的线程来处理每个请求,即所有连接的I/O由一个线程来处理,
   节省了CPU,但如果I/O过多,可能会在selector处造成瓶颈;因为要处理线程处理结果与SocketChannel的对应关系,不适合用多个selector且也增加了模型的复杂度;
       而AIO则去除了selector,I/O以及线程处理结果与I/O的对应关系由操作系统来完成,故效率最高,但需操作系统支持;

6. DatagramPacket和DatagramSocket(UDP套接字)
   DatagramPacket对象是所存数据报的载体,存储了所传输的数据,端口号,远程地址等;
   DatagramSocket的send方法负责发送DatagramPacket对象所包含的报文,而receive方法负责接收DatagramPacket对象所包含的报文;   
   注意:DatagramPacket对象最好不要复用,因为其会保留原始的长度信息,需要每次手动刷新;
         其getData总是返回原始数组的大小,即使DatagramPacket本身包含了长度和偏移量的信息;

7. JAVA中UDP通信方式与TCP/IP通信的区别
   UDP没有错误恢复机制,即send的数据并不会有超时重传及补传机制,同样receive也只接收一次send的数据;
   UDP无须建立连接;

8. 发送和接收数据
   发送方和接收方必须在信息的编码方式上达成一致;
   如对于数字来说:发送信息的基本单位,高位在前还是低位在前,有符号无符号等都需考虑;
   对于字符串来说,发送信息的基本单位,字符串的编码都需考虑;

9. 多任务处理
   对于多个请求,可以产生多个Socket,然后提供给多个实现了Thread接口对象,由这些对象来启动多线程处理;
   同时,也可以持有多个实现了Thread接口的对象的List,通过利用这些对象并改变该对象持有的Socket,来完成请求;

10.默认行为
   可开启keepAlive(),即如果不段时间连接上不发送数据,则对另一端发送探测消息,确定连接是否正常;
   可设置超时时间,即如果I/O操作超过一定时间不响应,则抛异常;
   
11.HTTP中的keep-alive与JAVA中的Socket
   1)为何使用keep-alive
   HTTP请求基于TCP/IP,如果多个请求都基于同一TCP/IP的连接,则可以节省每个单独建立TCP/IP连接的开销;
   2)如何触发keep-alive
   对于HTTP/1.0,需在头信息中加入Connection:keep-alive,而在HTTP/1.1中,则默认keep-alive;
   3)服务器及浏览器对keep-alive的处理
     一个原理:keep-alive需双方的参与,任何一方关闭都会使keep-alive结束;
     HTTP/1.1规定服务器如果关闭keep-alive连接,需发出一个通知;
     服务器端的处理通常会设置keep-alive有效时间,以在规定时间结束后可以释放连接占用的内存;如果有效,则会在响应头加入Connection:keep-alive;
     而浏览器端则有可能使用keep-alive来利用TCP/IP连接,也可能直接新建TCP/IP连接来请求;
       原因在于:随着硬件及操作系统的发展,客户端新建TCP/IP连接的开销实际上并不是很大,
                 比起等待第一个请求结束再发送第二个请求来说,直接新建多个TCP/IP来同时请求,得到响应的速度更快;    
   故事实上,keep-alive对于浏览器来讲,并不一定会提升性能,因为在客户端看来,瓶颈在服务器端的处理响应速度,而非浏览器多建几个TCP连接的时间和资源开销;
             keep-alive对于服务器来讲,可以连接复用,节省资源,但是如果连接上总是没有请求,则该连接在该时间内则占用了服务器的内存,浪费资源
    因此,keep-alive对于浏览器是多TCP/IP连接的CPU内存消耗和更快的得到处理结果的权衡;
                    对于服务器则是TCP/IP连接复用对CPU内存的节省和TCP/IP连接空置对CPU内存的浪费之间的合理权衡;
                    
12.NIO
   1)NIO的使用背景
      多个Socket保持长连接,连接上请求很少,但该连接却需要单独一个线程,浪费了系统的线程资源;
      需对不同的请求的设置处理优先级;
      不同的请求需要修改相同的资源,需进行同步;
   2)NIO解决的方法
      一个selector线程执有所有Socket,对Socket上的请求进行轮询,如有I/O请求,由selector处理,其它请求则由selector处理I/O部分;
      selector负责所有的线程转发,故可在转发层面控制请求的优先级;
      selector暴露了buffer,使得通过控制buffer大小以及使用操作系统的缓存来优化系统成为可能;      
      
13. Java Socket与底层TCP实现
    TCP的Socket包含了sendQ(发送缓存),recvQ(接收缓存),Delivered(接收者从缓存读取的数据)
    通用的发送过程:
    1)TCP/IP连接的建立
      TCP/IP连接建立需三次握手:客户端连接请求,服务端确认信息,客户端确认信息
      对于Socket来说,当接收到服务端确认信息时(第2步),就认为连接建立,即Socket的状态为Established;
      对于ServerSocket来说,当accept()方法接收到连接请求时,即产生Socket实例,认为连接建立,而非等到三次握手完成;    
    2)TCP/IP数据的发送接收
      Socket调用write()方法,将数据写入底层sendQ,则wirte()方法完成;    
            写入前,如果wirte()的buffer比sendQ大,则会将buffer进行分组,待sendQ发送完一部分后,再写入,直至完全写入;
      sendQ将数据发送到recvQ
            依赖于底层实现,依靠重传机制来保证数据的正确有序到达recvQ
      recvQ将数据传入read()
            recvQ依据read()方法buffer的大小,分成合适的包传入read()
    故write()结束并不代表数据已到达接收端,仅代表数据已写入sendQ,如传输过程中发生异常,则应用程序发送端不会收到任何通知;
    3)TCP/IP连接关闭
      Socket调用close()或者shutdownOutput()方法,则代表关闭;
      而此时,仍有未发送的数据存在于sendQ中,如有异常,应用程序不会知道;
      在TCP/IP协议中,则需要另一方也发送close()才算结束;