这个项目是我大一的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 {
//实例化一个Socket
socket = 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