文章目录
- 知识点
- Java NIO 编程
- BIO
- NIO
- Buffer 缓冲区
- Channel 通道
- Selector多路选择器
- 示例代码
- Java AIO 编程
- 基础知识
- 示例代码
- 三种I/O的区别
- Netty 编程
- 基础知识
- 关键技术
- 通道 Channel
- 基于事件驱动 EventLoop
- 事件
- 事件处理 ChannelHandler
- ChannelHandler工作模式:责任链
- ByteBuf
- 示例代码
- pom.xml
- netty1
- Server
- Client
- netty2(添加多个Handler)
- Server
- Client
- Java Mail 编程
- 邮件基础知识
- Java Mail
- 服务器配置
- 工具包
- 核心API
- 示例代码
- 收取邮件
- 发送邮件
- 不同类型的邮件
- 文本邮件
- HTML邮件(带图片)
- 带附件的邮件
- 练习
- 服务端
- 客户端
- 运行结果
知识点
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的区别
BIO | NIO | AIO | |
阻塞方式 | 阻塞 | 非阻塞 | 非阻塞 |
同步方式 | 同步 | 同步 | 异步 |
编程难度 | 简单 | 较难 | 困难 |
客户机/服务器线程对比 | 1:1 | N:1 | N:1 |
性能 | 低 | 高 | 高 |
Netty 编程
基础知识
Netty, http://netty.io">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 包
- https://javaee.github.io/javamail,目前1.6.2
- mvn dependency
核心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();
}
}
}
}
运行结果