JavaNIO实现聊天室功能代码实例
发表于:2025-01-31 作者:千家信息网编辑
千家信息网最后更新 2025年01月31日,这篇文章主要讲解了"JavaNIO实现聊天室功能代码实例",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"JavaNIO实现聊天室功能代码实例"吧!一、服
千家信息网最后更新 2025年01月31日JavaNIO实现聊天室功能代码实例
这篇文章主要讲解了"JavaNIO实现聊天室功能代码实例",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"JavaNIO实现聊天室功能代码实例"吧!
一、服务器端
package com.chat.server;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.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Iterator;import java.util.Vector;/** * 聊天室:服务端 * @author zing * */public class ChatServer implements Runnable { //选择器 private Selector selector; //注册ServerSocketChannel后的选择键 private SelectionKey serverKey; //标识是否运行 private boolean isRun; //当前聊天室中的用户名称列表 private Vectorunames; //时间格式化器 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * 构造函数 * @param port 服务端监控的端口号 */ public ChatServer(int port) { isRun = true; unames = new Vector (); init(port); } /** * 初始化选择器和服务器套接字 * * @param port 服务端监控的端口号 */ private void init(int port) { try { //获得选择器实例 selector = Selector.open(); //获得服务器套接字实例 ServerSocketChannel serverChannel = ServerSocketChannel.open(); //绑定端口号 serverChannel.socket().bind(new InetSocketAddress(port)); //设置为非阻塞 serverChannel.configureBlocking(false); //将ServerSocketChannel注册到选择器,指定其行为为"等待接受连接" serverKey = serverChannel.register(selector, SelectionKey.OP_ACCEPT); printInfo("server starting..."); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { try { //轮询选择器选择键 while (isRun) { //选择一组已准备进行IO操作的通道的key,等于1时表示有这样的key int n = selector.select(); if (n > 0) { //从选择器上获取已选择的key的集合并进行迭代 Iterator iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey key = iter.next(); //若此key的通道是等待接受新的套接字连接 if (key.isAcceptable()) { //记住一定要remove这个key,否则之后的新连接将被阻塞无法连接服务器 iter.remove(); //获取key对应的通道 ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); //接受新的连接返回和客户端对等的套接字通道 SocketChannel channel = serverChannel.accept(); if (channel == null) { continue; } //设置为非阻塞 channel.configureBlocking(false); //将这个套接字通道注册到选择器,指定其行为为"读" channel.register(selector, SelectionKey.OP_READ); } //若此key的通道的行为是"读" if (key.isReadable()) { readMsg(key); } //若次key的通道的行为是"写" if (key.isWritable()) { writeMsg(key); } } } } } catch (IOException e) { e.printStackTrace(); } } /** * 从key对应的套接字通道上读数据 * @param key 选择键 * @throws IOException */ private void readMsg(SelectionKey key) throws IOException { //获取此key对应的套接字通道 SocketChannel channel = (SocketChannel) key.channel(); //创建一个大小为1024k的缓存区 ByteBuffer buffer = ByteBuffer.allocate(1024); StringBuffer sb = new StringBuffer(); //将通道的数据读到缓存区 int count = channel.read(buffer); if (count > 0) { //翻转缓存区(将缓存区由写进数据模式变成读出数据模式) buffer.flip(); //将缓存区的数据转成String sb.append(new String(buffer.array(), 0, count)); } String str = sb.toString(); //若消息中有"open_",表示客户端准备进入聊天界面 //客户端传过来的数据格式是"open_zing",表示名称为zing的用户请求打开聊天窗体 //用户名称列表有更新,则应将用户名称数据写给每一个已连接的客户端 if (str.indexOf("open_") != -1) {//客户端连接服务器 String name = str.substring(5); printInfo(name + " online"); unames.add(name); //获取选择器已选择的key并迭代 Iterator iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey selKey = iter.next(); //若不是服务器套接字通道的key,则将数据设置到此key中 //并更新此key感兴趣的动作 if (selKey != serverKey) { selKey.attach(unames); selKey.interestOps(selKey.interestOps() | SelectionKey.OP_WRITE); } } } else if (str.indexOf("exit_") != -1) {// 客户端发送退出命令 String uname = str.substring(5); //删除此用户名称 unames.remove(uname); //将"close"字符串附加到key key.attach("close"); //更新此key感兴趣的动作 key.interestOps(SelectionKey.OP_WRITE); //获取选择器上的已选择的key并迭代 //将更新后的名称列表数据附加到每个套接字通道key上,并重设key感兴趣的操作 Iterator iter = key.selector().selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey selKey = iter.next(); if (selKey != serverKey && selKey != key) { selKey.attach(unames); selKey.interestOps(selKey.interestOps() | SelectionKey.OP_WRITE); } } printInfo(uname + " offline"); } else {// 读取客户端聊天消息 String uname = str.substring(0, str.indexOf("^")); String msg = str.substring(str.indexOf("^") + 1); printInfo("("+uname+")说:" + msg); String dateTime = sdf.format(new Date()); String smsg = uname + " " + dateTime + "\n " + msg + "\n"; Iterator iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey selKey = iter.next(); if (selKey != serverKey) { selKey.attach(smsg); selKey.interestOps(selKey.interestOps() | SelectionKey.OP_WRITE); } } } } /** * 写数据到key对应的套接字通道 * @param key * @throws IOException */ private void writeMsg(SelectionKey key) throws IOException { SocketChannel channel = (SocketChannel) key.channel(); Object obj = key.attachment(); //这里必要要将key的附加数据设置为空,否则会有问题 key.attach(""); //附加值为"close",则取消此key,并关闭对应通道 if (obj.toString().equals("close")) { key.cancel(); channel.socket().close(); channel.close(); return; }else { //将数据写到通道 channel.write(ByteBuffer.wrap(obj.toString().getBytes())); } //重设此key兴趣 key.interestOps(SelectionKey.OP_READ); } private void printInfo(String str) { System.out.println("[" + sdf.format(new Date()) + "] -> " + str); } public static void main(String[] args) { ChatServer server = new ChatServer(19999); new Thread(server).start(); }}
二、客户端
1、服务类,用于与服务端交互
package com.chat.client;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SocketChannel;public class ClientService { private static final String HOST = "127.0.0.1"; private static final int PORT = 19999; private static SocketChannel sc; private static Object lock = new Object(); private static ClientService service; public static ClientService getInstance(){ synchronized (lock) { if(service == null){ try { service = new ClientService(); } catch (IOException e) { e.printStackTrace(); } } return service; } } private ClientService() throws IOException { sc = SocketChannel.open(); sc.configureBlocking(false); sc.connect(new InetSocketAddress(HOST, PORT)); } public void sendMsg(String msg) { try { while (!sc.finishConnect()) { } sc.write(ByteBuffer.wrap(msg.getBytes())); } catch (IOException e) { e.printStackTrace(); } } public String receiveMsg() { ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.clear(); StringBuffer sb = new StringBuffer(); int count = 0; String msg = null; try { while ((count = sc.read(buffer)) > 0) { sb.append(new String(buffer.array(), 0, count)); } if (sb.length() > 0) { msg = sb.toString(); if ("close".equals(sb.toString())) { msg = null; sc.close(); sc.socket().close(); } } } catch (IOException e) { e.printStackTrace(); } return msg; }}
2、登陆窗体,用户设置名称
package com.chat.client;import java.awt.Toolkit;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JTextField;/** * 设置名称窗体 * * @author zing * */public class SetNameFrame extends JFrame { private static final long serialVersionUID = 1L; private static JTextField txtName;// 文本框 private static JButton btnOK;// ok按钮 private static JLabel label;// 标签 public SetNameFrame() { this.setLayout(null); Toolkit kit = Toolkit.getDefaultToolkit(); int w = kit.getScreenSize().width; int h = kit.getScreenSize().height; this.setBounds(w / 2 - 230 / 2, h / 2 - 200 / 2, 230, 200); this.setTitle("设置名称"); this.setDefaultCloseOperation(EXIT_ON_CLOSE); this.setResizable(false); txtName = new JTextField(4); this.add(txtName); txtName.setBounds(10, 10, 100, 25); btnOK = new JButton("OK"); this.add(btnOK); btnOK.setBounds(120, 10, 80, 25); label = new JLabel("[w:" + w + ",h:" + h + "]"); this.add(label); label.setBounds(10, 40, 200, 100); label.setText("在上面的文本框中输入名字
显示器宽度:" + w + "
显示器高度:" + h + ""); btnOK.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String uname = txtName.getText(); ClientService service = ClientService.getInstance(); ChatFrame chatFrame = new ChatFrame(service, uname); chatFrame.show(); setVisible(false); } }); } public static void main(String[] args) { SetNameFrame setNameFrame = new SetNameFrame(); setNameFrame.setVisible(true); }}
3、聊天室窗体
package com.chat.client;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import javax.swing.DefaultListModel;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JList;import javax.swing.JScrollPane;import javax.swing.JTextArea;import javax.swing.event.ListSelectionEvent;import javax.swing.event.ListSelectionListener;/** * 聊天室窗体 * @author zing * */public class ChatFrame { private JTextArea readContext = new JTextArea(18, 30);// 显示消息文本框 private JTextArea writeContext = new JTextArea(6, 30);// 发送消息文本框 private DefaultListModel modle = new DefaultListModel();// 用户列表模型 private JList list = new JList(modle);// 用户列表 private JButton btnSend = new JButton("发送");// 发送消息按钮 private JButton btnClose = new JButton("关闭");// 关闭聊天窗口按钮 private JFrame frame = new JFrame("ChatFrame");// 窗体界面 private String uname;// 用户姓名 private ClientService service;// 用于与服务器交互 private boolean isRun = false;// 是否运行 public ChatFrame(ClientService service, String uname) { this.isRun = true; this.uname = uname; this.service = service; } // 初始化界面控件及事件 private void init() { frame.setLayout(null); frame.setTitle(uname + " 聊天窗口"); frame.setSize(500, 500); frame.setLocation(400, 200); //设置可关闭 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //不能改变窗体大小 frame.setResizable(false); //聊天消息显示区带滚动条 JScrollPane readScroll = new JScrollPane(readContext); readScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); frame.add(readScroll); //消息编辑区带滚动条 JScrollPane writeScroll = new JScrollPane(writeContext); writeScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); frame.add(writeScroll); frame.add(list); frame.add(btnSend); frame.add(btnClose); readScroll.setBounds(10, 10, 320, 300); readContext.setBounds(0, 0, 320, 300); readContext.setEditable(false);//设置为不可编辑 readContext.setLineWrap(true);// 自动换行 writeScroll.setBounds(10, 315, 320, 100); writeContext.setBounds(0, 0, 320, 100); writeContext.setLineWrap(true);// 自动换行 list.setBounds(340, 10, 140, 445); btnSend.setBounds(150, 420, 80, 30); btnClose.setBounds(250, 420, 80, 30); //窗体关闭事件 frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { isRun = false; service.sendMsg("exit_" + uname); System.exit(0); } }); //发送按钮事件 btnSend.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String msg = writeContext.getText().trim(); if(msg.length() > 0){ service.sendMsg(uname + "^" + writeContext.getText()); } //发送消息后,去掉编辑区文本,并获得光标焦点 writeContext.setText(null); writeContext.requestFocus(); } }); //关闭按钮事件 btnClose.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { isRun = false; service.sendMsg("exit_" + uname); System.exit(0); } }); //右边名称列表选择事件 list.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { // JOptionPane.showMessageDialog(null, // list.getSelectedValue().toString()); } }); //消息编辑区键盘按键事件 writeContext.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } //按下键盘按键后释放 @Override public void keyReleased(KeyEvent e) { //按下enter键发送消息 if(e.getKeyCode() == KeyEvent.VK_ENTER){ String msg = writeContext.getText().trim(); if(msg.length() > 0){ service.sendMsg(uname + "^" + writeContext.getText()); } writeContext.setText(null); writeContext.requestFocus(); } } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub } }); } // 此线程类用于轮询读取服务器发送的消息 private class MsgThread extends Thread { @Override public void run() { while (isRun) { String msg = service.receiveMsg(); if (msg != null) { //若是名称列表数据,则更新聊天窗体右边的列表 if (msg.indexOf("[") != -1 && msg.lastIndexOf("]") != -1) { msg = msg.substring(1, msg.length() - 1); String[] userNames = msg.split(","); modle.removeAllElements(); for (int i = 0; i < userNames.length; i++) { modle.addElement(userNames[i].trim()); } } else { //将聊天数据设置到聊天消息显示区 String str = readContext.getText() + msg; readContext.setText(str); readContext.selectAll();//保持滚动条在最下面 } } } } } // 显示界面 public void show() { this.init(); service.sendMsg("open_" + uname); MsgThread msgThread = new MsgThread(); msgThread.start(); this.frame.setVisible(true); }}
感谢各位的阅读,以上就是"JavaNIO实现聊天室功能代码实例"的内容了,经过本文的学习后,相信大家对JavaNIO实现聊天室功能代码实例这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!
选择
通道
数据
服务
消息
名称
套接字
用户
窗体
聊天室
客户
客户端
服务器
实例
事件
按钮
文本
存区
更新
代码
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
java8 数据库
高级网络安全工程师培训
济南职业学院计算机网络技术
数据库中010是固定的
作文题目网络安全
魔兽怀旧服灵风服务器介绍
东莞学习通网络技术有限公司
网络安全 能源
网络安全主题班会设计总结
ftp服务器访问不了
云服务器安装php
分布式数据库怎么卖
北京麦卡在线网络技术有限公司
网络安全各种加密方式
山东电信代理服务器ip地址
火影忍者手游服务器超时怎么打
第四轮网络安全学科评估
新网服务器搭建网站
以下什么属于数据库系统
网络安全公司研究院
已有数据库创建表
网络安全法300m答题
卫生计生委网络安全管理制度
丽水数据库审计系统怎么样
西安软件开发好找工作吗
网络安全调查问题
常州工程项目管控软件开发平台
阿里云服务器代金券活动
软件开发数据库试题
网络安全学习网址