聊天室

  • 项目简介
    • 一、实现功能
    • 二、设计图
    • 三、GUI部分介绍
    • 四、网络编程部分
    • 五、运行示例
    • 六、代码
      • 1.服务器
      • 2.客户端

项目简介

  这是一个java版的有界面的聊天室项目,主要用到了GUI中的javax.swing包和网络编程中的TCP/socket编程。GUI部分的难点是图形控件的API较为复杂,可以参考API手册。网络编程部分的难点是客户端之间通过服务器进行通信,不仅服务器要使用多线程,而且每个服务线程要求能够调用其他服务线程的socket来向其他客户端发送消息,客户端和服务器的消息都通过socket来传递给对方。

一、实现功能

  1. 当客户端连接到服务器时,可收到服务器连接成功的提示消息,以及服务器更新的当前在线用户列表,显示在线用户的IP地址和端口号,自己的ip和端口号不显示。
  2. 可以用鼠标点击选择在线列表中的某一个用户,在发送消息的文本框输入内容,进行私聊;也可以用鼠标选中在线列表中的所有用户,进行群聊。
  3. 用户可以清空自己的聊天记录,即清屏。
  4. 接收消息时可以显示发送消息的ip地址和端口号。
  5. 客户端下线时,会给服务器发送消息,服务器向所有的客户端更新在线列表。
  6. 服务器使用多线程工作,由HashMap存储所有的服务线程。

二、设计图

三、GUI部分介绍

  1. 聊天窗口:显示自己发送的和收到的信息,包括发信人的ip和端口号,用设置成不可编辑的JTextArea控件实现,用JScrollPane包装实现滚动条。
  2. 打字窗口:用设置成可编辑的JTextArea实现。
  3. 当前在线列表:显示当前在线的所有人(自己除外)的ip地址和端口号,在客户端上线或下线时,服务器向所有客户端即时更新在线列表。用JTable实现,可动态插入或删除行。
  4. 发送按钮:将打字窗口中的文字按照在线列表中选择的收信人发送给服务器,由服务器转发给收信人,然后将打字窗口清屏。用JButton控件实现。
  5. 清屏按钮:将连天窗口清屏,由JButton实现。
  6. 退出按钮:向服务器发送退出信息,并关闭此客户端程序。服务器接收到消息后立马更新在线列表。
  7. 收信人标签:提醒用户,没有收信人则不能发送消息。

四、网络编程部分

  1. 服务器使用多线程工作,每个客户端都享有一个服务线程。
  2. 每个客户端用自己的ip地址和端口号组成一个字符串作为用户标识uid,其组成格式为uid=IP:端口号,ip和端口号用冒号分隔开,方便拆分。
  3. 客户端和服务器之间每次通信都是传递一个字符串, 字符串可能有以下几种结构:
String 作用
Chat/收信人uid/聊天内容 客户端发往服务器,表示该客户端要向别的客户端发送消息
Chat/发信人uid/聊天内容 服务器发给客户端,表示服务器转发给收信客户端的消息
OnLineListUpdate/发信人uid 服务器发给客户端,表示有客户端加入或退出,要更新所有客户端的当前在线列表
Exit/ 客户端发往服务器,表示该客户端退出
  1. 收信人uid格式:ip1:port1,ip2:port2,ip3:port3…
    发信人uid格式:ip:port
  2. 服务器类静态存储容器
容器 内容
String数组 储存当前在线所有人的uid
HashMap<String,服务线程> 储存所有的服务线程,可以根据uid取出对应的服务线程
  1. 服务器用while(true)循环持续监听客户端消息,根据消息类型作处理
服务器收到消息类型 服务器向客户端发送消息类型
Chat/收信人uid /聊天内容 Chat/发信人uid/聊天内容
Exit/ 向所有客户端发送OnLineListUpdate/在线者uid
  1. 客户端用while(true)持续监听服务器消息,根据消息类型作处处理
客户端收到消息类型 客户端如何处理
Chat/发信人uid /聊天内容 在聊天窗口显示发信人uid以及聊天内容
OnLineListUpdate/在线者uid 更新在线列表信息

五、运行示例

六、代码

1.服务器
package chat_room;import java.io.*;
import java.util.*;
import java.net.*;
import java.text.*;public class Server
{public static void main(String[] args) throws Exception{//建立服务器ServerSocketServerSocket ss = new ServerSocket(6666);//提示Server建立成功System.out.println("Server online... " + ss.getInetAddress().getLocalHost().getHostAddress() + ", " + 6666);//监听端口,建立连接并开启新的ServerThread线程来服务此连接while(true){//接收客户端SocketSocket s = ss.accept();//提取客户端IP和端口String ip = s.getInetAddress().getHostAddress();int port = s.getPort();//建立新的服务器线程, 向该线程提供服务器ServerSocket,客户端Socket,客户端IP和端口new Thread(new ServerThread(s, ss, ip, port)).start();}}
}class ServerThread implements Runnable
{//获取的客户端SocketSocket s = null;//获取的服务器ServerSocketServerSocket ss = null;//获取的客户端IPString ip = null;//获取的客户端端口int port = 0;//组合客户端的ip和端口字符串得到uid字符串String uid = null;//静态ArrayList存储所有uid,uid由ip和端口字符串拼接而成static ArrayList<String> uid_arr = new ArrayList<String>();//静态HashMap存储所有uid, ServerThread对象组成的对static HashMap<String, ServerThread> hm = new HashMap<String, ServerThread>();public ServerThread(Socket s, ServerSocket ss, String ip, int port){this.s = s;this.ss = ss;this.ip = ip;this.port = port;uid = ip + ":" + port;}@Overridepublic void run(){//将当前客户端uid存入ArrayListuid_arr.add(uid);//将当前uid和ServerThread对存入HashMaphm.put(uid, this);//时间显示格式SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");//控制台打印客户端IP和端口System.out.println("Client connected: " + uid);try{//获取输入流InputStream in = s.getInputStream();//获取输出流OutputStream out = s.getOutputStream();//向当前客户端传输连接成功信息String welcome = sdf.format(new Date()) + "\n成功连接服务器...\n服务器IP: "+ ss.getInetAddress().getLocalHost().getHostAddress()+ ", 端口: 6666\n客户端IP: " + ip + ", 端口: " + port + "\n";out.write(welcome.getBytes());//广播更新在线名单 updateOnlineList(out);//准备缓冲区byte[] buf = new byte[1024];int len = 0;//持续监听并转发客户端消息while(true){//读取客户端的消息存放在字节数组中len = in.read(buf);//将字节数组转换成字符串String msg = new String(buf, 0, len);System.out.println(msg);//消息类型:退出或者聊天String type = msg.substring(0, msg.indexOf("/"));//消息本体:空或者聊天内容String content = msg.substring(msg.indexOf("/") + 1);//根据消息类型分别处理//客户端要退出if(type.equals("Exit")){//更新ArrayList和HashMap, 删除退出的uid和线程uid_arr.remove(uid_arr.indexOf(uid));hm.remove(uid);//广播更新在线名单updateOnlineList(out);//控制台打印客户端IP和端口System.out.println("Client exited: " + uid);//结束循环,结束该服务线程break;}//客户端要聊天else if(type.equals("Chat")){//提取收信者地址String[] receiver_arr = content.substring(0, content.indexOf("/")).split(",");//提取聊天内容String word = content.substring(content.indexOf("/")+1);//向收信者广播发出聊天信息chatOnlineList(out, uid, receiver_arr, word);}}}catch(Exception e){}}//向所有已连接的客户端更新在线名单public void updateOnlineList(OutputStream out) throws Exception{for(String tmp_uid : uid_arr){//获取广播收听者的输出流out = hm.get(tmp_uid).s.getOutputStream();//将当前在线名单以逗号为分割组合成长字符串一次传送StringBuilder sb = new StringBuilder("OnlineListUpdate/");for(String member : uid_arr){sb.append(member);//以逗号分隔uid,除了最后一个if(uid_arr.indexOf(member) != uid_arr.size() - 1)sb.append(",");}out.write(sb.toString().getBytes());}}//向指定的客户端发送聊天消息public void chatOnlineList(OutputStream out, String uid, String[] receiver_arr, String word) throws Exception{for(String tmp_uid : receiver_arr){//获取广播收听者的输出流out = hm.get(tmp_uid).s.getOutputStream();//发送聊天信息out.write(("Chat/" + uid + "/" + word).getBytes());}}
}
2.客户端
package chat_room;import java.io.*;
import java.net.*;
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.text.*;public class Client
{//建立客户端Socketstatic Socket s = null;//消息接收者uidstatic StringBuilder uidReceiver = null;public static void main(String[] args){//创建客户端窗口对象ClientFrame cframe = new ClientFrame();//窗口关闭键无效,必须通过退出键退出客户端以便善后cframe.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);//获取本机屏幕横向分辨率int w = Toolkit.getDefaultToolkit().getScreenSize().width;//获取本机屏幕纵向分辨率int h = Toolkit.getDefaultToolkit().getScreenSize().height;//将窗口置中cframe.setLocation((w - cframe.WIDTH)/2, (h - cframe.HEIGHT)/2);//设置客户端窗口为可见cframe.setVisible(true);try{//连接服务器s = new Socket(InetAddress.getLocalHost(), 6666);//获取输入流InputStream in = s.getInputStream();//获取输出流OutputStream out = s.getOutputStream();//获取服务端欢迎信息byte[] buf = new byte[1024];//读取数据到buf中,并返回读取长度int len = in.read(buf);//将欢迎信息打印在聊天消息框内cframe.jtaChat.append(new String(buf, 0, len));cframe.jtaChat.append("\n");//持续等待接收服务器信息直至退出while(true){in = s.getInputStream();len = in.read(buf);System.out.println(len);//处理服务器传来的消息String msg = new String(buf, 0, len);//消息类型:更新在线名单或者聊天String type = msg.substring(0, msg.indexOf("/"));//消息本体:更新后的名单或者聊天内容String content = msg.substring(msg.indexOf("/") + 1);//根据消息类型分别处理//更新在线名单if(type.equals("OnlineListUpdate")){//提取在线列表的数据模型DefaultTableModel tbm = (DefaultTableModel) cframe.jtbOnline.getModel();//清除在线名单列表tbm.setRowCount(0);//更新在线名单String[] onlinelist = content.split(",");//逐一添加当前在线者for(String member : onlinelist){String[] tmp = new String[2];//如果是自己则不在名单中显示if(member.equals(InetAddress.getLocalHost().getHostAddress() + ":" + s.getLocalPort()))continue;//当前在线者的IP地址tmp[0] = member.substring(0, member.indexOf(":"));//当前在线着的端口号tmp[1] = member.substring(member.indexOf(":") + 1);//添加当前在线者之一tbm.addRow(tmp);}//提取在线列表的渲染模型DefaultTableCellRenderer tbr = new DefaultTableCellRenderer();//表格数据居中显示tbr.setHorizontalAlignment(JLabel.CENTER);cframe.jtbOnline.setDefaultRenderer(Object.class, tbr);}//聊天else if(type.equals("Chat")){String sender = content.substring(0, content.indexOf("/"));String word = content.substring(content.indexOf("/") + 1);//在聊天窗打印聊天信息cframe.jtaChat.append(cframe.sdf.format(new Date()) + "\n来自 " + sender + ":\n" + word + "\n\n");//显示最新消息cframe.jtaChat.setCaretPosition(cframe.jtaChat.getDocument().getLength());}}}catch(Exception e){cframe.jtaChat.append("服务器挂了.....\n");e.printStackTrace();}}
}//客户端窗口
class ClientFrame extends JFrame
{//时间显示格式SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");//窗口宽度final int WIDTH = 800;//窗口高度final int HEIGHT = 580;//创建发送按钮JButton btnSend = new JButton("发送");//创建清除按钮JButton btnClear = new JButton("清屏");//创建退出按钮JButton btnExit = new JButton("退出");//创建消息接收者标签JLabel lblReceiver = new JLabel("发给:");//创建文本输入框, 参数分别为行数和列数JTextArea jtaSay = new JTextArea();//创建聊天消息框JTextArea jtaChat = new JTextArea();//当前在线列表的列标题String[] colTitles = {"IP", "端口"};//当前在线列表的数据String[][] rowData = null;//创建当前在线列表JTable jtbOnline = new JTable(new DefaultTableModel(rowData, colTitles){//表格不可编辑,只可显示@Overridepublic boolean isCellEditable(int row, int column){return false;}});//创建聊天消息框的滚动窗JScrollPane jspChat = new JScrollPane(jtaChat);//创建当前在线列表的滚动窗JScrollPane jspOnline = new JScrollPane(jtbOnline);//设置默认窗口属性,连接窗口组件public ClientFrame(){//标题setTitle("聊天室");//大小setSize(WIDTH, HEIGHT);//不可缩放setResizable(false);//设置布局:不适用默认布局,完全自定义setLayout(null);//设置按钮大小和位置btnSend.setBounds(20, 510, 60, 25);btnClear.setBounds(140, 510, 60, 25);btnExit.setBounds(260, 510, 60, 25);//设置标签大小和位置lblReceiver.setBounds(20, 420, 300, 30);//添加按钮this.add(btnSend);this.add(btnClear);this.add(btnExit);//添加标签this.add(lblReceiver);//设置文本输入框大小和位置jtaSay.setBounds(20, 460, 500, 40);//添加文本输入框this.add(jtaSay);//聊天消息框自动换行jtaChat.setLineWrap(true);//聊天框不可编辑,只用来显示jtaChat.setEditable(false);//设置滚动窗的水平滚动条属性:不出现jspChat.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);//设置滚动窗的垂直滚动条属性:需要时自动出现jspChat.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);//设置滚动窗大小和位置jspChat.setBounds(20, 20, 500, 400);//添加聊天窗口的滚动窗this.add(jspChat);//设置滚动窗的水平滚动条属性:不出现jspOnline.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);//设置滚动窗的垂直滚动条属性:需要时自动出现jspOnline.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);//设置当前在线列表滚动窗大小和位置jspOnline.setBounds(525, 20, 250, 400);//添加当前在线列表this.add(jspOnline);//添加发送按钮的响应事件btnSend.addActionListener(new ActionListener(){@Overridepublic void actionPerformed(ActionEvent event){//显示最新消息jtaChat.setCaretPosition(jtaChat.getDocument().getLength());try{//有收信人才发送if(Client.uidReceiver.toString().equals("") == false){//在聊天窗打印发送动作信息jtaChat.append(sdf.format(new Date()) + "\n发往 " + Client.uidReceiver.toString() + ":\n");//显示发送消息jtaChat.append(jtaSay.getText() + "\n\n");//向服务器发送聊天信息OutputStream out = Client.s.getOutputStream();out.write(("Chat/" + Client.uidReceiver.toString() + "/" + jtaSay.getText()).getBytes());}}catch(Exception e){}finally{//文本输入框清除jtaSay.setText("");}}});//添加清屏按钮的响应事件btnClear.addActionListener(new ActionListener(){@Overridepublic void actionPerformed(ActionEvent event){//聊天框清屏jtaChat.setText("");}});//添加退出按钮的响应事件btnExit.addActionListener(new ActionListener(){@Overridepublic void actionPerformed(ActionEvent event){try{//向服务器发送退出信息OutputStream out = Client.s.getOutputStream();out.write("Exit/".getBytes());//退出System.exit(0);}catch(Exception e){}}});//添加在线列表项被鼠标选中的相应事件jtbOnline.addMouseListener(new MouseListener() {@Overridepublic void mouseClicked(MouseEvent event) {//取得在线列表的数据模型DefaultTableModel tbm = (DefaultTableModel) jtbOnline.getModel();//提取鼠标选中的行作为消息目标,最少一个人,最多全体在线者接收消息int[] selectedIndex = jtbOnline.getSelectedRows();//将所有消息目标的uid拼接成一个字符串, 以逗号分隔Client.uidReceiver = new StringBuilder("");for (int i = 0; i < selectedIndex.length; i++) {Client.uidReceiver.append((String) tbm.getValueAt(selectedIndex[i], 0));Client.uidReceiver.append(":");Client.uidReceiver.append((String) tbm.getValueAt(selectedIndex[i], 1));if (i != selectedIndex.length - 1)Client.uidReceiver.append(",");}lblReceiver.setText("发给:" + Client.uidReceiver.toString());}@Overridepublic void mousePressed(MouseEvent event) {};@Overridepublic void mouseReleased(MouseEvent event) {};@Overridepublic void mouseEntered(MouseEvent event) {};@Overridepublic void mouseExited(MouseEvent event) {};});}
}

源码:github源码链接:)

java版mini聊天室相关推荐

  1. java多人聊天室与网络画板

    本篇主要是对本人前一篇通信博客在部分功能和代码结构上的优化,以下为前篇博客的地址: java通信-网络聊天室&网络画板(简陋版) 一.代码结构上的优化: 对前篇博客服务器类中最后流的关闭上代码 ...

  2. 基于Java+Swing实现聊天室

    基于Java+Swing实现聊天室 一.系统介绍 二.功能展示 三.其它 1.其他系统实现 四.获取源码 一.系统介绍 Java聊天室系统主要用于实现在线聊天,基本功能包括:服务端和客户端.本系统结构 ...

  3. java 简单的聊天室_Java简单聊天室

    实现Java简单的聊天室 所用主要知识:多线程+网络编程 效果如下图 /** * * @author Administrator * *简单的多人聊天系统--重点:同时性,异步性 *1.客户端:发送消 ...

  4. java网络编程-聊天室

    目录 V01 # 聊天室客户端(V1) # 聊天室服务端(V1) V02 # 聊天室客户端(V2) # 聊天室服务端(V2) V03 V04 # 聊天室客户端(V4) # 聊天室服务端(V4) V05 ...

  5. QQ版网络聊天室完整项目+MFC\C++\C(更改服务器IP可实现异机沟通)

    QQ版网络聊天室完整项目+MFC\C++\C 资源地址1 资源地址2 项目简介 服务器端部分代码 客户端响应部分代码 数据库连接 理论~ 实例展示 资源地址1 https://github.com/M ...

  6. java 实现wobsocket 聊天室

    文章目录 一.java 实现wobsocket 聊天室 二.使用步骤 1.pom.xml 引入jar包 2.java 代码(WsServer.java) 3.web 页面代码(room.html) 一 ...

  7. Java 多人聊天室(带界面)

    Java 多人聊天室 先说,记录本人的学习过程,当笔记了 多人聊天室分为 1.服务器 ①.while循环 (guanbo) 每连接一个用户就记录一个用户的属性. ②.定义一个存储用户的类( MySoc ...

  8. java通信—网络聊天室网络画板(简陋版)

    一.大体结构: 采用TCP/IP模式的Socket(套接字)通信,实现通过服务端作中转的客户端与客户端之间的通信. 二.当前功能: 实现简单的客户机之间信息的收发通信: 通过自定义byte数组与int ...

  9. Java Dome(实现一个简易版QQ聊天室)

    目录 前言 流程图示 Dome结构预览 Message类 服务端用户登录与查询与消息转发 服务端代码 客户端登录 客户端消息接收与发送 注意点 客户端代码 运行结果示意 前言 这玩意就是一个基于TCP ...

  10. java开发websocket聊天室_java实现基于websocket的聊天室

    [实例简介] java实现基于websocket的聊天室 [实例截图] [核心代码] chatMavenWebapp └── chat Maven Webapp ├── pom.xml ├── src ...

最新文章

  1. Sublime 2 配置
  2. 文献记录(part59)--多任务学习
  3. 深度相机 物体三维重建_基于深度相机进行室内完整场景三维重建的方法及系统_2017100513665_说明书_专利查询_专利网_钻瓜专利网...
  4. Python学习(五)列表的简单操作
  5. Java中的继承性特性
  6. Python入门基础篇(二)元组,更适合新手哦!!!
  7. 水晶报表如何查看sql_amazon自动广告如何查看报表?怎么知道亚马逊的自然流量和广告流量分别是多少?...
  8. 【FFMPEG系列】windows下编译ffmpeg且加入libx264
  9. 2018-06-13 第三十八天
  10. js中(function(){…})()立即执行函数写法理解
  11. Strtus2入门简单框架搭建
  12. 依赖注入原理,作用,注入方式——Spring IOC/DI(二)
  13. 关于未来IOT和移动APP集成的五个预言
  14. python 3.8.2安装教程
  15. python股票量化交易(4)---金叉与死叉
  16. CATIA二次开发-装配技术
  17. 竞争10nm产品市场 联发科新增Helio X35
  18. 将1自动补位为01_自动补位为辅助后游戏就输了一半?那是你不懂辅助的正确打开方式...
  19. mysql 查看版本的几种方法
  20. Javascript笔记大全01,会持续更新~

热门文章

  1. 如何让阿三 Windows 10、11 的恢复分区(Recovery Partition)恢复到 “盖茨” 模式
  2. 【方差分析】之matlab求解
  3. element cannot be mapped to a null key
  4. Python打包exe文件并换图标【最强版教程】
  5. stm32f103测脉冲数用于带霍尔编码器的电机测速
  6. python倒计时弹框提示带注释_注意时间用python制作倒计时提醒工具
  7. java 双三次线性插值_双线性及双三次插值
  8. 西电网络攻防大赛--渗透测试第五题
  9. 京东app优惠券python抓取_备战双十一,scrapy框架爬取京东优惠券信息
  10. 小学计算机说课稿模板,获奖小学信息技术说课稿模板