demo1(单线程简单通讯)io流
服务端
public class Server {
public static void main(String[] args) throws Exception {
//5.创建ServerSocket、Socket、OutputStream、InputStream以及端口号并初始化
ServerSocket serverSocket = null;
boolean flag = true;
Socket socket = null;
OutputStream out = null;
InputStream in = null;
try {
//6.开启服务器并接收客户端发送的数据
serverSocket = new ServerSocket(8000);//创建服务器套接字
System.out.println("服务器开启,等待连接...");
socket = serverSocket.accept();//获得连接
//接收客户端发送的内容
in = socket.getInputStream();
//7.使用输出流对象将信息返回给客户端
out = socket.getOutputStream();
System.out.println(socket.getInetAddress().getHostAddress() + "---连接成功");
out.write("连接成功".getBytes());
out.flush();
while (flag) {
byte[] b = new byte[1024];
int len = in.read(b);
System.out.println("client:" + new String(b, 0, len));
//控制台输入
Scanner src = new Scanner(System.in);
String str = src.nextLine();
out.write(str.getBytes());
out.flush();
if (str.equals("bye")) {
flag = false;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//8.关闭流对象、ServerSocket对象以及Socket对象
in.close();
out.close();
serverSocket.close();
socket.close();
}
}
}
客户端
public class Client {
public static void main(String[] args) throws Exception {
//1.定义 Socket 对象、OutputStream 对象和一个 InputStream 对象并完成初始化
Socket socket = null;
OutputStream out = null;
InputStream in = null;
boolean flag = true;
//定义服务器端的 IP 地址和端口号
String serverIP = "127.0.0.1";//服务器端 IP 地址
int port = 8000;//服务器端端口号
try {
//2.建立与服务器端的连接并将数据发送到服务器端
socket = new Socket(serverIP, port);//建立连接
out = socket.getOutputStream();//发送数据
while (flag) {
//3.从输入流中读出服务器的反馈信息并输出到控制台
byte[] b = new byte[1024];
in = socket.getInputStream();
int len = in.read(b);
String response = new String(b, 0, len);
System.out.println("server:" + response);
if ("bye".equals(response)) {
flag = false;
break;
}
//控制台输入
Scanner src = new Scanner(System.in);
String str = src.nextLine();
out.write(str.getBytes());
out.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.关闭流及 Socket 对象
in.close();
out.close();
socket.close();
}
}
}
demo1(单线程简单通讯)nio
服务端
public class NIOServer {
// 用于存放客户端SocketChannel集合
private static Map<String, SocketChannel> clientMap = new HashMap();
private static ByteBuffer sBuffer = ByteBuffer.allocate(1024);
// 通道管理器(Selector)
private static Selector selector;
public static void main(String[] args) throws IOException {
// 创建通道管理器(Selector)
selector = Selector.open();
// 创建通道ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 将通道设置为非阻塞
serverSocketChannel.configureBlocking(false);
// 将ServerSocketChannel对应的ServerSocket绑定到指定端口(port)
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(8000));
/**
* 将通道(Channel)注册到通道管理器(Selector),并为该通道注册selectionKey.OP_ACCEPT事件
* 注册该事件后,当事件到达的时候,selector.select()会返回,
* 如果事件没有到达selector.select()会一直阻塞。
*/
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务已启动等待连接。。。");
// 循环处理
while (true) {
// 当注册事件到达时,方法返回,否则该方法会一直阻塞
selector.select();
// 获取监听事件
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
// 迭代处理
while (iterator.hasNext()) {
// 获取事件
SelectionKey key = iterator.next();
// 移除事件,避免重复处理
iterator.remove();
// 检查是否是一个就绪的可以被接受的客户端请求连接
if (key.isAcceptable()) {
handleAccept(key);
} else if (key.isReadable()) {// 检查套接字是否已经准备好读数据
handleRead(key);
}
}
}
}
/**
* 处理客户端连接成功事件
*/
private static void handleAccept(SelectionKey key) throws IOException {
// 获取客户端连接通道
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
// 信息通过通道发送给客户端
String msg = "连接成功!";
socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
// 启动线程监听客户端输入
new Thread() {
@Override
public void run() {
while (true) {
try {
sBuffer.clear();
//控制台输入
Scanner src = new Scanner(System.in);
String str = src.nextLine();
// System.out.println(sendText);
sBuffer.put(Charset.forName("UTF-8").encode(str));
sBuffer.flip();
socketChannel.write(sBuffer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
clientMap.put(getClientName(socketChannel), socketChannel);
// 给通道设置读事件,客户端监听到读事件后,进行读取操作
socketChannel.register(selector, SelectionKey.OP_READ);
}
/**
* 监听到读事件,读取客户端发送过来的消息
*/
private static void handleRead(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
// 从通道读取数据到缓冲区
ByteBuffer buffer = ByteBuffer.allocate(128);
channel.read(buffer);
// 输出客户端发送过来的消息
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("client:" + channel.toString() + " " + msg);
//转发给其他客户端
// dispatch(channel, msg);
}
/**
* 转发消息给各个客户端
*/
private static void dispatch(SocketChannel client, String info) throws IOException {
if (!clientMap.isEmpty()) {
for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) {
SocketChannel temp = entry.getValue();
if (!client.equals(temp)) {
sBuffer.clear();
sBuffer.put(Charset.forName("UTF-8").encode(getClientName(client) + ":" + info));
sBuffer.flip();
temp.write(sBuffer);
}
}
}
}
/**
* 生成客户端名字
*/
private static String getClientName(SocketChannel client){
Socket socket = client.socket();
return "[" + socket.getInetAddress().toString().substring(1) + ":" + Integer.toHexString(client.hashCode()) + "]";
}
}
客户端
public class NIOClient {
// 通道管理器(Selector)
private static Selector selector;
private static Charset charset = Charset.forName("UTF-8");
public static void main(String[] args) throws IOException {
// 创建通道管理器(Selector)
selector = Selector.open();
// 创建通道SocketChannel
SocketChannel channel = SocketChannel.open();
// 将通道设置为非阻塞
channel.configureBlocking(false);
// 客户端连接服务器,其实方法执行并没有实现连接,需要在handleConnect方法中调channel.finishConnect()才能完成连接
channel.connect(new InetSocketAddress("localhost", 8000));
/**
* 将通道(Channel)注册到通道管理器(Selector),并为该通道注册selectionKey.OP_CONNECT
* 注册该事件后,当事件到达的时候,selector.select()会返回,
* 如果事件没有到达selector.select()会一直阻塞。
*/
channel.register(selector, SelectionKey.OP_CONNECT);
// 循环处理
while (true) {
/*
* 选择一组可以进行I/O操作的事件,放在selector中,客户端的该方法不会阻塞,
* selector的wakeup方法被调用,方法返回,而对于客户端来说,通道一直是被选中的
* 这里和服务端的方法不一样,查看api注释可以知道,当至少一个通道被选中时。
*/
selector.select();
// 获取监听事件
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
// 迭代处理
while (iterator.hasNext()) {
// 获取事件
SelectionKey key = iterator.next();
// 移除事件,避免重复处理
iterator.remove();
// 检查是否是一个就绪的已经连接服务端成功事件
if (key.isConnectable()) {
handleConnect(key);
} else if (key.isReadable()) {// 检查套接字是否已经准备好读数据
handleRead(key);
}
}
}
}
/**
* 处理客户端连接服务端成功事件
*/
private static void handleConnect(SelectionKey key) throws IOException {
// 获取与服务端建立连接的通道
SocketChannel channel = (SocketChannel) key.channel();
if (channel.isConnectionPending()) {
// channel.finishConnect()才能完成连接
channel.finishConnect();
}
channel.configureBlocking(false);
ByteBuffer sBuffer = ByteBuffer.allocate(1024);
// 数据写入通道
String msg = "Hello Server!";
channel.write(ByteBuffer.wrap(msg.getBytes()));
// 启动线程监听客户端输入
new Thread() {
@Override
public void run() {
while (true) {
try {
sBuffer.clear();
//控制台输入
Scanner src = new Scanner(System.in);
String str = src.nextLine();
// System.out.println(sendText);
sBuffer.put(charset.encode(str));
sBuffer.flip();
channel.write(sBuffer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
// 通道注册到选择器,并且这个通道只对读事件感兴趣
channel.register(selector, SelectionKey.OP_READ);
}
/**
* 监听到读事件,读取客户端发送过来的消息
*/
private static void handleRead(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
// 从通道读取数据到缓冲区
ByteBuffer buffer = ByteBuffer.allocate(128);
channel.read(buffer);
// 输出服务端响应发送过来的消息
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("server:" + msg);
}
}
netty demo
服务端配置
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel channel) {
// 基于换行符号
// channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
// 解码转String,注意调整自己的编码格式GBK、UTF-8
channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));
// 编码转String,注意调整自己的编码格式GBK、UTF-8
channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));
// 在管道中添加我们自己的接收数据实现方法
channel.pipeline().addLast(new MyServerHandler());
}
}
server回调处理
public class MyServerHandler extends SimpleChannelInboundHandler {
public static HashMap<String, Channel> channelList = new HashMap();
/**
* 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
SocketChannel channel = (SocketChannel) ctx.channel();
System.out.println("链接报告开始");
System.out.println("链接报告信息:有一客户端链接到本服务端");
System.out.println("链接报告IP:" + channel.localAddress().getHostString());
System.out.println("链接报告Port:" + channel.localAddress().getPort());
System.out.println("链接报告完毕");
//通知客户端链接建立成功
String str = "通知客户端链接建立成功" + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\n";
ctx.writeAndFlush(str);
}
/**
* 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端断开链接" + ctx.channel().localAddress().toString());
}
@Override
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
channelList.put("1",ctx.channel());
//接收msg消息{与上一章节相比,此处已经不需要自己进行解码}
System.out.println(" client:" + msg);
//通知客户端链消息发送成功{不需要通过ByteBuf,可以直接发送字符串}
// String str = "服务端收到:" + new Date() + " " + msg + "\r\n";
// ctx.writeAndFlush(str);
}
/**
* 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
System.out.println("异常信息:\r\n" + cause.getMessage());
}
}
server端启动
public class NettyServer {
public static void main(String[] args) {
new NettyServer().bing(7397);
}
private void bing(int port) {
//配置服务端NIO线程组
EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
EventLoopGroup childGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(parentGroup, childGroup)
.channel(NioServerSocketChannel.class) //非阻塞模式
.option(ChannelOption.SO_BACKLOG, 128)
.childHandler(new ServerChannelInitializer());
//绑定端口,同步等待成功.
ChannelFuture f = b.bind(port).sync();
System.out.println("netty server start done wait conn...");
boolean flag=true;
while (flag) {
//控制台输入
Scanner src = new Scanner(System.in);
String str = src.nextLine();
Channel channel= MyServerHandler.channelList.get("1");
channel.writeAndFlush(str);
if("byebye".equals(str)){
f.channel().close();
break;
}
}
//等待服务端监听端口关闭
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
childGroup.shutdownGracefully();
parentGroup.shutdownGracefully();
}
}
}
客户端
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
// NioEventLoopGroup group = new NioEventLoopGroup();
try{
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) {
// ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
ch.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));
// 编码转String,注意调整自己的编码格式GBK、UTF-8
ch.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));
ch.pipeline().addLast(new ClientHandler());
}
});
ChannelFuture future = bootstrap.connect("127.0.0.1", 7397).sync();
boolean flag=true;
while (flag) {
//控制台输入
Scanner src = new Scanner(System.in);
String str = src.nextLine();
future.channel().writeAndFlush(str);
if("bye".equals(str)){
future.channel().close();
break;
}
}
future.channel().closeFuture().sync();
}finally {
group.spliterator();
}
}
客户端回调处理
public class ClientHandler extends SimpleChannelInboundHandler {
/**
* 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
SocketChannel channel = (SocketChannel) ctx.channel();
//通知客户端链接建立成功
// String str = "通知客户端链接建立成功" + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\n";
System.out.println("连接成功");
ctx.writeAndFlush("");
}
/**
* 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端断开链接" + ctx.channel().localAddress().toString());
}
@Override
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
//接收msg消息{与上一章节相比,此处已经不需要自己进行解码}
System.out.println( "server:" + msg);
//通知客户端链消息发送成功{不需要通过ByteBuf,可以直接发送字符串}
String str = "--客户端收到--" + "\r\n";
ctx.writeAndFlush(str);
}
/**
* 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
System.out.println("异常信息:\r\n" + cause.getMessage());
}
}
来源:CSDN
作者:zmemorys
链接:https://blog.csdn.net/zmemorys/article/details/104005068