前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java使用NIO实现Socket通信

Java使用NIO实现Socket通信

作者头像
半月无霜
发布2023-03-03 14:59:05
7420
发布2023-03-03 14:59:05
举报
文章被收录于专栏:半月无霜

Java使用NIO实现Socket通信

一、介绍

在上次的博客中,已经了解到NIO当中最为重要的两个对象。分别是缓冲Buffer和通道Channel,也进行了基本的使用,不过使用的是FileChannel,主要用来与文件打交道。

那么,这一次使用NIO实现Socket网络通信,主要是使用到ServerSocketChannelSocketChannel

同样,在本次作为NIO的网络通信,建议先了解传统BIO的网络通信,传送门在此

二、实现

1)服务端

代码语言:javascript
复制
package com.banmoon.test;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

@Slf4j
public class TestSocketServer {

    public static void main(String[] args) throws IOException {
        // 开启通道,得到一个ServerSocketChannel对象
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 得到一个选择器
        Selector selector = Selector.open();
        // 绑定端口
        serverSocketChannel.socket().bind(new InetSocketAddress(2333));
        // 设置为非阻塞
        serverSocketChannel.configureBlocking(false);

        // 将socketChannel注册到selector上,事件为OP_ACCEPT,接收事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 循环等待客户端连接
        while (true) {
            // 服务器等待2秒,如果没有接收事件发生,将进入下次循环
            if (selector.select(2000) == 0) {
                log.info("服务器已等待2秒,继续静默等待");
                continue;
            }

            // 如果有事件发成,将会进入此逻辑,获取到相关的SelectionKey集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            // 遍历这个集合
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                // 判断是否发生了OP_ACCEPT事件
                if (selectionKey.isAcceptable()) {
                    // 获取到SocketChannel通道。
                    // 此方法虽然是阻塞的,但由于上面基于事件的判断,到这一步时,连接就已经产生了,所以不会阻塞
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    // 将socketChannel设置为非阻塞
                    socketChannel.configureBlocking(false);
                    // 将上面的socketChannel注册到selector选择器上,事件注册为OP_READ,读取事件
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }

                // 判断是否发生了OP_READ事件
                if (selectionKey.isReadable()) {
                    // 通过选择器获取到发生了OP_READ事件的SocketChannel
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    // 获取到该socketChannel关联的缓冲
                    ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();
                    // 读取通道中的数据
                    socketChannel.read(byteBuffer);
                    // 反转缓冲,进行读取
                    byteBuffer.flip();
                    log.info("服务端接受到数据:{}", new String(byteBuffer.array(), 0, byteBuffer.limit(), StandardCharsets.UTF_8));
                }

                // 删除当前的selectionKey,避免重复执行
                iterator.remove();
            }
        }

    }

}

2)客户端

代码语言:javascript
复制
package com.banmoon.test;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

@Slf4j
public class TestSocketClient {

    public static void main(String[] args) throws IOException, InterruptedException {
        // 开启通道,得到一个SocketChannel对象
        SocketChannel socketChannel = SocketChannel.open();
        // 设置为非阻塞
        socketChannel.configureBlocking(false);
        // 创建一个服务器端地址信息的对象
        InetSocketAddress netAddress = new InetSocketAddress("127.0.0.1", 2333);
        // 连接服务器,如果没有连接成功将进入循环
        if (!socketChannel.connect(netAddress)) {
            // 循环判断是否完成连接,阻塞方法,2秒后释放
            while (!socketChannel.finishConnect()) {
                log.info("与服务器连接中,请稍后");
                TimeUnit.SECONDS.sleep(1);
            }
        }
        log.info("连接服务器成功");
        // 当执行到此处时,说明与服务器的连接已经成功,发送数据
        String str = "hello,半月无霜";
        ByteBuffer byteBuffer = ByteBuffer.wrap(str.getBytes(StandardCharsets.UTF_8));
        // 写入通道
        socketChannel.write(byteBuffer);
        // 避免通道关闭阻塞在此
        CountDownLatch countDownLatch = new CountDownLatch(1);
        countDownLatch.await();
    }

}

3)测试

先启用服务端,再启用客户端,客户端可以启用多个

或者也可以先启用客户端,再启用服务端

服务端:

https://banmoon-pic.oss-cn-guangzhou.aliyuncs.com/images/20220705231526.png

客服端:

https://banmoon-pic.oss-cn-guangzhou.aliyuncs.com/images/20220705231138.png

三、最后

实际上,这只是简单的一个应用,后续复杂的都是基于此简单的服务、客户端进行展开。继续加油!

我是半月,祝你幸福!!!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-07-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java使用NIO实现Socket通信
    • 一、介绍
      • 二、实现
        • 1)服务端
        • 2)客户端
        • 3)测试
      • 三、最后
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档