网络编程的基本模型是c/s模型,即客户端和服务端进行通信。
服务端通过监听自身ip和绑定的端口提供服务,客服端通过制定的ip和端口发起请求,经过三次握手请求,确认连接成功,然后进行通信;而在Java发展的历程上来看主要分为三个模式
BIO:阻塞同步通信
NIO:非阻塞同步通信
AIO:非阻塞异步通信
1.传统IO编程(同步阻塞模式):
由图可以看出来:(上图是引用别人的图)
每一个client的访问在服务端都会产生一个线程用来处理,并在处理完后再返回clent端,最后线程销毁
缺点就是在访问量大的时候服务端的线程数量会快速增加,系统的性能则会急剧下降,最后系统崩溃(线程是操作系统宝贵的资源,windows 线程上线大于在1000左右,而Linux上线大约在2000左右,所以在连接数量大的时候,系统死的会很快,即系统无法满足高并发,高性能应用)
BIO serverSocket端
ServerSocket端方法实现
(1).创建绑定监听端口的
SeverSocket对象,用于服务端和客户端之间请求链接
(2).server.accept()
程序阻塞等待客户端的访问并返回socket对象
(3).利用
accept()返回的socket 对象进行IO的读写操作(获取数据和发送数据)
(4).完成后关闭IO流 和 释放socket对象
- ServerSocket server = null;
- server = new ServerSocket(PROT);//
- System.out.println(" server start .. ");
- //2、进行阻塞(防止程序启动后直接执行到底,等待客户端的链接)
- Socket socket = server.accept();
- InputStream is = socket.getInputStream();
- InputStreamReader isr =newInputStreamReader(is);
- BufferedReader br =newBufferedReader(isr);
- while((info=br.readLine())!=null){
- System.out.println("Hello,我是服务器,客户端说:"+info);
- socket.shutdownInput();//关闭输入流
- OutputStream os = socket.getOutputStream();
- PrintWriter pw = new PrintWriter(os);
- pw.write("Hello World!");
- } catch (IOException e) {
clent端代码
(1).用服务器的IP地址和端口号实例化Socket对象。
(2).获得Socket上的流,把流封装进BufferedReader/PrintWriter的实例,以进行读写
(3).利用Socket提供的getInputStream和getOutputStream方法,通过IO流对象,向服务器发送数据流
(4).关闭打开的流和Socket。
- BufferedReader in = null;
- socket = new Socket("127.0.0.1", "9999");
- in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
- out = new PrintWriter(socket.getOutputStream(), true);
- out.println("接收到客户端的请求数据...");
- out.println("接收到客户端的请求数据1111...");
- String response = in.readLine();
- System.out.println("Client: " + response);
- //下面的关闭查找放在 finally 中,保证程序完整执行
为了解决这种情况 ,可以利用线程池来管理(限制线程最大请求数量,来减少对线程的消耗,但是连接超过最大数是只能等待有空余的线程可以复用的时候才能执行)
2、NIO编程(同步非阻塞模式)
NIO在表面上看来就是在请求和服务处理中间加了个缓冲区,即
请求数据是先将数据保存到缓冲区 然后传递个处理程序 ,处理程序再将返回值保存到缓冲区中,最后在传递给请求客户端
clent请求 ------> |server---> 缓存buff-----> 程序处理Handler
程序处理Handler ------> |server---> 缓存buff-----> clent请求
其核心组件有三个
Channels
Buffers
Selectors
Channel :通道,是指客服端和服务端之间的连接通道,用来判断服务端和客户端的链接以及两者之间的数据流传输。
Buffers : 缓存,存在与服务端一个组件,用于缓存接收客户端发来的数据,或者缓存服务端处理完后要发往客户端的数据,当数据完全缓存到buffers 的时候在传递给业务逻辑处理,或者将数据传输给客户端。
Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。
dmo
Server端
- public class NIOService {
- private static int DEFAULT_PORT = 12345;
- private static ServerSocket server;
- private static ExecutorService executorService = Executors.newFixedThreadPool(60);
- //根据传入参数设置监听端口,如果没有参数调用以下方法并使用默认值
- public static void start() throws IOException{
- public synchronized static void start(int port) throws IOException{
- if(server != null) return;
- server = new ServerSocket(port);
- System.out.println("服务器已启动,监听端口为:" + port);
- //如果没有客户端接入,将阻塞在accept操作上。
- socket = server.accept();
- executorService.execute(new DoSothingHandler(socket));
- System.out.println("服务器已关闭。");
clent端代码
- //定义检测SocketChannel的Selector对象
- private Selector selector=null;
- private Charset charset=Charset.forName("UTF-8");
- private SocketChannel sc=null;
- public void init() throws IOException{
- selector=Selector.open();
- InetSocketAddress isa=new InetSocketAddress("127.0.0.1",12345);
- sc=SocketChannel.open(isa);
- sc.configureBlocking(false);
- //将Socketchannel对象注册到指定Selector
- sc.register(selector, SelectionKey.OP_READ);
- new ClientThread().start();
- Scanner scan=new Scanner(System.in);
- while(scan.hasNextLine()){
- String line=scan.nextLine();
- //将键盘输入的内容输出到SocketChannel中
- sc.write(charset.encode(line));
未完待续(如有不足之处请指出)