资讯专栏INFORMATION COLUMN

Java中Nio编程实现网络编程的多客户端与服务器连接完整步骤

不知名网友 / 2558人阅读

摘要:编程核心是通道和选择器,选择器通过不断轮询,执行对应的函数。所以我们需要捕获这个异常,并且开始不断重连。如果客户端关闭那么服务器也要主动关闭他数据库代码及实体类如果还想实现数据库方面代码,私我

前言:

物联网答辩终于结束了 记一下自己网络编程的代码 重连代码写了好久 try catch原来可以这么用!

学习地址:https://www.bilibili.com/video/BV1gz4y1C7RK

项目结构:

1、服务器代码:

package Server;import Entiles.User;import Utils.JdbcUtil;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.*;import java.nio.charset.Charset;import java.text.SimpleDateFormat;import java.util.Iterator;import java.util.Scanner;import java.util.Set;public class ChatServer {    public void StratServer(int port) throws IOException {        //soket通道 客户通道        //创建服务端通道        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();        //非堵塞模式        serverSocketChannel.configureBlocking(false);        //创建buffer        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);        //绑定端口        serverSocketChannel.bind(new InetSocketAddress(port));        //创建selector 选择器        Selector selector = Selector.open();        //注册通道        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);        //轮播查询        System.out.println("智能水表服务端(端口:"+port+")已经启动");//        如果有就绪状态的通道 则select方法返回1        while(true){            while (selector.select()>0){//            因为有多个通道 ,所以采用集合 获取所有就绪的通道                //        得到所有就绪状态的通道集合到key中                Set<SelectionKey> selectionKeys = selector.selectedKeys(); //selectedKeys所有已经就绪的key集合//        转成集合迭代器                Iterator<SelectionKey> iterator = selectionKeys.iterator();                while(iterator.hasNext()){                    SelectionKey selectionKey = iterator.next();                    //有人来连                    if(selectionKey.isAcceptable()){                        acceptOperator(serverSocketChannel,selector);                    }                    //发过来了已经                    else if(selectionKey.isReadable()){                        readOperator(selector,selectionKey);                    }                    //返回水表数据                    else if(selectionKey.isWritable()){                        writeOperator(selector,selectionKey);                    }                    iterator.remove();                }            }        }    }    //处理服务器写事件    private void writeOperator(Selector selector,SelectionKey selectionKey) {        try {            //有channel可写,取出可写的channel            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();            //                    设计非阻塞            socketChannel.configureBlocking(false);            socketChannel.write(Charset.forName("UTF-8").encode("数据库存入成功!" ));            //重新将channel注册到选择器上,设计为监听            socketChannel.register(selector,SelectionKey.OP_READ);        }catch (IOException e){            e.printStackTrace();        }    }    //    处理读事件    private void readOperator(Selector selector, SelectionKey selectionKey) throws IOException {        try {            //                    获取就绪通道            SocketChannel socketChannel = (SocketChannel)selectionKey.channel();//                    设计buffer            ByteBuffer buffer = ByteBuffer.allocate(1024);//                    循环读客户端数据            int length=0;            String msg="";            if((length=socketChannel.read(buffer))>0){  //读到buffer里面//                        切换模式                buffer.flip();                msg+=Charset.forName("UTF-8").decode(buffer);  //从buffer里面取数据 解码            }            System.out.println(msg);            String str[]=msg.split(":");            String temp=str[1];            String str2[]=temp.split("、");            SimpleDateFormat tempDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");            String datetime = tempDate.format(new java.util.Date());            JdbcUtil.find(new User(str2[0],str2[1],str2[2],datetime));            //重新将channel注册到选择器上,设计为监听            socketChannel.register(selector,SelectionKey.OP_WRITE);        }catch (IOException e){            selectionKey.cancel();            selectionKey.channel().close();            System.out.println("有客户端断连,我已主动关闭");        }                        //光广播到其他用户上去//                        if(msg.length()>0){//                            System.out.println(msg);//                            castOtherClient(msg,selector,socketChannel);//                        }    }        //广播到其他客户端//    private void castOtherClient(String msg, Selector selector, SocketChannel socketChannel) throws IOException {////        //获取所有就绪的channel//        Set selectionKeySet = selector.keys();//        循环处理搜索就绪的channel//        for (SelectionKey selectionKey : selectionKeySet){            获取每一个channel//            Channel tarChannel = selectionKey.channel();//            不给自己发信息//            if(tarChannel instanceof SocketChannel && tarChannel!=socketChannel){//                ((SocketChannel)tarChannel).write(Charset.forName("UTF-8").encode(msg)); //传输数据是编码,发送数据是解码//            }//        }//    }    //处理接收状态的通道    private void acceptOperator(ServerSocketChannel serverSocketChannel, Selector selector)  {        try {            //                    获取连接            SocketChannel socketChannel = serverSocketChannel.accept();//                    设计非阻塞            socketChannel.configureBlocking(false);//                     注册通道            socketChannel.register(selector,SelectionKey.OP_READ);            //回复客户端消息            socketChannel.write(Charset.forName("UTF-8").encode("您已成功连接到服务器!"));        }catch (IOException e){            e.printStackTrace();        }    }    public static void main(String[] args) throws IOException {        new ChatServer().StratServer(7890);    }}

解释:
其中我的readOperator函数是负责读取客户端传来的信息。Nio编程核心是通道和选择器,选择器通过不断轮询,执行对应的函数。
此项目我加入了数据库,如果收到客户端信息,存入数据库。

2、客户端代码

2.1、负责发送的主客户端

package Client;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.nio.charset.Charset;import java.util.Scanner;public class ChatClient {    public void startClient(String name ) throws IOException, InterruptedException {        //        创建通道 绑定主机和端口        SocketChannel socketChannel = null;            socketChannel = SocketChannel.open(new InetSocketAddress(                    "127.0.0.1", 7890));        //接受服务端响应的数据        Selector selector = Selector.open();        socketChannel.configureBlocking(false);        socketChannel.register(selector, SelectionKey.OP_READ);        //创建线程        new Thread(new ClientThread(selector)).start();//负责拿到服务器端数据        //向服务端发送数据        System.out.println("请输入抄水表编号、抄水量、抄表员(抄水时间自动生成)(请在1min中内完成)");        Scanner scanner = new Scanner(System.in);        while (scanner.hasNextLine()){            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);            String str = scanner.nextLine(); //键盘获取输入的内容            if(str.length()>0){               socketChannel.write(Charset.forName("UTF-8").encode("客户端:"+name+":"+str+"(已加载数据库)"));                //System.out.println(Charset.forName("UTF-8").encode(name+":"+str));            }        //设计非堵塞模式        socketChannel.configureBlocking(false);        //设计buffer    }    }    public static void main(String[] args) throws IOException, InterruptedException {        new ChatClient().startClient("gx");    }}

2.2、负责接收服务器信息的客户端线程

package Client;import java.io.IOException;import java.net.ConnectException;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.nio.charset.Charset;import java.util.Iterator;import java.util.Set;public class ClientThread implements Runnable{    private Selector selector;    ClientThread(Selector selector){        this.selector=selector;    }    @Override    public void run() {//        while(true){            for(;;){                try {                    int length=selector.select();                    if(length ==0){                        continue;                    }                    //        得到所有就绪状态的通道集合到key中                    Set<SelectionKey> selectionKeys = selector.selectedKeys(); //selectedKeys所有已经就绪的key集合                    //        转成集合迭代器                    Iterator<SelectionKey> iterator = selectionKeys.iterator();                    while(iterator.hasNext()){                        SelectionKey selectionKey = iterator.next();                        if(selectionKey.isReadable()){                            readOperator(selector,selectionKey);                        }                        iterator.remove();                    }                } catch (IOException | InterruptedException e) {                    e.printStackTrace();                }            }        }    //    处理接收服务器信息事件    private void readOperator(Selector selector, SelectionKey selectionKey) throws InterruptedException {        try {            //                    获取就绪通道            SocketChannel socketChannel = (SocketChannel)selectionKey.channel();//                    设计buffer            ByteBuffer buffer = ByteBuffer.allocate(1024);//                    循环读客户端数据            int length=0;            String msg="";            if((length=socketChannel.read(buffer))>0){  //读到buffer里面//                        切换模式                buffer.flip();                msg+= Charset.forName("UTF-8").decode(buffer);  //从buffer里面取数据 解码            }            System.out.println(msg);            //重新将channel注册到选择器上,设计为监听            socketChannel.register(selector,SelectionKey.OP_READ);        }catch (IOException e){            selectionKey.cancel();            System.out.println("服务器中断 开始准备重连");            while (true){                try {                    new ChatClient().startClient("gx");                } catch (IOException ioException) {                    System.out.println("正在重连(5s) ");                    //ioException.printStackTrace();                    Thread.sleep(5000);                    continue;                }            }        }    }}

3、关键重连代码解释

1 重连代码解释

如果服务器突然关闭,如果在这里不try catch异常,而是向上抛出的话,随着服务器异常关闭,客户端也挂了,而且不会重连。
所以我们需要捕获这个异常,并且开始不断重连。
我写了一个死循环,不断连接,如果连不上他还是会报错,所以连不上的错也要捕获异常,不然程序就报错结束了,所以捕获连接没上的话,continue重新执行while循环,直到连接上服务器。

2 如果客户端关闭 那么服务器也要主动关闭他

4 数据库代码及实体类:

如果还想实现数据库方面代码,私我

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/125587.html

相关文章

  • 一篇文章带你彻底搞懂NIO

    摘要:阻塞当进行读写时,线程是阻塞的状态。当任何一个收到数据后,中断程序将唤起进程。接收数据当收到数据后,中断程序会给的就绪列表添加引用。当接收到数据,中断程序一方面修改,另一方面唤醒等待队列中的进程,进程再次进入运行状态如下图。 本篇文章目的在于基本概念和原理的解释,不会贴过多的使用代码。 什么是NIO Java NIO (New IO)是 Java 的另一个 IO API (来自 jav...

    ziwenxie 评论0 收藏0
  • Java NIO浅析

    摘要:阻塞请求结果返回之前,当前线程被挂起。也就是说在异步中,不会对用户线程产生任何阻塞。当前线程在拿到此次请求结果的过程中,可以做其它事情。事实上,可以只用一个线程处理所有的通道。 准备知识 同步、异步、阻塞、非阻塞 同步和异步说的是服务端消息的通知机制,阻塞和非阻塞说的是客户端线程的状态。已客户端一次网络请求为例做简单说明: 同步同步是指一次请求没有得到结果之前就不返回。 异步请求不会...

    yeooo 评论0 收藏0
  • 初探Java Socket

    摘要:绑定完成后允许套接字进行连接并等待连接。服务端根据报文返回响应,并关闭连接。单线程服务器多进程及多线程服务器复用服务器复用的多线程服务器单线程服务器一次只处理一个请求,直到其完成为止。 前言 本篇文章将涉及以下内容: IO实现Java Socket通信 NIO实现Java Socket通信 阅读本文之前最好了解过: Java IO Java NIO Java Concurrenc...

    张汉庆 评论0 收藏0
  • java同步非阻塞IO

    摘要:的异步即是异步的,也是非阻塞的。但是,也可以进行一层稍微薄点的封装,保留这种多路复用的模型,比如的,是一种同步非阻塞的模型。系统调用操作系统的系统调用提供了多路复用的非阻塞的系统调用,这也是机制实现需要用到的。 异步IO编程在javascript中得到了广泛的应用,之前也写过一篇博文进行梳理。js的异步IO即是异步的,也是非阻塞的。非阻塞的IO需要底层操作系统的支持,比如在linux上...

    caoym 评论0 收藏0
  • 少啰嗦!一分钟带你读懂JavaNIO和经典IO的区别

    摘要:的选择器允许单个线程监视多个输入通道。一旦执行的线程已经超过读取代码中的某个数据片段,该线程就不会在数据中向后移动通常不会。 1、引言 很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO? 在本文中,将尝试用简明扼要的文字,阐明Java NIO和经典IO之...

    Meils 评论0 收藏0

发表评论

0条评论

不知名网友

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<