ServerSocket三个参数:port,backlog,bindAddr。
绑定端口port:绑定失败将抛出BindException,可能由于端口已被占用或端口不能被该用户使用(例如1~1023可能只能用root用户绑定)所致,参数0表示操作系统分配,称为匿名端口。
客户连接请求队列的长度:客户端连接请求由操作系统的先进先出队列管理,ServerSocket通过accept方法取出该队列的请求,backlog用于改变请求队列长度,但是如果该值大于操作系统限制或小于等于0则依然使用操作系统的值。
设定绑定的IP地址:bindAddr参数,多IP时可以指定绑定到哪个IP
不带参数的构造器:创建完ServerSocket对象后需要调用bind方法,bind之前可以设置一些参数,有些参数必须在bind之前调用。
accept方法会阻塞,客户端连接上来后才返回。
ServerSocket不用close,程序结束时会释放,也可显示调用。
ServerSocket的isClosed,isBound与Socket类似。
获取服务器绑定IP和端口:getInetAddress和getLocalPort
FTP使用匿名端口
ServerSocket选项:
1. SO_TIMEOUT:accept等待客户端连接超时的时间,0表示永远不超时,一直等待客户端连接。
2. SO_REUSEADDR:和Socket的类似
3. SO_RCVBUF:接收数据的缓冲区的大小,与操作系统有关。
连接时间、延迟和带宽的相对重要性:与Socket类似
多线程服务器(重点)
前面的例子中的服务器都无法同时响应多个客户连接请求。
使用线程池注意事项书(74-76页)
如何正确关闭服务器(书76-80页)
第4章 非阻塞通信
ServerSocket和Socket运行过程中可能会阻塞,如果服务器需要与多个客户端通信,需要多个线程。每个线程都有可能处于长时间阻塞状态。利用JDK的非阻塞机制,服务器只需创建一个线程,就能完成同时与多个客户端通信。
线程阻塞:放弃CPU、暂停运行、等到阻塞的原因消除,才能恢复运行;或者被其他线程终端,该线程会退出阻塞状态,并且抛出InterruptedException。
导致线程阻塞的原因:
(1) 线程执行了Thread.sleep
(2) 线程要执行一段同步代码,由于无法获得同步锁而进入阻塞状态
(3) 线程执行了一个对象的wait方法
(4) IO操作或远程通信时,等待资源而进入阻塞状态。
客户端可能阻塞的情况:
(1) 请求与服务器建立连接
(2) 线程从Socket的输入流读入数据
(3) 线程向Socket的输出流写一批数据
(4) 调用了setSoLinger方法设置了关闭Socket的延迟时间
服务端可能阻塞的情况:
(1) 线程执行ServerSocket的accept方法,等待客户端连接。
(2) 线程从Socket的输入流读入数据
(3) 线程向Socket的输出流写一批数据
阻塞IO和非阻塞IO:这种可能出现阻塞的称为阻塞IO,否则是非阻塞IO。
(1) 虚拟机会为每个线程分配独立的堆栈空间,线程越多,系统管理压力越大,单纯的增加线程并不能使性能持续上升。线程数量要合理,并不是越多越好。
(2) 许多时间浪费在阻塞IO上。
非阻塞思想:
服务器程序只需一个线程就能同时负责接收客户的连接、接收各个客户发送的数据,以及向各个客户发送响应数据。采用轮询方式,当某一种操作就绪时,就执行该操作,否则就查看是否还有其他就绪的操作可以执行。所谓非阻塞就是如果操作还没就绪,立即返回,而不会一直等到操作就绪。这时while循环是阻塞住的,一般有个特定的退出条件。
Java.nio包的非阻塞通信类:
ServerSocketChannel和SocketChannel:支持阻塞非阻塞,代替ServerSocket和Socket
Selector:为ServerSocketChannel监控连接就绪事件,为SocketChannel监控连接就绪、读就绪、写就绪事件。
SelectionKey:注册事件的句柄,当一个SelectionKey对象位于Selector对象的selected-keys集合中时,就表示与这个SelectionKey对象相关的事件发生了。
ByteBuffer:字节缓冲区。read和write都把数据放入缓冲区中。需要用Charset转换。
缓冲区Buffer:书88页
通道Channel:书90页
SelectableChannel:书92页
ServerSocketChannel:书93页
SocketChannel:书93页
Selector类:书96页
SelectionKey类:书97页
阻塞非阻塞模式总结:书127页
第5章 创建非阻塞的HTTP服务器
参见源代码
第6章 客户端协议处理框架
通用协议处理框架封装了Socket,Java对客户程序的通信过程进行了抽象,主要包括:URL、URLConnection、URLStreamHandler、ContentHandler类。后三个需要自己实现。对于一些常用协议JDK有相应实现,这个主要为了简化Socket操作。例如:JDK为HTTP协议实现了协议处理框架。用户可以对于自己的协议进行实现。
第8章 基于UDP的数据报和套接字
TCP协议是网络传输层的一种可靠的数据传输协议。如果数据在传输途中丢失或损坏,TCP会保证再次发送数据:如果数据到达接收方的顺序被打乱,TCP会在接收方重新恢复数据的正确顺序。应用层无须担心接收到乱序或错误的数据。
TCP的可靠性是有代价的,这种代价就是传输速度的降低。建立和销毁TCP连接会花费很长的时间。如果通信双方实际上通信的时间很短,要传输的数据很少,那么建立和销毁TCP连接的代价就相对较高。
UDP,不可靠传输,传输速度快,传输单元称为UDP数据报,无法保证数据报一定到达目的地,也无法保证发送顺序。可以通过应用层协议保证可靠性。常用于即时通信,音频视频,NFS,TFTP。
UDP是无连接协议,交换数据无需建立连接。DatagramSocket负责接收和发送UDP数据报,DatagramPacket表示UDP数据报。
DatagramPacket类:
用于发送数据的构造方法需要设定数据报到达的目的地址,而用于接收数据的构造方法无需设定地址。一般是服务端无需地址,客户端需要指明数据报发送的目的地。
数据报大小:length参数决定了数据报的长度。如果收到的数据报大于该值,则多余部分丢弃。选择原则:网络越差,数据报应越小,反之则可以加大数据报长度,8KB是个好的折中方案。
数据格式:数据报中只能存放字节形式的数据。
重用DatagramPacket:详见代码
DatagramSocket类:
接收和发送数据报:send和receive方法。
管理连接:只与特定DatagramSocket交换数据。connect方法。
关闭DatagramSocket:
选项:
SO_TIMEOUT:接收数据报时的等待超