这个项目是我大一的java期末大作业,学习了一个学期的成果,编程能力不够,做的并不够好,写在这里记录一下自己的成长,也欢迎大家给出宝贵意见!
第一次写博客哟,请多多支持!

该篇博客写一个基于java的Socket 单/多人网络即时聊天室,网络编程,基于TCP协议实现的聊天室。

因为这个聊天室最大的特色是使用了网络编程技术,所以首先我们来了解一下Socket 。

Socket又称套接字 ,套接字使用TCP提供了两台计算机之间的通信机制。

客户端程序创建一个套接字,并尝试连接服务器的套接字。
当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行
通信。java.net.ServerSocket类为服务器提供了一种监听客户端并与他们建立连接的机制。

Socket编程在客户端与服务端的使用注意
对于客户端,需要使用new Socket(ip,port)

对于服务器端,需要借助于ServerSocket 的accept方法获得与特定的客户端相关联的Socket实例

通信的双方借助于socket里面的方法,获得输出流和输入流完成相关的通信的逻辑处理

该篇博客的目录

  • 聊天室程序运行展示图
    • 设计思路
      • 代码实现

聊天室程序运行展示图

效果图:
登陆界面:

注册界面:
好友在线列表:
聊天界面以及服务器界面:

设计思路

项目需求:

  • 登陆,注册,好友在线列表,聊天界面,服务器管理界面
  • 私聊功能
  • 群聊功能
  • 实时好友在线状态
  • 解决并发问题,使用多线程
  • 绑定数据库,用于存储用户信息
  • 使用网络编程技术,在局域网下多机互联

我将整个聊天室项目分为了三个主要的部分,分别是:客户端(Client),服务器端(Server),数据库。

数据库:数据库是布置在服务器端的,主要功能就是存储用户的信息(用户名,密码,昵称 等等)。在实际上线的项目中,数据库的作用是至关重要的。本项目中使用的数据库是MYSQL数据库(项目中绑定数据库需要使用JDBC)

JDBC实际上是一套接口,用于连接代码和数据库

客户端:客户端(Client)或称为用户端,是指与服务器相对应,为客户提供本地服务的程序,需要与服务端互相配合运行,本项目中客户端将接收用户输入的信息封装到公共类(Massage)再通过对象流(ObjectInputStream/ObjectOutputStream)发送到服务器端或从服务器端接收Massage类信息。

服务器端:服务器实际上相当于是一个信息的分析站和中转站。服务器端使用多线程和HashMap(哈希表)数据结构,接收每个客户端的信息以及每个客户端发送到服务器端的信息,通过解析后判断出客户端的行为并作出相应的操作以及将信息进行转发。通过JDBC接口与服务器本地数据库相连。

1. 一开始我是先将五个GUI界面(登陆,注册,好友在线列表,服务器管理界面)编写出来,因为这样便于后续项目后台的调试,而且看起来会更直观。

编写GUI界面需要学习java的GUI类,本文章不会讲关于GUI

**2.**写好了GUI界面后,就可以使用Socket编写一个网络传输的雏形了(将服务器端和客户端分两个项目写),我使用的是对象流(上文提到),因为为了传输数据的安全和传输的便利,我使用的“消息类”Massage,用于存放要传输的消息。

3. 接下来是登陆,注册功能,首先由登陆/注册的界面监听键盘输入,获取用户输入的信息,并由客户端后台将信息封装成Massage类并通过对象流ObjectOutStream发送给服务器端,服务器端接收Massage类后解析后判断用户的行为是登陆还是注册,再通过JDBC与数据库查询或者更改信息,返回登陆或注册结果,并回送给客户端,客户端提示是否登陆成功或注册成功。

4. 私聊以及群聊功能,与上同理,聊天界面监听键盘获取用户要发送的文字消息,通过对象流发送给服务器端,服务器端判断用户是私聊还是群聊,并将消息进行对应的转发。

5. 实时好友在线状态显示,我认为整个项目最难的一个功能。首先要在服务器端后台再创建一个HashMap,存放每个在线用户,每当有用户上线或者下线的时候都要及时遍历整个HashMap,并将实时的好友在线状态发送给每个客户端。
难点:如何做到实时获取到每个用户的在线状态?

为了让大家更容易理解更直观的看到整个项目结构

呈上自己画的类结构图
请笑纳:

代码实现

用于存放消息的公共类:

因为这是用于传输的消息类,所以一定要先进行类序列化。
Massage中有5个成员变量分别有不同的功能。

package common;import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;/*** @author * @create 2020-05-25 7:38* 用于传输的信息类*/
public class Massage implements Serializable {private static final long serialVersionUID = 8475898535881670782L;private String massageType ;    //信息类型private String massage;         //信息private String owner;           //发送者private String friend;          //接收者private String date;            //当前时间public String getMassageType() {return massageType;}public void setMassageType(String massageType) {this.massageType = massageType;}public String getMassage() {return massage;}public void setMassage(String massage) {this.massage = massage;}public String getOwner() {return owner;}public void setOwner(String owner) {this.owner = owner;}public String getFriend() {return friend;}public void setFriend(String friend) {this.friend = friend;}public String getDate(){return date;}public void setDate(Date d){//对时间格式化SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");date = sdf.format(d);}
}

客户端登陆后台:

port:8787
ObjectOutputStream 发送Massage给服务器,服务器回送登陆结果,若 if (massage.getMassageType().equals(MassageType.MASSAGE_COMM))
的结果为true,则登陆成功进入好友在线列表界面并开启一个新的线程,否者提示登陆失败!

package webclient.model;import common.Massage;
import common.MassageType;
import common.User;
import webclient.view.WebChatFriendlist;import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Date;/*** @author * @create 2020-05-25 7:44* 登录后台,发送用户信息验证合法性*/
public class CilentConnServer {public   Socket socket;/***将信息发送到服务器验证用户的合法性*@return*/public boolean CheckUser(Object u) {boolean b=false;    //布尔类型的变量用于表明用户是否合法try {//实例化一个Socketsocket = new Socket("127.0.0.1", 8787);//实例化对象输出流ObjectOutputStream oos  = new ObjectOutputStream(socket.getOutputStream());User user = (User)u;//设置信息类型user.getMassage().setMassageType(MassageType.MASSAGE_COMM);//设置时间user.getMassage().setDate(new Date());Object o = (User) user;//将对象发送到服务器oos.writeObject(o);System.out.println("user已发送"+user);//实例化对象输入流等待服务器回送信息ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());System.out.println("massage:");//读取服务器回送的对象信息Massage massage = (Massage)ois.readObject();//验证用户的登录if (massage.getMassageType().equals(MassageType.MASSAGE_COMM)) {System.out.println("登录成功!");//创建一个该用户与客户端保持的线程ClientConnServerThread ccst = new ClientConnServerThread(user.getName(),socket);ManagerSocket.addSocket(user.getName(),socket);//将线程放入管理线程的类中ManegerClientConnServer.addClientConnServerThread(user.getName(), ccst);//发送好友信息请求包//线程启动ccst.start();b = true;} else {System.out.println("登录失败");return  b;}} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return  true;}}

客户端与服务器连接的线程类:
ClientConnServerThread 继承Thread
重写了run()可以接收服务器端发送的信息,并发送聊天消息,以及对好友在线列表进行更新

当没有发送也没有接收的时候线程会因为accpet()进入阻塞状态。
WebChatFriendlist中存放每个好友的实时在线状态,使用的是HashMap

package webclient.model;import common.Massage;
import common.MassageType;
import webclient.view.CilentChatJframe;
import webclient.view.WebChatFriendlist;import javax.swing.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.util.Iterator;/*** @author * @create 2020-05-26 22:36* 这是客户端和服务器端获取链接的线程*/
public class ClientConnServerThread extends Thread {private Socket s;private String username;public ClientConnServerThread(String username,Socket s) {this.username = username;this.s = s;}public Socket getSocket() {return s;}@Overridepublic void run() {while (true) {//不停的读取从服务器发来的消息while (true) {try {//实例化对象输入流ObjectInputStream ois = new ObjectInputStream(s.getInputStream());Massage m = (Massage) ois.readObject();System.out.println(m.getMassage());//根据服务器回送的信息进行对应的操作if (m.getMassageType().equals(MassageType.MASSAGE_COMM_AVG)) {String mm = m.getOwner() + ":" + m.getMassage() + "  [" + m.getDate() + "]";//通过socket获取用户的聊天界面对象if (ManagerChatJFrame.hashMap.containsKey(s)) {CilentChatJframe cjf = ManagerChatJFrame.getCilentChatJframe(s);if (null != cjf) {//将信息显示到对应的界面cjf.jTextArea_up.append(mm + "\r\n");}}}else if(m.getMassageType().equals(MassageType.MASSAGE_FRIEND)){String fff = m.getFriend();String[] split = fff.split("&");for (int i = 0; i < split.length; i++) {String substring = split[i].substring(0);if (substring.length()!=0) {split[i] = substring;}}if (!ManagerFirendList.hashMap.containsKey(s)){//创建好友列表界面WebChatFriendlist wc = new WebChatFriendlist(username,split);//将界面存放到hashmap中ManagerFirendList.addWebChatFriendlist(s,wc);}//修改相应的好友列表//将实时的好友在线情况更新到好友界面上Iterator<Socket> it = ManagerFirendList.hashMap.keySet().iterator();String updateuser  = updateuser = m.getMassage();while (it.hasNext()){WebChatFriendlist webChatFriendlist = ManagerFirendList.getWebChatFriendlist(it.next());//调用方法进行更新webChatFriendlist.updateFriend(updateuser);//将实时的在线信息存入ManagerFrientUpdate}ManagerFrientUpdate.updateuser = updateuser;//   for (int i = 0; i < split.length; i++) {ManagerFrientUpdate.updateusers = split;//   }}} catch (IOException e) {try {s.close();} catch (IOException ex) {ex.printStackTrace();}JOptionPane.showMessageDialog(null,"连接已中断!");System.exit(0);e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}}}
}

服务器端后台:

        ServerSocket serverSocket = new ServerSocket(8787);实例化ServerSocket并创建端口与客户端取得联系

通过 if (new ChackCorrect(username,userpwd).ifright())与数据库对接判断用户合法性,并回送给客户端
后台主要处理和转发信息,代码中有注释!

package webserver.model;import common.Massage;
import common.MassageType;
import common.User;
import common.UserLogup;
import webserver.view.ServerBuildFrame;import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;/*** @author * @create 2020-05-25 10:15* 服务器端*/
public class WebServer {Object o;ServerBuildFrame serverBuildFrame;AloneThreadConnClient thread;public WebServer(ServerBuildFrame serverBuildFrame) {this.serverBuildFrame = serverBuildFrame;try {//实例化ServerSocket并创建端口ServerSocket serverSocket = new ServerSocket(8787);//将服务器信息显示到服务器面板serverBuildFrame.txtIP.setText("127.0.0.1");serverBuildFrame.txtServerName.setText("WebChat");serverBuildFrame.txtPort.setText(String.valueOf(serverSocket.getLocalPort()));while (true){System.out.println("服务器监听中···········");//获取socketSocket s = serverSocket.accept();//读取输入流对象ObjectInputStream ois = new ObjectInputStream(s.getInputStream());o = ois.readObject();System.out.println("服务器接收到object");if (o instanceof User){User u = (User) o;Massage ma = new Massage();String username = u.getName();String userpwd =u.getPassword();if (new ChackCorrect(username,userpwd).ifright()) {if (!ThreadInToHashMap.hashMap.containsKey(username)&&u.getMassage().getMassageType().equals(MassageType.MASSAGE_COMM)) {ma.setMassageType(MassageType.MASSAGE_COMM);//登陆成功,创建一个线程单独与此客户端保持联系thread = new AloneThreadConnClient(s);//将线程放入hashmapThreadInToHashMap.addClientThread(u.getName(), thread);serverBuildFrame.txtNumber.setText(ThreadInToHashMap.hashMap.size() + "人");serverBuildFrame.taLog.append(username + "已上线" + "  [" + u.getMassage().getDate() + "]" + "\r\n");thread.start();}else{//设置登陆失败的信息ma.setMassageType(MassageType.MASSAGE_LOGIN_FAIL);ma.setOwner("server");ma.setFriend(username);ma.setMassage(MassageType.MASSAGE_LOGIN_ISFAIL);ma.setDate(new Date());serverBuildFrame.taLog.append("用户'"+u.getName()+"'注册失败!  ["+ma.getDate()+"]。");}} else {//设置登陆失败的信息ma.setMassageType(MassageType.MASSAGE_LOGIN_FAIL);ma.setOwner("server");ma.setFriend(username);ma.setMassage(MassageType.MASSAGE_LOGIN_ISFAIL);ma.setDate(new Date());serverBuildFrame.taLog.append("用户'"+u.getName()+"'注册失败!  ["+ma.getDate()+"]。");}ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());oos.writeObject(ma);}/***判断客户端的行为*@return*/if (o instanceof UserLogup){UserLogup u = (UserLogup)o;Massage ma = new Massage();//如果读取到的对象是UserLogup,对UserLogup的信息在数据库进行注册if ( new ToMysqlLogup().toMysqlLogup(u)){//注册成功后,回送一条信息给客户端ma.setMassageType(MassageType.MASSAGE_LOGIN_SUCCESS);Massage massage = new Massage();massage.setDate(new Date());//将注册消息打印到服务器日志serverBuildFrame.taLog.append("用户'"+u.getName()+"'注册成功!  ["+massage.getDate()+"]。");}else{ma.setMassageType(MassageType.MASSAGE_LOGIN_ISFAIL);}ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());oos.writeObject(ma);}}} catch (Exception e) {e.printStackTrace();}}public static void main(String[] args) {new WebServer(new ServerBuildFrame());}
}
服务器与客户端联系的线程package webserver.model;import common.Massage;
import common.MassageType;
import webserver.view.ServerBuildFrame;import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Date;
import java.util.Iterator;/*** @author * @create 2020-05-26 11:31* 客户端与服务器启用的线程*/
public class AloneThreadConnClient extends Thread{public Socket s ;public  String friend = "";public  String getFriend() {return this.friend;}public AloneThreadConnClient(Socket s) {this.s = s;}@Overridepublic void run() {//notifyOther();while (true) {try {ObjectInputStream ois = new ObjectInputStream(s.getInputStream());Massage ma = (Massage) ois.readObject();friend = ma.getFriend();//取得接收人的通讯线程System.out.println("我是:"+ma.getOwner()+":聊天对象:"+friend);//判断用户的聊天对象和消息类型if ("多人聊天室".equals(ma.getFriend())&&ma.getMassageType().equals(MassageType.MASSAGE_COMM_AVG)){/***通过迭代器迭代在线用户获取用户账号并转发用户发送的消息*@return*/Iterator<String> iterator = ThreadInToHashMap.hashMap.keySet().iterator();String speak = "";while (iterator.hasNext()){//获取用户的账号String sn = iterator.next().toString();//获取对应的用户通讯线程AloneThreadConnClient clientThreas = ThreadInToHashMap.getClientThreas(sn);//获取对应用户的聊天对象String fq = clientThreas.getFriend();//System.out.println(sn+"_在线上,在和_"+fq+"聊天______________");if ((!(sn.equals(ma.getOwner())))&&("多人聊天室".equals(fq))){//如果用户在多人聊天室,则将消息转发给此用户AloneThreadConnClient thread = ThreadInToHashMap.getClientThreas(sn);ObjectOutputStream oos = new ObjectOutputStream(thread.s.getOutputStream());ma.setMassageType(MassageType.MASSAGE_COMM_AVG);oos.writeObject(ma);}speak = "多人聊天室:"+ma.getMassage()+"  ["+ma.getDate()+"]";}ServerBuildFrame.serverJframe.taLog.append(speak+"\r\n");}else//判断消息类型为普通消息if (ma.getMassageType().equals(MassageType.MASSAGE_COMM_AVG)){String mass = ma.getOwner() + "正在对" + ma.getFriend() + "说" + ma.getMassage()+"  ["+ma.getDate()+"]";//消息打印到服务器面板上ServerBuildFrame.serverJframe.taLog.append("用户:"+mass+"\r\n");if (ma.getMassageType().equals(MassageType.MASSAGE_COMM_AVG)&&"我上线啦!!!".equals(ma.getMassage())){if (ThreadInToHashMap.hashMap.containsKey(ma.getFriend())) {AloneThreadConnClient atc = ThreadInToHashMap.getClientThreas(ma.getFriend());ObjectOutputStream ooss = new ObjectOutputStream(atc.s.getOutputStream());ma.setFriend("你");ooss.writeObject(ma);}}else{//判断消息的对象是否在线,在线则发送不在则提示if (ThreadInToHashMap.hashMap.containsKey(ma.getFriend())) {AloneThreadConnClient atc = ThreadInToHashMap.getClientThreas(ma.getFriend());ObjectOutputStream ooss = new ObjectOutputStream(atc.s.getOutputStream());ooss.writeObject(ma);}else{ObjectOutputStream oosss = new ObjectOutputStream(s.getOutputStream());Massage mm = new Massage();mm.setOwner("系统消息");mm.setMassage("对方暂时不在线。");mm.setDate(new Date());mm.setMassageType(MassageType.MASSAGE_COMM_AVG);oosss.writeObject(mm);}}}else if (ma.getMassageType().equals(MassageType.MASSAGE_ON_LINE)){SelectMysqlfriends smf = new SelectMysqlfriends();String friends = smf.getfriends();
//                    //判断消息类型为请求好友在线列表String oo = ThreadInToHashMap.getOlineFriend();//获取当前的好友在线列表System.out.println("在线:"+oo);Massage massage = new Massage();massage.setDate(new Date());massage.setOwner("server");massage.setMassageType(MassageType.MASSAGE_FRIEND);if ("null".equals(oo)){oo = "@";}//为所有在线用户回送消息massage.setMassage(oo);massage.setFriend(friends);Iterator<String> iterator = ThreadInToHashMap.hashMap.keySet().iterator();while (iterator.hasNext()){String sd = iterator.next().toString();AloneThreadConnClient thread = ThreadInToHashMap.getClientThreas(sd);ObjectOutputStream oos = new ObjectOutputStream(thread.s.getOutputStream());oos.writeObject(massage);}}} catch (IOException e) {try {s.close();} catch (IOException ex) {ex.printStackTrace();}e.printStackTrace();try {s.close();return;} catch (IOException ex) {ex.printStackTrace();}} catch (ClassNotFoundException e) {e.printStackTrace();}}}
}

代码量太大,在这就不一 一详细说明了

有需要的可以去看源代码,已经上传到GitHub。

GutHub地址:
客户端:https://github.com/mygitzzw/MychatroomClient
服务器端:https://github.com/mygitzzw/MychatroomServer

如果你还有其他的看法或者想法,欢迎在评论区留下你的意见,大家一起讨论,一起进步!!!

java大作业网络聊天室相关推荐

  1. 基于Java socket的网络聊天室的设计与实现

    目 录 摘要 I Abstract II 目 录 III 1 引言 1 2 网络聊天室的简介 2 2.1网络聊天室现状和发展 2 3 完成网络聊天室的技术以及环境 4 3.1 Java的介绍 4 3. ...

  2. Java WebSocket实现网络聊天室(群聊+私聊)

    WebChat聊天室 2018.02.26 源码地址早就贴了呀, 留邮箱不如自己下载 项目地址: https://github.com/Amayadream/WebChat 2017.01.11更新 ...

  3. 华清远见Qt作业网络聊天室1014

    客户端: #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent) : ...

  4. JAVA大作业——网络在线对战游戏——坦克大战

    目录 大作业要求 实机演示 主机环回地址布置连接演示 多人联机对战演示 WASD控制坦克移动和按J键发射炮弹攻击 攻击到敌人后会爆炸并且消灭敌人 按下C键设置IP主机连接 大作业要求 简单的小游戏 要 ...

  5. 计算机html大作业聊天室,java大作业设计报告-JAVA聊天室.docx

    PAGE \* MERGEFORMAT18 JAVA程序设计 多人聊天室 设计目的 Java 编程语言是个简单.面向对象.分布式.解释性.健壮.安全与系统无关.可移植.高性能.多线程和静态的语言.本次 ...

  6. 网络聊天室(Java)

    摘要 本文阐述了基于Linux环境,Java语言实现的基本聊天室功能,涉及Linux下的Java 语言的Socket编程.以及Java语言的多线程编程. 关键字 Linux         Java  ...

  7. Java网络聊天室---个人博客

    Java网络聊天室 ---个人博客 一.项目简介 功能描述: 使用图形用户界面和socket通信,能实现一个聊天室中多人聊天,可以两人私聊,可以发送文件. 实现类似QQ用户注册.登录.聊天等功能. 参 ...

  8. 视频教程-网络聊天室Java基础版(Socket_Swing编程)仿QQ聊天-Java

    网络聊天室Java基础版(Socket_Swing编程)仿QQ聊天 IT行业资深从业者,7年资深Java高级开发,Java架构师.曾就职银行.电信等行业多家上市公司.担任项目负责人,软件架构师.有丰富 ...

  9. 视频教程-Java基础中国象棋和网络聊天室Swing开发-Java

    Java基础中国象棋和网络聊天室Swing开发 IT行业资深从业者,7年资深Java高级开发,Java架构师.曾就职银行.电信等行业多家上市公司.担任项目负责人,软件架构师.有丰富的高并发.分布式系统 ...

最新文章

  1. rxjs of操作符里subscribeToArray的实现原理示意图及分析
  2. Oracle Enterprises Manager 12C安装
  3. c语言中系统函数设定像素,在imag中对每个像素应用自定义函数的快速方法
  4. SDK Manager配置
  5. linux、mysql、nginx、tomcat 环境下压力测试的主要调试参数
  6. 为何程序员都用茶轴_程序员都用抢票神器,而你还在助力抢票吗?
  7. Python 以正弦信号绘制信号
  8. Locust1.x 的监控平台——boomer
  9. 数据分析:OLS回归分析
  10. HTML5之Worker用法
  11. ElasticSearch(一)8.4.3版下载安装
  12. 《雍正皇帝》文化专有词泰译研究(选题缘由)
  13. Verilog中parameter使用
  14. UE4-4.26蓝图功能实现:按键升降电梯
  15. 潘多拉开发板——emwin5.44裸机移植记录(ST7789驱动)
  16. 春节后面试别人的经历总结之一,好岗位分享给还在找工作中的软件开发爱好者们...
  17. 2022-10-2 塘朗山--梅林水库一游
  18. 我是深圳南山的集体户口 要将我老婆的户口随迁入深圳 没有房产 可以办深圳人才中心的集体户口吗
  19. 《乔布斯传》圈点(8)
  20. 长痘的原因及治疗方法

热门文章

  1. Web前端基础体验学习过程1 HTML篇
  2. Python数据挖掘-OneR算法简介
  3. 文件上传漏洞---Web渗透学习
  4. javax.faces.FacesException: Unable to find CDI BeanManager的解决方法
  5. 手速最快的是电竞选手?错了,是程序员
  6. 年度征文 | 回顾2022,展望2023(我难忘的2022,我憧憬的2023)
  7. http://www.cvvision.cn/2888.html
  8. VBA基础函数:取数组最大下标——UBound函数
  9. AGI (Analytical Graphics Inc.)
  10. tcl语言读取文件一行_tcl读取数据输入输出