总结:编程的核心是通道和选择器。 选择器通过不断的轮询来执行相应的功能。 所以你需要捕获这个异常并开始不断地重新连接。 如果客户端关闭,服务器也必须关闭其数据库代码和实体类。 如果您仍然想实现数据库代码,请将其保密。
前言:
物联网的防御终于结束了。 请记住重新连接您的网络编程代码。 当我有一段时间第一次写代码的时候,我发现try catch还可以这样用!
学习地址:https://www.bilibili.com/video/BV1gz4y1C7RK
项目结构:
1.服务器代码:package Server;import Entiles.User;导入 Utils.JdbcUtil;导入 java.io.IOException;导入 java.net.InetSocketAddress;导入 java.nio.ByteBuffer;导入 java.nio.channels.*;导入java.nio.charset.Charset;导入java.text.SimpleDateFormat;导入java.util.Iterator;导入java.util.Scanner;导入java.util.Set;公共类ChatServer { public void StratServer(int port) throws IOException { //Socket 通道 客户端通道 //创建服务器通道ServerSocketChannelserverSocketChannel = ServerSocketChannel.open(); //非阻塞模式 serverSocketChannel.configureBlocking(false); //创建缓冲区 ByteBuffer byteBuffer = ByteBuffer.allocate(1024); //绑定端口 serverSocketChannel.bind(new InetSocketAddress(port)); //选择器选择器 创建一个选择器selector = Selector.open(); //注册通道。 //轮播查询 System.out.println("智能水表服务器(端口:"+port+")已启动"); // 如果有就绪通道,则select方法为1 while(true){ while (selector. select()>0){// 由于我们有多个通道,因此我们使用集合来获取所有就绪通道 // 通过 key 获取所有就绪通道的集合 设置 SetselectionKeys = Selector.selectedKeys(); // 将 selectedKeys 中设置的所有准备好的键 // 转换为集合迭代器Iterator iterator = selectedKeys.iterator(); while(iterator.hasNext()){SelectionKeyselectionKey = iterator.next(); //有人来连接 if(selectionKey.isAcceptable()){acceptOperator(serverSocketChannel,selector);通过 /else if(selectionKey.isReadable()){ 发送 readOperator(selector,selectionKey); } //返回水表数据 else if(selectionKey.isWritable()){ writeOperator(selector,selectionKey); iterator.remove(); } } } } //处理服务器写入事件(选择器选择ector,SelectionKeyselectionKey) { try { //有一个要写入的通道,因此检索可写的通道。 SocketChannel Socket channel = (SocketChannel)selectionKey.channel(); // 设计一个非阻塞套接字 Channel.configureBlocking(false). socketChannel.write(Charset.forName("UTF-8").encode("数据库保存成功!" )); //使用它设计监听的选择器重新注册通道。 }catch (IOException e){ e.printStackTrace(); } } // 处理读事件 private void readOperator(Selector Selector, SelectionKeyselectionKey) throws IOException { try { // 准备 Channel SocketChannel Socket channel = (SocketChannel )selectionKey.channel( );//设计缓冲区ByteBufferbuffer = ByteBuffer.allocate(1024); //循环读取客户端数据 int string msg=""; if((length=socketChannel.read(buffer))>0){ //读入缓冲区 //切换模式 buffer.flip(); msg+=Charset.forName("UTF-8").decode(buffer); //从缓冲区中获取数据解码} System.out.println(msg); String str[]=msg.split(":"); String temp=str[1]; "); SimpleDateFormat tempDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 字符串日期和时间 = tempDate.format(new java.util.Date()); // 向选择器重新注册通道。 设计用于监听socketChannel .registe。r(selector,SelectionKey.OP_WRITE); }catch (IOException e){selectionKey.cancel(); System.out.println("发生客户端断开连接" } / /广播给其他用户 // if(msg.length()>0){// System.out.println(msg);// CastOtherClient(msg,selector,socketChannel);// } } // 广播其他客户端 // private void CastOtherClient(String msg, Selectorselector, SocketChannel socketchannel) throws IOException {//// // 全部准备好获取通道/ /SetselectionKeySet = selecter.keys();//循环处理可搜索通道// for (SelectionKeyselectionKey:selectionKeySet){ 获取每个通道 // Channel tarChannel = selectionKey.channel();//不要给自己发送信息//if(tarChannelinstanceofSocketChannel&&tarChannel!=socketChannel){//((SocketChannel)tarChannel).write(Charset.forName("UTF-8") ) .encode (msg)); //对发送的数据进行编码,对发送的数据进行解码// }// }// } //处理接收状态的通道 private void acceptOperator(ServerSocketChannel serverSocketChannel, Selector 选择器) { try { // 获取连接 SocketChannel. Accept(); // 设计非阻塞。 socketChannel.configureBlocking(false); // 注册通道。 socketChannel.register(selector,SelectionKey.OP_READ); //回复客户端消息。 }catch (IOException e){ e.printStackTrace(); public static void main(String[] args) throws IOException { new ChatServer().StratServer(7890); }}
说明:我的readOperator函数从终端读取客户信息起作用。 Nio编程的核心是通道和选择器。 选择器通过不断的轮询来执行相应的功能。 将此项目添加到数据库中。 一旦收到客户信息,就会将其存储在数据库中。
2. 客户端代码
2.1. 负责发送的主客户端
包 Client;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java . .channels.SelectionKey;导入 java.nio.channels.Selector ;导入 java.nio.channels.SocketChannel;Import java.nio.charset.Charset;Import java.util.Scanner;public class ChatClient { public void startClient(String name) throws IOException, InterruptedException { // 绑定主机和端口的 Channel SocketChannel 创建。 Socket Channel = SocketChannel.open(新我netSocketAddress( "127.0.0.1", 7890)); //接受服务器返回的数据 Selector = Selector.open(); SocketChannel.configureBlocking(false); //创建线程 new Thread(new ClientThread(selector)).start();//负责在服务器端获取数据//发送数据到服务器 System.out.println("请输入水表读数、水表读数金额、抄表值(Water测试时间自动生成)(请在1分钟内完成) Scanner Scanner = new Scanner (System.in); while (scanner.hasNextLine()){ ByteBuffer byteBuffer = ByteBuffer.allocate(1024); String = 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); //设计缓冲区 } } public static void main(String[] args) throws IOException, InterruptedException { new ChatClient().startClient("gx"); }}
2.2. 负责接收服务器信息的Client线程
Client;Import java.io.IOException;导入java.net.ConnectException;导入java.net.InetSocketAddress;导入java.nio.ByteBuffer;导入java.nio.channels.SelectionKey;导入java.nio.channels.Selector;导入java.nio。 Channel.SocketChannel;导入java.nio.charset.Charset;导入java.util.Iterator;导入 java.util.Set; 公共类 ClientThread 实现 Runnable{ 私有选择器选择器; this.selector=selector; @Override public void run().{// while(true){ for(;;){ try { int length=selector.select(); if(length ==0){ continue; } // 设置所有就绪通道的集合 Get SetselectionKeys = 选择器。 selectedKeys(); //selectedKeys的所有准备好的key集合 //转换为集合迭代器 Iterator iterator = selectedKeys.iterator(); while(iterator.hasNext()){SelectionKeyselectionKey = iterator.next(); if(selectionKey.isReadable()){ readOperator(selector,selectionKey); iterator.remove();| InterruptedException e) { e.printStackTrace() } } } // 处理接收方服务器信息事件 private void readOperator(Selector selected, SelectionKeyselectionKey) throws InterruptedException { try { // 准备好通道 SocketChannel Socket channel = (SocketChannel )selectionKey.channel ();//设计缓冲区 ByteBuffer Buffer = ByteBuffer.allocate(1024);//循环读取客户端数据 int length=0; if((length=socketChannel.read(buffer))>0){ //读入缓冲区//切换模式buffer.flip(); msg+= Charset.forName("UTF-8").decode(buffer); //从缓冲区获取数据并解码} System.out.println(msg);// 向选择器重新注册通道。 它被设计为监听socketChannel.register(selector,SelectionKey.OP_READ); }catch (IOException e){selectionKey.cancel(); System.out.println("服务器中断开始准备恢复"); while (true){ try { new ChatClient().startClient("gx" ); IOException ioException) { System.out.println("重新连接(5 秒)"); //ioException.printStackTrace(); Thread.sleep(5000); 继续; } } } }} 3.主要重连代码解释
1 关于代码解释
如果服务器突然关闭,在这里捕获异常的话如果不这样做,如果服务器异常关闭,客户端也会挂起并且无法重新连接。 所以你需要捕获这个异常并开始不断地重新连接。 我写了一个无限循环来不断连接。 即使无法连接也会报错,因此需要捕获连接失败的异常。 如果没有,程序将报告错误并退出。 所以如果capture连接没有连接上, continue 会重新执行 while 。 环形。 直到连接到服务器。
如果2如果客户端关闭了,服务端也必须主动关闭客户端。
4 数据库代码和实体类:
如果要实现数据库代码,请保持私有
评论前必须登录!
注册