一个Server 对 一个Client
Client
public class Client {
public static void main(String[] args) throws Exception {
// 1. 获取网络通道
SocketChannel channel = SocketChannel.open();
// 2. 设置为非阻塞方式
channel.configureBlocking(false);
// 3.1 设置服务器 ip 端口
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 9999);
// 3.2 连接服务器
// 连接服务器失败
if (!channel.connect(address)){
// 重新连接服务器失败
while (!channel.finishConnect()){
System.out.println("连接服务端同时,做点别的事情");
}
}
// 4. 创建缓冲区,存入数据
ByteBuffer buffer = ByteBuffer.wrap("hello nio".getBytes());
// 5. 发送数据
channel.write(buffer);
// 连接服务器后防止程序直接结束
System.in.read();
}
}
Server
public class Server {
public static void main(String[] args) throws Exception {
// 1. 获取 ServerSocketChannel 对象
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 2. 获取 Selector 对象
Selector selector = Selector.open();
// 3. 绑定端口号
serverSocketChannel.bind(new InetSocketAddress(9999));
// 4. 设置阻塞方式
serverSocketChannel.configureBlocking(false);
// 5. 把 channel 注册给 selector 监听有没有客户端连接
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 6. selector 开始干活
while (true) {
// 6.1 监控客户端判断是否有连接,每隔2秒检测一次
if (selector.select(2000) == 0) {
System.out.println("没有client连接, 可以做其他事情。。。");
continue;
}
// 6.2 得到 SelectionKey(代表 Selector 和 ServerSocketChannel 的注册关系) , 判断事件
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
// 客户端连接事件发生
if(selectionKey.isAcceptable()){
System.out.println("客户端连接事件发生。。。");
// 获取连接 channel
SocketChannel socketChannel = serverSocketChannel.accept();
// 设置为非阻塞
socketChannel.configureBlocking(false);
// 将 channel 注册到selector,接下来为读事件,传来的数据存到ByteBuffer中
socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
}
// 读取客户端数据事件发生
if (selectionKey.isReadable()){
// 从selectionKey中获取 channel
SocketChannel channel = (SocketChannel) selectionKey.channel();
// 从selectionKey中得到与之关联的数据
ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();
// 将数据放入缓冲区
channel.read(byteBuffer);
System.out.println("客户端发来数据 " + new String(byteBuffer.array()));
}
// 6.3 手动从集合中移除当前key,防止重复处理
iterator.remove();
}
}
}
}
一个Server 对 多个Client
启动 Client
若要启动多个 Client 在 VM Option 中配置 -Dport=端口号
public class TestChat {
public static void main(String[] args) throws IOException {
ChatClient chatClient = new ChatClient();
new Thread(() -> {
while (true){
try {
chatClient.receiveMsg();
Thread.sleep(2000);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}).start();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()){
String msg = scanner.nextLine();
chatClient.sendMsg(msg);
}
}
}
ChatClient
public class ChatClient {
private final String HOST = "127.0.0.1";
private final int PORT = 9999;
private SocketChannel socketChannel;
// ip + 端口
private String username;
public ChatClient() throws IOException {
// 获取 channel
socketChannel = SocketChannel.open();
// 设置阻塞方式
socketChannel.configureBlocking(false);
// 设置端口号
InetSocketAddress address = new InetSocketAddress(PORT);
// 连接服务器
if (!socketChannel.connect(address)) {
while (!socketChannel.finishConnect()) {
System.out.println("...");
}
}
// ip+端口地址作为username
username = socketChannel.getLocalAddress().toString();
System.out.println("----------Client (" + username + ") is ready----------");
}
// 发送消息
public void sendMsg(String msg) throws IOException {
// 发送 stop 时关闭连接
if (msg.equals("stop")) {
socketChannel.close();
return;
}
msg = username + "说: " + msg;
// 将字节数组放入缓冲区中
ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
socketChannel.write(buffer);
}
// 接收消息
public void receiveMsg() throws IOException {
// 创建1024大小的buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
int size = socketChannel.read(buffer);
if (size > 0) {
// trim() 为去除多余的空格
System.out.println(new String(buffer.array()).trim());
}
}
}
ChatServer
public class ChatServer {
private ServerSocketChannel listenerChannel;
private Selector selector;
private static final int PORT = 9999;
public ChatServer() {
try {
// 1. 获取 channel
listenerChannel = ServerSocketChannel.open();
// 2. 获取 selector
selector = Selector.open();
// 3. 绑定端口
listenerChannel.bind(new InetSocketAddress(PORT));
// 4. 设置阻塞方式
listenerChannel.configureBlocking(false);
// 5. 注册监听队列到选择器
listenerChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() {
try {
// 6. 监听
while (true) {
// 6.1 监控客户端判断是否有连接
if (selector.select(2000) == 0) {
System.out.println("server 没有client连接 ...");
continue;
}
// 6.2 得到 SelectionKey , 判断事件
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
// 客户端连接事件
if (selectionKey.isAcceptable()) {
// 获取客户端 channel
SocketChannel socketChannel = listenerChannel.accept();
// 设置客户端 channel 阻塞方式
socketChannel.configureBlocking(false);
// 将客户端 channel 注册到 selector 中, 接下来为读操作
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println(socketChannel.getRemoteAddress() + " 上线了!");
}
// 读取客户端数据事件
if (selectionKey.isReadable()) {
// 获取消息
readMsg(selectionKey);
}
// 6.3 手动从集合中移除当前key,防止重复处理
iterator.remove();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 读取数据
private void readMsg(SelectionKey selectionKey) throws IOException {
// 获取通道
SocketChannel channel = (SocketChannel) selectionKey.channel();
// 获取缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 将数据写入缓冲区
int size = channel.read(buffer);
if(size > 0){
String msg = new String(buffer.array());
// 控制台打印消息
printInfo(msg);
// 发广播给其他客户端
broadCast(channel, msg);
}
}
// 广播消息给其他客户端
private void broadCast(SocketChannel except, String msg) throws IOException {
// 当前 channel 注册在 Selector 上所有的 key
Set<SelectionKey> keys = selector.keys();
for (SelectionKey selectionKey : keys) {
Channel channel = selectionKey.channel();
// 排除发送消息的 channel
if (channel instanceof SocketChannel &&except != channel) {
SocketChannel destChannel = (SocketChannel) channel;
ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
destChannel.write(buffer);
}
}
}
private void printInfo(String info) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("[" + sdf.format(new Date()) + "] -> " + info);
}
public static void main(String[] args) {
new ChatServer().start();
}
}