任务:客户端每隔两秒发送一个带有时间戳的 “hello, world!” 消息给服务端,服务端收到消息之后打印该信息。
我们新建两个类:IOServer
和 IOClient
来分别表示服务端和客户端。
IOServer.java
:
1 | public class IOServer { |
IOServer
中我们做了几件事:
- 创建了一个
ServerSocket
来监听 3333 端口并创建一个线程(第5行),线程里面不断调用阻塞方法serversocket.accept();
获取新的连接(第10行) - 一旦获取到新的连接,给每个连接创建一个新的线程,这个线程负责从该连接中读取数据(第11行),以字节流(InputStream)的方式读取数据(15-18行)。
IOClient.java
:
1 | public class IOClient { |
IOClient
中我们每隔2秒向服务端发送一条格式为 timestamp: hello,world!
的消息。
通过 IOServer
和 IOClient
我们基于传统 IO 模型成功完成了任务,但仅限于完成了任务。
由于针对每一个 IOClient
实例,IOServer
都会创建一个新线程来维护连接和处理数据,这在客户端(IOClient)实例较少的时候可以正常工作。一旦客户端增多,服务端(IOServer)可能要支撑成千上万的连接,传统 IO 模型在此时就会显得比较吃力:
- 每一个客户端都需要一个新线程,会导致线程资源受限;而线程资源是操作系统中极其重要的资源,客户端每隔2秒才发送一次消息,这意味着大量的服务端线程处理阻塞状态,这是非常严重的资源浪费
- 一旦客户端数据激增,在服务端实例一定的情况下,将会导致服务端线程爆炸。而单机可认为 CPU 数是固定的,因此大量 CPU 时钟会被用来进行线程切换,而实际处理工作的时钟减少,造成性能下降
- 另一方面,数据的读写是以字节流(InputStream、OutputStream 为单位进行的),存在效率问题
如果要在客户实例比较多的场景完成这个任务,我们需要解决上面的三个问题。