实际生产中多客户端连接服务器通信是常用应用,多客户端连接一个服务器端,多客户端连接多个服务器;
package com.cc.netty.best.multi.connection;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class Server {
public static void main(String[] args) {
//服务类
ServerBootstrap bootstrap = new ServerBootstrap();
//boss和worker
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try {
//设置线程池
bootstrap.group(boss, worker);
//设置socket工厂
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.childHandler(new ChannelInitializer<Channel>() { //设置管道工厂
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new ServerHandler());
}
});
//设置参数,TCP参数
bootstrap.option(ChannelOption.SO_BACKLOG, 2048);//serverSocketchannel的设置,链接缓冲池的大小
bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);//socketchannel的设置,维持链接的活跃,清除死链接
bootstrap.childOption(ChannelOption.TCP_NODELAY, true);//socketchannel的设置,关闭延迟发送
//绑定端口
ChannelFuture future = bootstrap.bind(8765);
System.out.println("server-8765:start");
//等待服务端关闭[阻塞]
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally{
//释放资源
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
package com.cc.netty.best.multi.connection;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class ServerHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(ctx.channel()+":"+msg);
//ctx.channel().writeAndFlush("hi");
ctx.writeAndFlush(ctx.channel()+"server-8765:hi");
}
/**
* 新客户端接入
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelActive");
}
/**
* 客户端断开
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelInactive");
}
/**
* 异常
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
}
}
package com.cc.netty.best.multi.connection;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
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.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class MultiClent {
/* 服务类 */
private Bootstrap bootstrap = new Bootstrap();
private final AtomicInteger index = new AtomicInteger(0);
/* 会话 将Channel封装到 ArrayList中实现池化 */
private List<Channel> channels = new ArrayList<Channel>();
/**
* <B>方法名称:</B>初始化方法<BR>
* <B>概要说明:</B>初始化链接<BR>
* @param count 链接数
*/
public void init(int count){
//worker:
EventLoopGroup worker = new NioEventLoopGroup();
//设置线程池
this.bootstrap.group(worker);
//设置socket工厂
this.bootstrap.channel(NioSocketChannel.class);
//设置管道
this.bootstrap.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
channel.pipeline().addLast(new StringDecoder());
channel.pipeline().addLast(new StringEncoder());
channel.pipeline().addLast(new ClientHandler());
}
});
/* 根据传入count值创建服务器连接,把连接封装进channels实现池化*/
for (int i = 1; i <= count; i++) {
ChannelFuture future = this.bootstrap.connect("127.0.0.1", 8765);
channels.add(future.channel());
}
}
/** -获取指定Channel
* -应用AtomicInteger(原子类)的getAndIncrement获取并自增,
* -同channels的大小进行取余,进行Math.abs取绝对值
* @param count
* @return
*/
public Channel getNextChannel(int count){
Channel channel = channels.get(Math.abs(index.getAndIncrement() % channels.size()));
if(!channel.isActive()){
//如果非活跃,重新连接
reconnect(channel);
if(count >= channels.size()){
throw new RuntimeException("no use channel !");
}
return getNextChannel(count + 1);
}
return channel;
}
public void reconnect(Channel channel){
ReentrantLock reentrantLock = new ReentrantLock();
try {
/** -加锁
* -新旧替换过程中必须保证操作唯一性(必须考虑并发)
*/
reentrantLock.lock();
if(channels.indexOf(channel) == -1){
return ;
}
Channel newChannel = this.bootstrap.connect("127.0.0.1", 8765).channel();
//用新创建的channal替换旧的channal
channels.set(channels.indexOf(channel), newChannel);
} finally {
reentrantLock.unlock();
}
}
}
package com.cc.netty.best.multi.connection;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* 客户端消息处理
*
*/
public class ClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("客户端收到消息:" + msg);
}
}
package com.cc.netty.best.multi.connection;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* 启动类
*
*/
public class Start {
public static void main(String[] args) {
MultiClent client = new MultiClent();
client.init(5);//创建5个客户端,连接5个channal,向服务器发送请求
//输入数据发起通信
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
while(true){
try {
System.out.println("请输入:");
String msg = bufferedReader.readLine();
//给定初始值为0,实际生产动态传入
client.getNextChannel(0).writeAndFlush(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}