JAVA NIO[ServerSocketChannel与SocketChannel]实现服务端客户端通信

        最近在学习网络编程,我相信,除了做游戏的,IM的程序员们,其他的后端开发估计对于网络编程都挺薄弱,所以决定补一补网络编程,以及为了更好的学习Netty。下面是简单得使用ServerSocketChannel、SocketChannel和Selector做一个简单的服务端和客户端通信。ServerSocketChannel和SocketChannel都将注册到Selector中,然后轮询Selector去处理进入准备状态的通道的通信。

Talk is cheap,show you the code!

服务端:

package com.hyf.nio.socket;

import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;

/**
 * @author Howinfun
 * @desc 服务端
 * @date 2019/6/21
 */
public class TestServerSocketChannel {
    public static void main(String[] args)throws Exception {
        // 打开一个服务端通道
        ServerSocketChannel channel = ServerSocketChannel.open();
        // 非阻塞
        channel.configureBlocking(false);
        // 监听端口号8080
        channel.socket().bind(new InetSocketAddress(8080));
        // 打开一个Selector
        Selector selector = Selector.open();
        // 注册到Selector中,ACCEPT操作
        channel.register(selector, SelectionKey.OP_ACCEPT);
        // handler处理
        ServerHandler handler = new ServerHandler();
        // 不断轮询Selector
        while (true){
            // 当准备好的通道大于0才有往下的操作
            if (selector.select()>0){
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    // 接收状态
                    if (key.isAcceptable()){
                        handler.handleAccept(key);
                    }
                    // 可读状态
                    if (key.isReadable()){
                        handler.handleRead(key);
                    }
                    // 处理过的key要移除掉
                    iterator.remove();
                }
            }
        }
    }
}

服务端通信处理:上面轮询Selector时,根据通道的状态来调用此处理器的方法来处理此次的通信

package com.hyf.nio.socket;

import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

/**
 * @author Howinfun
 * @desc 服务器的通信处理
 * @date 2019/6/21
 */
public class ServerHandler {
    public  void handleAccept(SelectionKey key) throws Exception{
        ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
        // 获取客户端链接,并注册到Selector中
        SocketChannel clientChannel = serverSocketChannel.accept();
        clientChannel.configureBlocking(false);
        // 讲通道注册到Selector里头,然后设置为读操作,第三个参数是将你需要带的东西,可通过key获取
        clientChannel.register(key.selector(),SelectionKey.OP_READ,ByteBuffer.allocate(48));
    }

    public void handleRead(SelectionKey key) throws Exception{
        SocketChannel clientChannel = (SocketChannel) key.channel();
        ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
        byteBuffer.clear();
        long byteRead = clientChannel.read(byteBuffer);
        if (byteRead == -1){
            clientChannel.close();
        }else{
            // 读取客户端发送的消息,并做出相应返回对应的消息
            byteBuffer.flip();
            String receiveMsg = new String(byteBuffer.array(),0,byteBuffer.limit());
            System.out.println("接收来自"+clientChannel.socket().getRemoteSocketAddress()+"的消息:"+receiveMsg);
            // 返回回应给客户端
            String sendMsg = "你好客户端,我已经接收到你的信息";
            byteBuffer.clear();
            byteBuffer.put(sendMsg.getBytes());
            byteBuffer.flip();
            clientChannel.write(byteBuffer);
            // 关闭通道 PS:千万不要关闭通道,不然客户端那边就被关闭掉了。。
            //clientChannel.close();
        }

    }
}

客户端:因为客户端要一直循环接收用户的输入,所以Selector的轮询是多开一条线程去处理

package com.hyf.nio.socket;

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.Scanner;

/**
 * @author Howinfun
 * @desc 客户端
 * @date 2019/6/21
 */
public class TestSocketChannel {
    private static boolean flag = true;
    public static void main(String[] args) throws Exception{
        // 打开一个客户端通道
        SocketChannel socketChannel = SocketChannel.open();
        // 网络连接
        socketChannel.connect(new InetSocketAddress("127.0.0.1",8080));
        socketChannel.configureBlocking(false);
        Selector selector = Selector.open();
        // 往selector里注册读操作,客户端往后的读操作都由selector去搞定
        socketChannel.register(selector, SelectionKey.OP_READ);
        // 启动一个线程去处理客户端的消息接收
        Thread thread = new Thread(new ClientSelectorThread(selector));
        thread.start();
        new Thread(()->{
            try {
                // 可循环输入,读取控制台的输入
                while (flag) {
                    System.out.print("请输入:");
                    //键盘输入数据
                    Scanner scan = new Scanner(System.in);
                    String msg = scan.next();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(48);
                    byteBuffer.clear();
                    byteBuffer.put(msg.getBytes());
                    byteBuffer.flip();
                    // 给服务器写消息
                    socketChannel.write(byteBuffer);
                }
            } catch (IOException e) {
                // 如果报错则断开循环输入
                flag = false;
            } finally {
                //最后关闭资源
                try {
                    socketChannel.close();
                    selector.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
}

轮询客户端Selector的线程:

package com.hyf.nio.socket;

import lombok.AllArgsConstructor;

import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;

/**
 * @author Howinfun
 * @desc 客户端的Selector,负责客户端的读操作
 * @date 2019/6/21
 */
@AllArgsConstructor
public class ClientSelectorThread implements Runnable{
    private Selector selector;

    @Override
    public void run() {
        try {
            ClientHandler clientHandler = new ClientHandler();
            while(true){
                if (selector.select()>0){
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while(iterator.hasNext()){
                        SelectionKey key = iterator.next();
                        if (key.isReadable()){
                            clientHandler.handleRead(key);
                        }

                        iterator.remove();
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

客户端通信处理:上面轮询Selector时,根据通道的状态来调用此处理器的方法来处理此次的通信:

package com.hyf.nio.socket;

import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;

/**
 * @author Howinfun
 * @desc 客户端的通信处理
 * @date 2019/6/21
 */
public class ClientHandler {

    public void handleRead(SelectionKey key) throws Exception{
        SocketChannel clientChannel = (SocketChannel) key.channel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(48);
        byteBuffer.clear();
        long byteRead = clientChannel.read(byteBuffer);
        if (byteRead == -1){
            clientChannel.close();
        }else{
            byteBuffer.flip();
            String receiveMsg = new String(byteBuffer.array(),0,byteBuffer.limit());
            System.out.println("接收来自服务器的消息:"+receiveMsg);
        }
    }
}

这是自己写的例子,必须是能运行并且能玩的!如果各位老铁有什么疑问或者出现什么问题,欢迎留言~

发布了156 篇原创文章 · 获赞 76 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/Howinfun/article/details/93339590
今日推荐