JAVA核心技术:Java网络编程(续)

知识点

Java NIO 编程

BIO

传统的TCP和UDP通讯:Blocking I/O

在这里插入图片描述
通讯效率可能由于网络不稳定等情况造成通讯效率低下。

NIO

Non-Blocking I/O, 非阻塞I/O, (又名New I/O)

  • JDK 1.4引入,1.7升级NIO 2.0 (包括了AIO)
  • 主要在java.nio包中
  • 提供非阻塞通讯等方式
  • 避免同步I/O通讯效率过低
  • 一个线程可以管理多个连接
  • 减少线程多的压力
  • 主要类
    • Buffer 缓存区
    • Channel 通道
    • Selector 多路选择器

在这里插入图片描述

Buffer 缓冲区
  • 一个可以读写的内存区域
    • ByteBuffer, CharBuffer, DoubleBuffer, IntBuffer, LongBuffer,
      ShortBuffer (StringBuffer 不是Buffer缓冲区)
  • 四个主要属性
    • capacity 容量, position 读写位置
    • limit 界限, mark 标记,用于重复一个读/写操作
Channel 通道
  • 全双工的,支持读/写(而Stream流是单向的)
  • 支持异步读写
  • 和Buffer配合,提高效率
  • ServerSocketChannel 服务器TCP Socket 接入通道,接收客户端
  • SocketChannel TCP Socket通道,可支持阻塞/非阻塞通讯
  • DatagramChannel UDP 通道
  • FileChannel 文件通道
Selector多路选择器
  • 每隔一段时间,不断轮询注册在其上的Channel
  • 如果有一个Channel有接入、读、写操作,就会被轮询出来
  • 根据SelectionKey可以获取相应的Channel,进行后续IO操作
  • 避免过多的线程
  • SelectionKey四种类型
    • OP_CONNECT
    • OP_ACCEPT
    • OP_READ
    • OP_WRITE

在这里插入图片描述

示例代码

NioServer

package nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NioServer {

    public static void main(String[] args) throws IOException {
    	int port = 8001;
    	Selector selector = null;
    	ServerSocketChannel servChannel = null;
    	
    	try {
			selector = Selector.open();
			servChannel = ServerSocketChannel.open();
			servChannel.configureBlocking(false);  //非阻塞模式
			servChannel.socket().bind(new InetSocketAddress(port), 1024);
			servChannel.register(selector, SelectionKey.OP_ACCEPT); //绑定多路选择器和通道
			System.out.println("服务器在8001端口守候");
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
    	
    	while(true)
    	{
    		try {
    			selector.select(1000); //轮询所有通道
    			Set<SelectionKey> selectedKeys = selector.selectedKeys();
    			Iterator<SelectionKey> it = selectedKeys.iterator();
    			SelectionKey key = null;
    			while (it.hasNext()) {
    				key = it.next();
    				it.remove();
    				try {
    					handleInput(selector,key);
    				} catch (Exception e) {
    					if (key != null) {
    						key.cancel();
    						if (key.channel() != null)
    							key.channel().close();
    					}
    				}
    			}
    		} 
    		catch(Exception ex)
    		{
    			ex.printStackTrace();    			
    		}
    		
    		try
    		{
    			Thread.sleep(500);
    		}
    		catch(Exception ex)
    		{
    			ex.printStackTrace();    			
    		}
    	}
    }
    
    public static void handleInput(Selector selector, SelectionKey key) throws IOException {

		if (key.isValid()) {
			// 处理新接入的请求消息
			if (key.isAcceptable()) {
				// Accept the new connection
				ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
				SocketChannel sc = ssc.accept();
				sc.configureBlocking(false);
				// Add the new connection to the selector
				sc.register(selector, SelectionKey.OP_READ);
			}
			if (key.isReadable()) {
				// Read the data
				SocketChannel sc = (SocketChannel) key.channel();
				ByteBuffer readBuffer = ByteBuffer.allocate(1024);
				int readBytes = sc.read(readBuffer);
				if (readBytes > 0) {
					readBuffer.flip();
					byte[] bytes = new byte[readBuffer.remaining()];
					readBuffer.get(bytes);
					String request = new String(bytes, "UTF-8"); //接收到的输入
					System.out.println("client said: " + request);
					
					String response = request + " 666";
					doWrite(sc, response);
				} else if (readBytes < 0) {
					// 对端链路关闭
					key.cancel();
					sc.close();
				} else
					; // 读到0字节,忽略
			}
		}
	}

	public static void doWrite(SocketChannel channel, String response) throws IOException {
		if (response != null && response.trim().length() > 0) {
			byte[] bytes = response.getBytes();
			ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
			writeBuffer.put(bytes);
			writeBuffer.flip();
			channel.write(writeBuffer);
		}
	}
}

NioClient

package nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;

public class NioClient {

	public static void main(String[] args) {

		String host = "127.0.0.1";
		int port = 8001;

		Selector selector = null;
		SocketChannel socketChannel = null;

		try 
		{
			selector = Selector.open();
			socketChannel = SocketChannel.open();
			socketChannel.configureBlocking(false); // 非阻塞

			// 如果直接连接成功,则注册到多路复用器上,发送请求消息,读应答
			if (socketChannel.connect(new InetSocketAddress(host, port))) 
			{
				socketChannel.register(selector, SelectionKey.OP_READ);
				doWrite(socketChannel);
			} 
			else 
			{
				socketChannel.register(selector, SelectionKey.OP_CONNECT);
			}

		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}

		while (true) 
		{
			try 
			{  //对通道遍历,读取通道返回值
				selector.select(1000);
				Set<SelectionKey> selectedKeys = selector.selectedKeys();
				Iterator<SelectionKey> it = selectedKeys.iterator();
				SelectionKey key = null;
				while (it.hasNext()) 
				{
					key = it.next();
					it.remove();
					try 
					{
						//处理每一个channel
						handleInput(selector, key);
					} 
					catch (Exception e) {
						if (key != null) {
							key.cancel();
							if (key.channel() != null)
								key.channel().close();
						}
					}
				}
			} 
			catch (Exception e) 
			{
				e.printStackTrace();
			}
		}
	

		// 多路复用器关闭后,所有注册在上面的Channel资源都会被自动去注册并关闭
//		if (selector != null)
//			try {
//				selector.close();
//			} catch (IOException e) {
//				e.printStackTrace();
//			}
//
//		}
	}

	public static void doWrite(SocketChannel sc) throws IOException { //向通道写数据
		byte[] str = UUID.randomUUID().toString().getBytes();
		ByteBuffer writeBuffer = ByteBuffer.allocate(str.length);
		writeBuffer.put(str);
		writeBuffer.flip();
		sc.write(writeBuffer);
	}

	public static void handleInput(Selector selector, SelectionKey key) throws Exception {

		if (key.isValid()) {
			// 判断是否连接成功
			SocketChannel sc = (SocketChannel) key.channel();
			if (key.isConnectable()) {
				if (sc.finishConnect()) {
					sc.register(selector, SelectionKey.OP_READ);					
				} 				
			}
			if (key.isReadable()) {
				ByteBuffer readBuffer = ByteBuffer.allocate(1024);
				int readBytes = sc.read(readBuffer);
				if (readBytes > 0) {
					readBuffer.flip();
					byte[] bytes = new byte[readBuffer.remaining()];
					readBuffer.get(bytes);
					String body = new String(bytes, "UTF-8");
					System.out.println("Server said : " + body);
				} else if (readBytes < 0) {
					// 对端链路关闭
					key.cancel();
					sc.close();
				} else
					; // 读到0字节,忽略
			}
			Thread.sleep(3000);
			doWrite(sc);
		}
	}
}

运行结果如如下

在这里插入图片描述
在这里插入图片描述

Java AIO 编程

基础知识

Asynchronous I/O, 异步I/O

  • JDK 1.7引入,主要在java.nio包中
  • 异步I/O,采用回调方法进行处理读写操作
  • 主要类
    • AsynchronousServerSocketChannel 服务器接受请求通道
      • bind 绑定在某一个端口 accept 接受客户端请求
    • AsynchronousSocketChannel Socket通讯通道
      • read 读数据 write 写数据
    • CompletionHandler 异步处理类
      • completed 操作完成后异步调用方法 failed 操作失败后异步调用方法

并发编程的同步:指多个线程需要以一种同步的方式来访问某一个数据结构。这里的同步的反义词是非同步的。
网络编程的同步`:指客户端和服务端直接的通讯等待方式。这里的同步的反义词是异步,即无需等待另外一段操作完成。

示例代码

AioServer

package aio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AioServer {

    public static void main(String[] args) throws IOException {  
    	AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open();   
        server.bind(new InetSocketAddress("localhost", 8001));  
        System.out.println("服务器在8001端口守候");
        
        //开始等待客户端连接,一旦有连接,做26行任务
        server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {  
            @Override  
            public void completed(AsynchronousSocketChannel channel, Object attachment) {  
            	 server.accept(null, this); //持续接收新的客户端请求
            	 
                 ByteBuffer buffer = ByteBuffer.allocate(1024); //准备读取空间
                 //开始读取客户端内容,一旦读取结束,做33行任务
                 channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                     @Override
                     public void completed(Integer result_num, ByteBuffer attachment) {
                         attachment.flip(); //反转此Buffer 
                         CharBuffer charBuffer = CharBuffer.allocate(1024);
                         CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
                         decoder.decode(attachment,charBuffer,false);
                         charBuffer.flip();
                         String data = new String(charBuffer.array(),0, charBuffer.limit());
                         System.out.println("client said: " + data);
                         channel.write(ByteBuffer.wrap((data + " 666").getBytes())); //返回结果给客户端
                         try{
                             channel.close();
                         }catch (Exception e){
                        	 e.printStackTrace();
                         }
                     }
      
                     @Override
                     public void failed(Throwable exc, ByteBuffer attachment) {
                         System.out.println("read error "+exc.getMessage());
                     }
                 });
                 

            }  
  
            @Override  
            public void failed(Throwable exc, Object attachment) {  
                System.out.print("failed: " + exc.getMessage());  
            }  
        });  

        while(true){
        	try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        }
    }  
}

AioClient

package aio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.UUID;


public class AioClient {

	public static void main(String[] a) {
		try
		{
			AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
			
			//18行连接成功后,自动做20行任务
			channel.connect(new InetSocketAddress("localhost", 8001), null, new CompletionHandler<Void, Void>() {

				public void completed(Void result, Void attachment) {
					String str = UUID.randomUUID().toString();
					
					//24行向服务器写数据成功后,自动做28行任务
					channel.write(ByteBuffer.wrap(str.getBytes()), null,
							new CompletionHandler<Integer, Object>() {

								@Override
								public void completed(Integer result, Object attachment) {
									try {
										System.out.println("write " + str + ", and wait response");
										//等待服务器响应
										ByteBuffer buffer = ByteBuffer.allocate(1024); //准备读取空间
						                 //开始读取服务器反馈内容,一旦读取结束,做39行任务
										channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
						                     @Override
						                     public void completed(Integer result_num, ByteBuffer attachment) {
						                         attachment.flip(); //反转此Buffer 
						                         CharBuffer charBuffer = CharBuffer.allocate(1024);
						                         CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
						                         decoder.decode(attachment,charBuffer,false);
						                         charBuffer.flip();
						                         String data = new String(charBuffer.array(),0, charBuffer.limit());
						                         System.out.println("server said: " + data);
						                         try{
						                             channel.close();
						                         }catch (Exception e){
						                        	 e.printStackTrace();
						                         }
						                     }
						      
						                     @Override
						                     public void failed(Throwable exc, ByteBuffer attachment) {
						                         System.out.println("read error "+exc.getMessage());
						                     }
						                 });
						                 
										channel.close();
									} catch (Exception e) {
										e.printStackTrace();
									}
								}

								@Override
								public void failed(Throwable exc, Object attachment) {
									System.out.println("write error");
								}

							});
				}

				public void failed(Throwable exc, Void attachment) {
					System.out.println("fail");
				}

			});
			Thread.sleep(10000);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}
}

每一个CompletionHandler都可以定义两个方法:completed和failed方法。
当操作成功完成,将自动回调completed方法;
如果操作发生异常,将自动回调failed方法。

运行结果如下:

在这里插入图片描述
在这里插入图片描述

三种I/O的区别

BIONIOAIO
阻塞方式阻塞非阻塞非阻塞
同步方式同步同步异步
编程难度简单较难困难
客户机/服务器线程对比1:1N:1N:1
性能

Netty 编程

基础知识

Netty, http://netty.io

  • 最早由韩国Trustin Lee 设计开发的
  • 后来由JBoss接手开发,现在是独立的Netty Project
  • 一个非阻塞的客户端-服务端网络通讯框架
  • 基于异步事件驱动模型
  • 简化Java的TCP和UDP编程
  • 支持HTTP/2, SSL等多种协议
  • 支持多种数据格式,如JSON等

关键技术

通道 Channel
  • ServerSocketChannel/NioServerSocketChannel/…
  • SocketChannel/NioSocketChannel
基于事件驱动 EventLoop
  • 为每个通道定义一个EventLoop,处理所有的I/O事件
  • EventLoop注册事件
  • EventLoop将事件派发给ChannelHandler
  • EventLoop安排进一步操作
事件
  • 事件按照数据流向进行分类
  • 入站事件:连接激活/数据读取/……
  • 出站事件:打开到远程连接/写数据/……
事件处理 ChannelHandler
  • Channel通道发生数据或状态改变
  • EventLoop会将事件分类,并调用ChannelHandler的回调函数
  • 程序员需要实现ChannelHandler内的回调函数
  • ChannelInboundHandler/ChannelOutboundHandler
ChannelHandler工作模式:责任链
  • 责任链模式
    • 将请求的接收者连成一条链
    • 在链上传递请求,直到有一个接收者处理该请求
    • 避免请求者和接收者的耦合
  • ChannelHandler可以有多个,依次进行调用
  • ChannelPipeline作为容器,承载多个ChannelHandler

在这里插入图片描述

ByteBuf
  • 强大的字节容器,提供丰富API进行操作

示例代码

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>MOOC18-08</artifactId>
  <version>0.0.1-SNAPSHOT</version>
    <properties>
  	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 	 <maven.compiler.source>14</maven.compiler.source>
 	 <maven.compiler.target>14</maven.compiler.target>
</properties>
  <dependencies>
  	<dependency>
	    <groupId>io.netty</groupId>
	    <artifactId>netty-all</artifactId> <!-- Use 'netty-all' for 4.0 or above -->
	    <version>4.1.33.Final</version>
	</dependency>
  </dependencies>
</project>
netty1
Server
package netty1;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.net.InetSocketAddress;

public class EchoServer {
    public static void main(String[] args) throws Exception {
        int port = 8001;
        final EchoServerHandler serverHandler = new EchoServerHandler();
        EventLoopGroup group = new NioEventLoopGroup();
        try {
        	//ServerBootstrap是netty中的一个服务器引导类
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)
                .channel(NioServerSocketChannel.class)  //设置通道类型
                .localAddress(new InetSocketAddress(port))  //设置监听端口
                .childHandler(new ChannelInitializer<SocketChannel>() { //初始化责任链
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(serverHandler); //添加处理类
                    }
                });

            ChannelFuture f = b.bind().sync();  //开启监听
            if(f.isSuccess()){
            	System.out.println(EchoServer.class.getName() +
                        " started and listening for connections on " + f.channel().localAddress());
            }
            
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
        }
    }    
}
package netty1;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf in = (ByteBuf) msg;
        String content = in.toString(CharsetUtil.UTF_8);
        System.out.println("Server received: " + content);
        
        ByteBuf out = ctx.alloc().buffer(1024);
        out.writeBytes((content + " 666").getBytes());
        ctx.write(out);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx)
            throws Exception {   	
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                .addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,
        Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}
Client
package netty1;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.net.InetSocketAddress;

public class EchoClient {
    
	public static void main(String[] args) throws Exception {
    	String host = "localhost";
        int port = 8001;
        
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                .channel(NioSocketChannel.class)
                .remoteAddress(new InetSocketAddress(host, port))
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch)
                        throws Exception {
                        ch.pipeline().addLast(new EchoClientHandler());
                    }
                });
            ChannelFuture f = b.connect().sync();
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
        }
    }   
}
package netty1;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

public class EchoClientHandler
    extends SimpleChannelInboundHandler<ByteBuf> {
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!",
                CharsetUtil.UTF_8));
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) {
        System.out.println(
                "Client received: " + in.toString(CharsetUtil.UTF_8));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,
        Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

运行结果如下:
在这里插入图片描述
在这里插入图片描述

netty2(添加多个Handler)
Server
package netty2;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
 
import java.net.InetSocketAddress;
 
public class EchoServer {
 
    public static void main(String[] args) throws Exception {
    	int port = 8001;
    	EchoServerFirstInHandler firstHandler = new EchoServerFirstInHandler();
    	EchoServerSecondInHandler secondHandler = new EchoServerSecondInHandler();        
        EchoServerOutHandler outHandler = new EchoServerOutHandler();
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(eventLoopGroup).
                    channel(NioServerSocketChannel.class).//指定channel使用NIO传输
                    localAddress(new InetSocketAddress(port)).//执行端口设置套接字地址
                    childHandler(new ChannelInitializer<SocketChannel>() {
                    	//Channel的channelpipeline
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline channelPipeline = socketChannel.pipeline();
                            channelPipeline.addFirst(outHandler);  
                            channelPipeline.addLast(firstHandler);
                            channelPipeline.addLast(secondHandler);
                        }
                    });
            ChannelFuture f = serverBootstrap.bind().sync();//异步绑定服务器,调用sync()方法阻塞等待直到绑定完成
            f.channel().closeFuture().sync();//获得Channel的closeFutrue,并且阻塞当前线程直到它完成
        } catch (InterruptedException e) {
            eventLoopGroup.shutdownGracefully().sync();
        }
    }
}
package netty2;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
 
import java.nio.charset.Charset;
 
public class EchoServerOutHandler extends ChannelOutboundHandlerAdapter {
 
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        System.out.println("EchoServerOutHandler write: "
           +((ByteBuf)msg).toString(Charset.defaultCharset()));
        ctx.write(msg, promise);
    }
}
package netty2;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
 
public class EchoServerFirstInHandler extends ChannelInboundHandlerAdapter {
 
 
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("FirstHandler: 注册事件");
        ctx.fireChannelRegistered();
    }
 
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("FirstHandler: 取消注册事件");
        ctx.fireChannelUnregistered();
    }
 
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("FirstHandler: 有新客户端连接接入。。。"+ctx.channel().remoteAddress());
        ctx.fireChannelActive();
    }
 
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("FirstHandler: 失去连接");
        ctx.fireChannelInactive();
    }
 
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf in = (ByteBuf) msg;
        System.out.println("FirstHandler: 读客户端传入数据="+in.toString(CharsetUtil.UTF_8));
        final ByteBuf byteBuf = Unpooled.copiedBuffer("FirstHandler channelRead data!", CharsetUtil.UTF_8);
        ctx.writeAndFlush(byteBuf);
        ctx.fireChannelRead(msg);
        //ReferenceCountUtil.release(msg);
    }
 
    public void channelReadComplete(ChannelHandlerContext ctx){
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess()) {                    
                }else { 
                }
                
            }
        });
        final ByteBuf byteBuf = Unpooled.copiedBuffer("FirstHandler channelReadComplete data!", CharsetUtil.UTF_8);
        ctx.writeAndFlush(byteBuf).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess())  {
                	System.out.println("FirstHandler: 执行成功="+future.isSuccess());
                }else {
 
                }
                //ReferenceCountUtil.release(byteBuf);
            }
        });
        ctx.fireChannelReadComplete();
    }
 
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        System.out.println("FirstHandler userEventTriggered");
    }
 
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        System.out.println("FirstHandler: channelWritabilityChanged");
    }
 
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}
package netty2;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

 
import java.util.concurrent.ScheduledExecutorService;
 
public class EchoServerSecondInHandler extends ChannelInboundHandlerAdapter {
 
 
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("SecondHandler: 注册事件");
    }
 
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("SecondHandler: 取消注册事件");
    }
 
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("SecondHandler: 有新客户端连接接入。。。"+ctx.channel().remoteAddress());
    }
 
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("SecondHandler: 失去连接");
    }
 
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf in = (ByteBuf) msg;
        System.out.println("SecondHandler: 读客户端传入数据="+in.toString(CharsetUtil.UTF_8));
        ctx.writeAndFlush(Unpooled.copiedBuffer("SecondHandler channelRead data!", CharsetUtil.UTF_8));
        //ctx.fireChannelActive();
    }
 
    public void channelReadComplete(ChannelHandlerContext ctx){
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess()) {                    
                }else { 
                }
            }
        });
        ctx.writeAndFlush(Unpooled.copiedBuffer("SecondHandler: channelReadComplete data!", CharsetUtil.UTF_8)).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess())  {
                	System.out.println("SecondHandler: 执行成功="+future.isSuccess());
                }else {
 
                }
            }
        });
    }
 
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        System.out.println("SecondHandler userEventTriggered");
    }
 
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        System.out.println("SecondHandler channelWritabilityChanged");
    }
 
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}
Client
package netty2;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.channel.socket.oio.OioSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
 
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
 
public class EchoClient {
 
    public static void main(String[] argsw) throws Exception {
    	int port = 8001;
        final EchoClientHandler clientHandler = new EchoClientHandler();
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(eventLoopGroup).
                    channel(NioSocketChannel.class).
                    remoteAddress(new InetSocketAddress("localhost",port)).
                    handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(clientHandler);
                        }
                    });
            
            //异步连接远程服务,连接远程服务成功后,输出"已经连接到服务器!"
            final ChannelFuture f = b.connect();
            f.addListener(new GenericFutureListener<Future<? super Void>>() {
                @Override
                public void operationComplete(Future<? super Void> future) throws Exception {
                    if (future.isSuccess()) {
                        System.out.println("已经连接到服务器!");
                        ByteBuf byteBuf = Unpooled.copiedBuffer("aaaaaaaaaaaaaaaa", Charset.defaultCharset());
                        ChannelFuture channelFuture = f.channel().writeAndFlush(byteBuf);
                    }else {
                        Throwable throwable = future.cause();
                        throwable.printStackTrace();
                    }
                }
            });
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            eventLoopGroup.shutdownGracefully().sync();
        }
    }
}
package netty2;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

/**
 * Listing 2.3 ChannelHandler for the client
 *
 * @author <a href="mailto:norman.maurer@gmail.com">Norman Maurer</a>
 */
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
 
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
 
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("和服务器通道已经激活");
    	//ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!", CharsetUtil.UTF_8));
    }
 
    public void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
        ByteBuf in = msg;
        System.out.println("读取服务端="+in.toString(CharsetUtil.UTF_8));
    }
 
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

运行结果如下:
在这里插入图片描述

在这里插入图片描述

Java Mail 编程

邮件基础知识

在这里插入图片描述

  • 主要协议(发送端口25, 接收端口110)

    • 发送, SMTP, Simple Mail Transfer Protocol
    • 接收, Pop3, Post Office Protocol 3, (POP)
    • 接收, IMAP, Internet Message Access Protocol, IMAP4
      • 摘要浏览
      • 选择下载附件
      • 多文件夹
      • 网络硬盘
  • 邮件格式

    • RFC822邮件格式:邮件头,文字邮件正文
    • MIME(MultiPurpose Internet Mail Extension) 图片/音频/视频等等
  • 邮件编码

    • 纯英文邮件, ASCII编码,7Bit
    • 8Bit编码
    • BASE64,6个bit, A-Za-z0-9+/
    • Quoted-printable,对每个非ASCII字符用“=“加上这个字符的十六进制编码.
  • 邮件中继:通过别人的邮件服务器(中继服务器)将你的邮件系统的邮件送到目标地址

  • 推荐材料

Java Mail

在这里插入图片描述

服务器配置

邮件服务器支持

  • 需要在邮件服务内设置,可以查看相关邮件帮助
  • 需要知道pop服务器和smtp服务器信息

在这里插入图片描述
在这里插入图片描述

工具包

javax.mail 包和javax.mail.internet 包

核心API

关键类

  • Session: 邮件会话 和HttpSession不同
  • Store: 邮件存储空间
  • Folder: 邮件文件夹
  • Message: 电子邮件
  • Address: 邮件地址
  • Transport: 发送协议类

示例代码

收取邮件
package tools;

import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
import java.util.*;

public class MailClientRecv {
  private Session session;
  private Store store;
  private String username = "aaa@126.com";
  private String password = "1234567899";
  private String popServer = "pop.126.com";
  
  public void init()throws Exception
  {
    //设置属性
    Properties  props = new Properties();
    props.put("mail.store.protocol", "pop3");
    props.put("mail.imap.class", "com.sun.mail.imap.IMAPStore");
    props.put("mail.pop3.class", "com.sun.mail.pop3.POP3Store");    

    // 创建Session对象
    session = Session.getInstance(props,null);
    session.setDebug(false); //输出跟踪日志

    // 创建Store对象
    store = session.getStore("pop3");
    
    //连接到收邮件服务器
    store.connect(popServer,username,password);
  }  
  
  public void receiveMessage()throws Exception
  {
	String folderName = "inbox";
    Folder folder=store.getFolder(folderName);
    if(folder==null)
    {
    	throw new Exception(folderName+"邮件夹不存在");
    }
    //打开信箱
    folder.open(Folder.READ_ONLY);
    System.out.println("您的收件箱有"+folder.getMessageCount()+"封邮件.");
    System.out.println("您的收件箱有"+folder.getUnreadMessageCount()+"封未读的邮件.");

    //读邮件
    Message[] messages=folder.getMessages();
    //for(int i=1;i<=messages.length;i++)
    for(int i=1;i<=3;i++)  
    {
      System.out.println("------第"+i+"封邮件-------");
      //打印邮件信息
      Message message = messages[i];
      //folder.getMessage(i).writeTo(System.out);
      System.out.println((message.getFrom())[0]);
      System.out.println(message.getSubject());
      System.out.println();
    }
    folder.close(false);  //关闭邮件夹
  }
  
  public void close()throws Exception
  {
	store.close();
  }
  
  public static void main(String[] args)throws Exception {
    MailClientRecv client=new MailClientRecv();
    //初始化
    client.init();
    //接收邮件
    client.receiveMessage();
    //关闭连接
    client.close();
  }
}
发送邮件
package tools;

import javax.mail.*;
import java.util.*;
import messages.*;


public class MailClientSend {
  private Session session;
  private Transport transport;
  private String username = "aaa@edu.cn";
  private String password = "1234567899";
  private String smtpServer = "webmail.ecnu.edu.cn";
  
  public void init()throws Exception
  {
	//设置属性
    Properties  props = new Properties();
    props.put("mail.transport.protocol", "smtp");
    props.put("mail.smtp.class", "com.sun.mail.smtp.SMTPTransport");
    props.put("mail.smtp.host", smtpServer); //设置发送邮件服务器
    props.put("mail.smtp.port", "25");
    props.put("mail.smtp.auth", "true"); //SMTP服务器需要身份验证    

    // 创建Session对象
    session = Session.getInstance(props,new Authenticator(){   //验账账户 
        public PasswordAuthentication getPasswordAuthentication() { 
          return new PasswordAuthentication(username, password); 
        }            
 });
    session.setDebug(true); //输出跟踪日志
    
    // 创建Transport对象
    transport = session.getTransport();           
  }
  
  public void sendMessage()throws Exception{
    //创建一个邮件
    //Message msg = TextMessage.generate();
	//Message msg = HtmlMessage.generate();
	Message msg = AttachmentMessage.generate();
    //发送邮件    
    transport.connect();
    transport.sendMessage(msg, msg.getAllRecipients());
    //打印结果
    System.out.println("邮件已经成功发送");
  } 
  
  public void close()throws Exception
  {
	transport.close();
  }
  
  public static void main(String[] args)throws Exception {
	  
    MailClientSend client=new MailClientSend();
    //初始化
    client.init();
    //发送邮件
    client.sendMessage();
    //关闭连接
    client.close();
  }
}
不同类型的邮件
文本邮件
package messages;

import java.util.Date;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.FileOutputStream;

public class TextMessage {
	public static MimeMessage generate() throws Exception {
		String from = "lychen@sei.ecnu.edu.cn "; // 发件人地址
		String to = "chenliangyu1980@126.com"; // 收件人地址
		
		String subject = "test";
		String body = "您好,这是来自一封chenliangyu的测试邮件";

		// 创建Session实例对象
		Session session = Session.getDefaultInstance(new Properties());
		// 创建MimeMessage实例对象
		MimeMessage message = new MimeMessage(session);
		// 设置发件人
		message.setFrom(new InternetAddress(from));
		// 设置收件人
		message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
		// 设置发送日期
		message.setSentDate(new Date());
		// 设置邮件主题
		message.setSubject(subject);
		// 设置纯文本内容的邮件正文
		message.setText(body);
		// 保存并生成最终的邮件内容
		message.saveChanges();

		// 把MimeMessage对象中的内容写入到文件中
		//msg.writeTo(new FileOutputStream("e:/test.eml"));
		return message;
	}
}
HTML邮件(带图片)
package messages;

import java.util.Date;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.FileOutputStream;

public class HtmlMessage {
	public static MimeMessage generate() throws Exception 
	{
		String from = "lychen@sei.ecnu.edu.cn "; // 发件人地址
		String to = "chenliangyu1980@126.com"; // 收件人地址
		
		String subject = "HTML邮件";
		String body = "<a href=http://www.ecnu.edu.cn>" 
		  + "<h4>欢迎大家访问我们的网站</h4></a></br>" 
		  + "<img src=\"https://news.ecnu.edu.cn/_upload/article/images/2e/e2/6b554d034c9192101208c732195e/16a6ec66-6729-4469-a5f4-0435e0f2e66a.jpg\">";

		// 创建Session实例对象
		Session session = Session.getDefaultInstance(new Properties());
		// 创建MimeMessage实例对象
		MimeMessage message = new MimeMessage(session);
		// 设置发件人
		message.setFrom(new InternetAddress(from));
		// 设置收件人
		message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
		// 设置发送日期
		message.setSentDate(new Date());
		// 设置邮件主题
		message.setSubject(subject);
		// 设置HTML格式的邮件正文
		message.setContent(body, "text/html;charset=gb2312");
		// 保存并生成最终的邮件内容
		message.saveChanges();

		// 把MimeMessage对象中的内容写入到文件中
		//msg.writeTo(new FileOutputStream("e:/HtmlMessage.eml"));
		return message;
	}
}
带附件的邮件
package messages;

import java.io.FileOutputStream;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
public class AttachmentMessage 
{
	public static MimeMessage generate() throws Exception
	{
		String from = "lychen@sei.ecnu.edu.cn "; // 发件人地址
		String to = "chenliangyu1980@126.com"; // 收件人地址
		
        String subject = "多附件邮件";        //邮件主题
        String body = "<a href=http://www.ecnu.edu.cn>" +
        			  "欢迎大家访问我们的网站</a></br>"; 
      
        // 创建Session实例对象
        Session session = Session.getDefaultInstance(new Properties());
     	// 创建MimeMessage实例对象
     	MimeMessage message = new MimeMessage(session);            
        message.setFrom(new InternetAddress(from));
        message.setRecipients(Message.RecipientType.TO,
        		InternetAddress.parse(to));
        message.setSubject(subject);
        
        //创建代表邮件正文和附件的各个MimeBodyPart对象
        MimeBodyPart contentPart = createContent(body);
        MimeBodyPart attachPart1 = createAttachment("c:/temp/ecnu4.jpg");
        MimeBodyPart attachPart2 = createAttachment("c:/temp/ecnu5.jpg");
        
        //创建用于组合邮件正文和附件的MimeMultipart对象
        MimeMultipart allMultipart = new MimeMultipart("mixed");
        allMultipart.addBodyPart(contentPart);
        allMultipart.addBodyPart(attachPart1);
        allMultipart.addBodyPart(attachPart2);
        
        //设置整个邮件内容为最终组合出的MimeMultipart对象
        message.setContent(allMultipart);
        message.saveChanges();
        
        //message.writeTo(new FileOutputStream("e:/ComplexMessage.eml"));
        return message;
	}
	
	public static MimeBodyPart createContent(String body) throws Exception
	{
        MimeBodyPart htmlBodyPart = new MimeBodyPart();          
        htmlBodyPart.setContent(body,"text/html;charset=gb2312");
        return htmlBodyPart;
	}
	
	public static MimeBodyPart createAttachment(String filename) throws Exception
	{
		//创建保存附件的MimeBodyPart对象,并加入附件内容和相应信息
		MimeBodyPart attachPart = new MimeBodyPart();
        FileDataSource fds = new FileDataSource(filename);
        attachPart.setDataHandler(new DataHandler(fds));
        attachPart.setFileName(fds.getName());
		return attachPart;
	}
}

练习

请基于NIO(第6章第六节的NIO,非AIO)编写一个群聊的程序,包括服务端程序和客户端程序。

服务端功能:只用一个线程,收到某客户端的信息,将消息在控制台输出,然后,发给其他另外的客户端。

客户端功能:每隔5秒发送一条信息给服务端。然后接收服务器转发过来的消息,并在控制台输出。

服务端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;

public class GroupChatServer {

	// 定义属性信息
	private ServerSocketChannel serverSocketChannel; // 在服务器端监听新的sockerChannnel连接
	private Selector selector; // 选择器
	private static final Integer PORT = 8001; // 监听的端口信息

	/**
	 * 定义构造方法
	 */
	public GroupChatServer() {
		try {
			serverSocketChannel = ServerSocketChannel.open();
			selector = Selector.open();
			serverSocketChannel.configureBlocking(false); // 设置管道为非阻塞模式
			serverSocketChannel.socket().bind(new InetSocketAddress(PORT));// 设置端口信息
			// 把通道注册到selector上,事件为监听事件
			serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
			System.out.println("服务器在8001端口守候");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 定义监听客户端有连接请求的方法
	 */
	public void seleorSocketChannel() {
		try {
			// 循环进行处理
			while (true) {
				// 如果连接大于0,有新的连接请求
				if (selector.select() > 0) {
					Iterator<SelectionKey> selectionKeyIterator = selector.selectedKeys().iterator();
					while (selectionKeyIterator.hasNext()) {
						SelectionKey selectionKey = selectionKeyIterator.next();
						// 如果事件是监听事件,则把事件注册到选择器,事件为读
						if (selectionKey.isAcceptable()) {
							SocketChannel socketChannel = serverSocketChannel.accept();
							socketChannel.configureBlocking(false);
							socketChannel.register(selector, SelectionKey.OP_READ);
							// 有新的用户上线
							System.out.println("有新用户上线了!!!");
						}
						// 如果事件是读的事件,把读取获取到的数据信息
						if (selectionKey.isReadable()) {
							readData(selectionKey);
						}
					}
					// 移除使用过的selectionKeyIterator
					selectionKeyIterator.remove();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 读取传输过来的数据
	 * 
	 * @param selectionKey 注册关系
	 */
	private void readData(SelectionKey selectionKey) {
		SocketChannel channel = null;
		try {
			channel = (SocketChannel) selectionKey.channel();// 获取通道信息
			// ByteBuffer buffer = (ByteBuffer)selectionKey.attachment();//获取缓冲区
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			// 开始读取数据信息
			if (channel.read(buffer) > 0) {
				String msg = new String(buffer.array());
				System.out.println(channel.getLocalAddress().toString().substring(1) + ":说" + msg);
				// 把消息转发给其他客户端
				sendOtherClients(channel, msg);
			}
		} catch (Exception e) {
			try {
				System.out.println(channel.getLocalAddress().toString().substring(1) + "离线了!!!");
				selectionKey.cancel();// 关闭selectionKey
				channel.close();// 关闭channel
			} catch (Exception e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
	}

	/**
	 * 转发信息给其他客户端,但是要排除自己
	 * 
	 * @param channel 通道
	 * @param msg     发送的信息
	 */
	private void sendOtherClients(SocketChannel channel, String msg) {
		//selector.keys()代表注册到选择器上的所有通道
		//selector.selectedKeys()代表监听的时候注册到选择器上的所有通道
		try {
			Set<SelectionKey> keys = selector.keys();
			for (SelectionKey key : keys) {
				SelectableChannel selectableChannel = key.channel();
				if (selectableChannel instanceof SocketChannel && selectableChannel != channel) {
					SocketChannel socketChannel = (SocketChannel) selectableChannel;
					ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());
					socketChannel.write(byteBuffer);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		GroupChatServer groupChatServerDemo = new GroupChatServer();
		groupChatServerDemo.seleorSocketChannel();
	}

}

客户端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;

public class GroupChatClient {

	// 定义属性信息
	private SocketChannel socketChannel;// 定义网络IO通道
	private Selector selector;// 定义选择器
	private static final Integer PORT = 8001; // 监听的端口信息
	private final String HOST = "127.0.0.1";// 定义ip地址
	private String userName;// 用户名

	/**
	 * 定义构造方法
	 */
	public GroupChatClient() {
		try {
			socketChannel = SocketChannel.open(new InetSocketAddress(HOST, PORT));
			selector = Selector.open();
			socketChannel.configureBlocking(false);
			socketChannel.register(selector, SelectionKey.OP_READ);
			userName = socketChannel.getLocalAddress().toString().substring(1);
			System.out.println(userName + ":准备好了");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 定义发送消息的方法
	 * 
	 * @param msg 消息
	 */
	public void sendMsg(String msg) {
		try {
			msg = userName + "说:" + msg;
			byte[] str = msg.getBytes();
			ByteBuffer writeBuffer = ByteBuffer.allocate(str.length);
			writeBuffer.put(str);
			writeBuffer.flip();
			socketChannel.write(writeBuffer);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取服务器转发的消息
	 */
	public void readMsg() {
		try {
			// 大于0代表有通道信息
			if (selector.select() > 0) {
				Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
				while (iterator.hasNext()) {
					SelectionKey selectionKey = iterator.next();
					// 获取读的操作
					if (selectionKey.isReadable()) {
						SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
						ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
						socketChannel.read(byteBuffer);
						System.out.println("服务器转发的信息为:" + new String(byteBuffer.array()));
					}
				}
				iterator.remove();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		final GroupChatClient groupChatClientDemo = new GroupChatClient();

		new Thread(new Runnable() {
			public void run() {
				while (true) {
					groupChatClientDemo.readMsg();
					try {
						Thread.sleep(3000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		while (true) {
			String str = UUID.randomUUID().toString();
			groupChatClientDemo.sendMsg(str);
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}

运行结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值