概述
本案例为简单的命令行实现,分为客户端和服务端,服务端实现监听客户端上线,接受客户端发送的聊天内容,并且把聊天内容转发给其他在线客户端(排除自己),客户端主要是发送消息给服务端,并且接受服务端转发过来的消息并且输出到控制台上(有图便于理解,代码里面大量注释)
服务端
public class Server {
//选择器,通道,端口
private Selector selector;
private ServerSocketChannel ssChannel;
private static final Integer PORT = 9999;
/**
* 构造器初始化
*/
public Server(){
try {
selector = Selector.open();
ssChannel = ServerSocketChannel.open();
ssChannel.configureBlocking(false); //设置为非阻塞模式
ssChannel.bind(new InetSocketAddress(PORT));
ssChannel.register(selector,SelectionKey.OP_ACCEPT); //注册
} catch (IOException e) {
e.printStackTrace();
}
}
// 监听--》读取消息--》转发消息
private void listen() {
try{
while (selector.select() > 0){ //一直监听,等待事件进入
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
SocketChannel socketChannel = null;
while (it.hasNext()){ //有事件接入
SelectionKey sk = it.next();
if (sk.isAcceptable()){ //这里是客户端登入。获取器通道,注册到咱们的选择器上
socketChannel = ssChannel.accept();
socketChannel.configureBlocking(false);
System.out.println(socketChannel.getRemoteAddress()+"----上线了");
socketChannel.register(selector,SelectionKey.OP_READ);
}else if (sk.isReadable()){ //登录的客户端发送消息
readData(sk);
}
it.remove();
}
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 处理客户端发送的消息
* @param sk
*/
private void readData(SelectionKey sk) {
SocketChannel socketChannel = null;
try{
socketChannel = (SocketChannel)sk.channel(); //获取当前客户端的通道
ByteBuffer buff = ByteBuffer.allocate(1024);
int len = 0;
int read = socketChannel.read(buff); //读取通道中的消息到缓冲区里面
buff.flip(); //切换为写模式,就是让buff中的position为0,limit为当前直接的长度
if (read>0){
String msg = new String(buff.array(),0,buff.remaining());
System.out.println("客户端"+socketChannel.getRemoteAddress()+": " + msg);
//消息转发
sendInfoToOtherClients(msg, socketChannel);
}
}catch (Exception e){
//这里出异常通常是客户端断开连接,意味着下线
try {
System.err.println(socketChannel.getRemoteAddress()+"下线了");
sk.channel();
socketChannel.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
/**
* 转发消息
* @param msg
* @param currentChannel 自己的通道
* @throws IOException
*/
private void sendInfoToOtherClients(String msg, SocketChannel currentChannel) throws IOException {
for (SelectionKey key : selector.keys()){ //遍历selector里面的通道
Channel channel = key.channel();
if (channel instanceof SocketChannel && channel!=currentChannel){ //排除自己,自己就不必要转发的
SocketChannel sc = (SocketChannel)channel;
ByteBuffer buff = ByteBuffer.wrap(msg.getBytes()); //wrap底层做了一个ByteBuffer的操作,封装好的api,范围确认好了
sc.write(buff);
}
}
}
public static void main(String[] args) {
System.out.println("--------服务端开始启动-----------");
Server server = new Server();
server.listen();
}
}
客户端
public class Client {
//定义相关的属性
private final String HOST = "127.0.0.1"; // 服务器的ip
private final int PORT = 9999; //服务器端口
private Selector selector;
private SocketChannel socketChannel;
private String username;
//构造器, 完成初始化工作
public Client() throws IOException {
selector = Selector.open();
//连接服务器
socketChannel = socketChannel.open(new InetSocketAddress("127.0.0.1", PORT));
//设置非阻塞
socketChannel.configureBlocking(false);
//将channel 注册到selector
socketChannel.register(selector, SelectionKey.OP_READ);
//得到username
username = socketChannel.getLocalAddress().toString().substring(1);
System.out.println(username + " 就绪...");
}
//向服务器发送消息
public void sendInfo(String info) {
info = username + " 说:" + info;
System.out.println("我:"+info);
try {
socketChannel.write(ByteBuffer.wrap(info.getBytes()));
}catch (IOException e) {
e.printStackTrace();
}
}
//读取从服务器端回复的消息
public void readInfo() {
try {
int readChannels = selector.select();
if(readChannels > 0) {//有可以用的通道
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if(key.isReadable()) {
//得到相关的通道
SocketChannel sc = (SocketChannel) key.channel();
//得到一个Buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
//读取
sc.read(buffer);
//把读到的缓冲区的数据转成字符串
String msg = new String(buffer.array());
System.out.println(msg.trim());
}
}
iterator.remove(); //删除当前的selectionKey, 防止重复操作
} else {
//System.out.println("没有可以用的通道...");
}
}catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
//启动我们客户端
Client chatClient = new Client();
//启动一个线程, 每隔3秒,读取从服务器发送数据
new Thread() {
public void run() {
while (true) {
chatClient.readInfo();
try {
Thread.currentThread().sleep(3000);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
//发送数据给服务器端
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String s = scanner.nextLine();
chatClient.sendInfo(s);
}
}
}