服务器端代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
/**
* 服务器端启动并监听6667端口,服务器接收客户端消息并实现转发,提示上线和下线
*/
public class GroupChatServer {
private static Selector selector;
//定义属性
private static ServerSocketChannel lisenChannel;//serversocketchannel监听是否有客户端连接
private static final int PORT=6667;//定义服务器的端口号
public GroupChatServer() {
try {
//得到选择器
selector= Selector.open();
//获取监听连接
lisenChannel=ServerSocketChannel.open();
//绑定端口
lisenChannel.socket().bind(new InetSocketAddress(PORT));
//设置非阻塞模式
lisenChannel.configureBlocking(false);
//注册
lisenChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
}
}
//监听方法
public void lisen(){
//循环监听
try {
while (true){
int select = selector.select();
if(select>0){//说明有事件处理
//遍历得到selectionKey集合
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
//取出selectionKey
SelectionKey selectionKey = iterator.next();
//分不同的事件类型处理事件
if(selectionKey.isAcceptable()){//accept事件
SocketChannel accept = lisenChannel.accept();
accept.configureBlocking(false);
//将此socketchannel注册到selector中
accept.register(selector,SelectionKey.OP_READ);
//提示已经连接过来了
System.out.println(accept.getRemoteAddress()+"上线了");
}
if(selectionKey.isReadable()){//通道发送read事件,即通道是可读的状态
//处理读
readData(selectionKey);
}
//移出此selectionKey,防止重复操作
iterator.remove();
}
}else {
System.out.println("等待......");
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
}
}
/**
* 读取客户端消息
* @param key 根据key反向获取用户通道
*/
public void readData(SelectionKey key) throws IOException {
//取到关联的channel
SocketChannel socketChannel=null;
try {
//得到channel
socketChannel= (SocketChannel) key.channel();
//创建buffer
ByteBuffer buffer=ByteBuffer.allocate(1024);
//读取数据,将数据读取到buffer中
int read = socketChannel.read(buffer);
//根据read的值处理
if(read>0){
//把缓冲区的数据转成字符串
java.lang.String msg = new java.lang.String(buffer.array());
//输出该消息
System.out.println("form客户端:"+msg);
//转发改消息给其他客户端
sendInforToOtherCilent(msg,socketChannel);
}
} catch (IOException e) {//如果捕获到异常,说明用户离线
System.out.println(socketChannel.getRemoteAddress()+"离线了。。。");
//取消注册
key.cancel();
//关闭通道
socketChannel.close();
}
}
/**
* 转发消息给其他客户端
* @param msg 转发的消息
* @param self 转发前自己的channel,由于服务器接收到以后,没有其他客户端这个概念,转发针对的是所有的客户端,
* 所以再这里要排除自己(不能自己发之后还自己接收)
*/
public void sendInforToOtherCilent(java.lang.String msg, SocketChannel self) throws IOException {
System.out.println("服务器转发消息");
//遍历所有的注册到seletor的socketchannel,并排除自己
for(SelectionKey key:selector.keys()){
//通过key取出对应的channel(要转发的channel)
Channel targetChannel= (Channel) key.channel();
//排除自己,由于key.channel中有各种类型的channel,所以需要判断一下是否是socketchannel
if(targetChannel instanceof SocketChannel &&targetChannel!=self){
SocketChannel target= (SocketChannel) targetChannel;
//将msg存储到buffer
ByteBuffer buffer=ByteBuffer.wrap(msg.getBytes());
//将buffer的数据写入通道
target.write(buffer);
}
}
}
}
客户端代码:
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.Scanner;
public class GroupChatClient {
//定义相关的属性
private final String HOST="127.0.0.1";
private final int PORT=6667;
private Selector selector;
private SocketChannel socketChannel;
private String username;
//构造器,完成初始化
public GroupChatClient() throws IOException {
selector = selector.open();
//连接服务器
socketChannel= SocketChannel.open(new InetSocketAddress(HOST,PORT));
//设置为非阻塞
socketChannel.configureBlocking(false);
//将channel注册到selector
socketChannel.register(selector, SelectionKey.OP_READ);
//得到username
username = socketChannel.getLocalAddress().toString().substring(1);
System.out.println(username+" is ok....");
}
//向服务器发送消息
public void sendMesToServer(String info){
try {
info=username+"说:"+info;
socketChannel.write(ByteBuffer.wrap(info.getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
}
//从服务器获取消息
public void recvFromServer(){
try {
int select = selector.select();
if(select>0){//有可以用的通道
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){//考虑的是可能客户端有很多的通道,所以一直while
SelectionKey selectionKey = iterator.next();
if(selectionKey.isReadable()){
//得到相关的通道
SocketChannel sc= (SocketChannel) selectionKey.channel();
//设置非阻塞
sc.configureBlocking(false);
//得到一个buffer
ByteBuffer buffer=ByteBuffer.allocate(1024);
//读取
sc.read(buffer);
//把读到的缓冲区字符串显示出来
String s = new String(buffer.array());
System.out.println(s.trim());//去除头尾空格
iterator.remove();//删除当前的seletionKey,防止重复操作
}else {
System.out.println("没有可以用的通道....");
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
//启动客户端
GroupChatClient groupChatClient=new GroupChatClient();
//开启线程,每隔三秒读取可能从服务器端发送的数据
new Thread(){
public void run(){
while (true){//启动一个线程一直读
groupChatClient.recvFromServer();
//每读一次休眠三秒
try {
Thread.currentThread().sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
//发送数据给服务器
Scanner scanner=new Scanner(System.in);
while (scanner.hasNextLine()){
String next = scanner.nextLine();
groupChatClient.sendMesToServer(next);
}
}
}
先启动服务端,再启动多个客户端