Java中Nio编程实现网络编程的多客户端与服务器连接完整步骤
Posted Cabbage coder
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中Nio编程实现网络编程的多客户端与服务器连接完整步骤相关的知识,希望对你有一定的参考价值。
前言:
物联网答辩终于结束了 记一下自己网络编程的代码 重连代码写了好久 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<SelectionKey> 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 数据库代码及实体类:
如果还想实现数据库方面代码,私我
以上是关于Java中Nio编程实现网络编程的多客户端与服务器连接完整步骤的主要内容,如果未能解决你的问题,请参考以下文章