java聊天室群聊及私聊实现!
一:业务逻辑
连接数据库,实现注册登录账号的功能
使用serverSocket接收并发送消息实现服务器功能
客户端使用socket与服务器交互
二:类设计及代码结构
MyMessage类:该类主要用于封装发送的消息内容
ServerSocketTest类:服务器段实现类
signup类:通过数据库实现登录注册
SocketFrame:聊天主界面类
三:代码
Mymessage类代码:
package objtalk;
import java.io.Serializable;
import java.util.ArrayList;
import javax.swing.text.StyledDocument;
/*
* 该消息类分为两种
* 一种是实际消息内容
* 还有一种是当前群聊的成员信息
* */
public class MyMessage implements Serializable {// 序列化&&反序列化(用于被传输的对象)
public static final long serialVersionUID = 1l;
public static final int MES_TYPE_PLAIN = 1;//文本消息
public static final int MES_TYPE_UPDATE_CLIENTLIST = 2;//更新用户列表消息
private StyledDocument content ;//非文本消息(例如图片)
private ArrayList<String> clientList;//当前群聊成员信息
private int mesType = -1;
private boolean ifmass=true;//判断是群聊消息还是私聊信息
private String ip="null";//私发message对象ip
private String usename = "null";
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public MyMessage(Boolean ifmass,int mesType) {
this.ifmass=ifmass;
this.mesType = mesType;
}
public int getMesType() {
return mesType;
}
public void setMesType(int mesType) {
this.mesType = mesType;
}
public StyledDocument getContent() {
return content;
}
public void setContent(StyledDocument content) {
this.content = content;
}
public ArrayList<String> getClientList() {
return clientList;
}
public void setClientList(ArrayList<String> clientList) {
this.clientList = clientList;
}
public boolean getisIfmass() {
return ifmass;
}
public void setIfmass(boolean ifmass) {
this.ifmass = ifmass;
}
}
signup类代码:
package objtalk;
/*创建一张talk数据表
* 表结构为usename和password
* */
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.Statement;
public class signup extends JFrame{
private JTextField usename = new JTextField();//文本输入框
private JTextField password = new JTextField();
private JButton signup = new JButton("注册");//按钮
private JButton signin = new JButton("登陆");
//数据库信息
private String user = "";
private String pwd = "";
private String url = "";//jdbc:myaql://ip或端口号/需要打开的database
private boolean tag = false;//判断账号密码是否正确
public signup() {
// TODO Auto-generated constructor stub
this.setSize(300,400);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setLayout(null);
this.add(usename);
this.add(password);
this.add(signin);
this.add(signup);
usename.setSize(100,30);
password.setSize(100,30);
signin.setSize(100,50);
signup.setSize(100,50);
usename.setLocation(100,100);
password.setLocation(100,200);
signin.setLocation(50,300);
signup.setLocation(150,300);
try {
Class.forName("com.mysql.jdbc.Driver");//开启数据库
} catch (ClassNotFoundException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
signin.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String useName = usename.getText().toString();
String passWord = password.getText().toString();
if(useName.isEmpty()||passWord.isEmpty()){
JOptionPane.showMessageDialog(null, "请输入用户名或密码", "error",JOptionPane.ERROR_MESSAGE);
}
else{
try {
Connection con = (Connection) DriverManager.getConnection(url, user, pwd);//数据库连
Statement stmt = (Statement) con.createStatement();//创建语句对象
String sql = "select * from talk";//数据库语句
ResultSet rs = (ResultSet) stmt.executeQuery(sql);//执行语句得到结果,以行的角度表现查询结果
java.sql.ResultSetMetaData rsmd = rs.getMetaData();//结果以列的形式展现
while(rs.next()){//按行逐个读取查询的内容,next()表示行的移动
if(rs.getString(1).equals(useName)&&rs.getString(2).equals(passWord)){
tag = true;
new SocketFrame().setVisible(true);//跳转到主界面
exits();//关闭当前界面
return;
}
}
if(tag==false){
JOptionPane.showMessageDialog(null, "账号密码错误!", "error",JOptionPane.ERROR_MESSAGE);
}
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
});
signup.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
String useName = usename.getText().toString();
String passWord = password.getText().toString();
if(useName.isEmpty()||passWord.isEmpty()){
JOptionPane.showMessageDialog(null, "请输入用户名或密码", "error",JOptionPane.ERROR_MESSAGE);
}
else{
Connection con;
try {
con = (Connection) DriverManager.getConnection(url, user, pwd);
Statement stmt = (Statement) con.createStatement();
String sql = "insert talk value('"+useName+"','"+passWord+"');";
stmt.executeUpdate(sql);
JOptionPane.showMessageDialog(null,"注册成功", "done",JOptionPane.ERROR_MESSAGE);
//stmt.executeQuery(sql);//执行语句得到结果,以行的角度表现查询结果
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}//数据库连接,Connection是接口不能用new
}
}
});
}
public void exits() {
this.setVisible(false);
}
public static void main(String[] args) {
new signup().setVisible(true);
}
}
SocktFrame类代码
package objtalk;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.channels.NonWritableChannelException;
import java.nio.channels.SelectableChannel;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.lang.model.element.Element;
import javax.swing.AbstractListModel;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.event.AncestorListener;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
public class SocketFrame extends JFrame {
private JTextPane jtpMes = new JTextPane();//消息框
private StyledDocument contentDoc = jtpMes.getStyledDocument();//取出文本(),属性定义(sas的容器)
private JScrollPane jspMes = new JScrollPane(jtpMes);//为消息框添加滑动框
private JButton btnSend = new JButton("Send");
private JButton btnConnect = new JButton("Connect");
private JButton btnSelectimg = new JButton("img");
private JTextPane jtpNewMes = new JTextPane();//消息框(可以显示图片和文字)
private JScrollPane jspNewMes = new JScrollPane(jtpNewMes);//为群聊框添加滑动框
private StyledDocument sendDoc = jtpNewMes.getStyledDocument();
private JPanel panSend = new JPanel();
JPanel btnPan = new JPanel();
private Font font = new Font("宋体", Font.PLAIN, 20);
private JList<String> listClient = new JList<>();
private JScrollPane jspClientList = new JScrollPane(listClient);
private Socket socket;
private ObjectOutputStream out;
private ReadThread reader;//读取消息线程
public SocketFrame() {
this.setSize(800, 600);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
init();
getContentPane().add(jspMes);
getContentPane().add(panSend, BorderLayout.SOUTH);
getContentPane().add(jspClientList, BorderLayout.EAST);
}
public void updateListClient(ArrayList list) {//跟新群聊用户信息
listClient.setModel(new ClientListModel(list));
}
class ClientListModel extends AbstractListModel {//更新list信息
ArrayList list;
public ClientListModel(ArrayList list) {
super();
this.list = list;
}
@Override
public Object getElementAt(int arg0) {
return list.get(arg0);
}
@Override
public int getSize() {
return list.size();
}
}
private void init() {
panSend.setLayout(new BorderLayout());
panSend.add(jspNewMes,BorderLayout.CENTER);
panSend.add(btnPan,BorderLayout.EAST);
btnPan.add(btnSend);
btnPan.add(btnConnect);
btnPan.add(btnSelectimg);
jtpMes.setEditable(false);
jtpMes.setFont(font);
jtpNewMes.setFont(font);
btnSend.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
String str = jtpNewMes.getText().trim();//得到文本
System.out.println(str);
if (str != null && str.length() > 0 && socket != null) {
SocketAddress address = socket.getRemoteSocketAddress();//得到本地地址
String ip = address.toString().substring(1,
address.toString().indexOf(":") + 1);//获得ip
SimpleAttributeSet sas = new SimpleAttributeSet();//容器存储消息体
StyleConstants.setFontSize(sas,24);//设置字体
try {
/*senDoc消息内容会自动从输入消息框获取(绑定更新,50行57行代码实现),这里只是在消息前面添加ip(类似用户名)*/
sendDoc.insertString(0, ip, sas);
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
MyMessage mes = new MyMessage(true,MyMessage.MES_TYPE_PLAIN);
mes.setContent(sendDoc);
sendMes(mes);//发送消息
try {
sendDoc.remove(0, sendDoc.getLength());//去除容器中的内容
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
btnConnect.addActionListener(new ActionListener() {//连接服务器
@Override
public void actionPerformed(ActionEvent arg0) {
if(socket==null){
try {
socket = new Socket("10.117.45.114", 12345);//具体ip自己设置
reader = new ReadThread(socket);
reader.start();
out = new ObjectOutputStream(socket.getOutputStream());//创建消息输入流
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
btnSelectimg.addActionListener(new ActionListener() {//选区本地图片存入容器
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
JFileChooser fc = new JFileChooser("d:");//文件选择器
FileNameExtensionFilter filter = new FileNameExtensionFilter("JPG & GIF Image", "jpg","gif");//文件筛选器
fc.setFileFilter(filter);
int i = fc.showOpenDialog(SocketFrame.this);
if(i == JFileChooser.APPROVE_OPTION){
try {
Image img = ImageIO.read(fc.getSelectedFile());
ImageIcon icon = new ImageIcon(img);
SimpleAttributeSet sas = new SimpleAttributeSet();//容器
StyleConstants.setIcon(sas, icon);//把图标放入sas容器
sendDoc.insertString(sendDoc.getLength(), "icon", sas);//把sas插入文本格式,属性定义
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (BadLocationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
});
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent arg0) {//关闭主界面后程序退出流关闭
if (out != null) {
MyMessage mes = new MyMessage(true,MyMessage.MES_TYPE_PLAIN);
//mes.setContent("quit");
sendMes(mes);
reader.stopRun();
}
}
});
listClient.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
super.mouseClicked(e);
if(e.getClickCount()==2){//双击触发私聊
new PrivateDialog(listClient.getSelectedValue().toString()).setVisible(true);
}
}
});
}
public void append(StyledDocument sd){
int caretPosition = jtpMes.getStyledDocument().getLength();
caretPosition+=sd.getLength();
try {
for(int i=0;i<sd.getLength();i++){
javax.swing.text.Element e = sd.getCharacterElement(i);
if(e.getName().equals("icon")){
contentDoc.insertString(contentDoc.getLength(), "icon", e.getAttributes());
i+=2;
}
else{
String s = sd.getText(i, 1);
contentDoc.insertString(contentDoc.getLength(), s, e.getAttributes());
}
}
contentDoc.insertString(contentDoc.getLength(), "\n", null);
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
jtpMes.setCaretPosition(caretPosition);
}
public void sendMes(MyMessage m) {
if (out != null) {
try {
out.reset();//反复发送同一个内容不断改变的对象需要使用reset(此时为sendDoc)
out.writeObject(m);
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ReadThread extends Thread {
Socket c;
boolean flag = true;
public ReadThread(Socket c) {
this.c = c;
}
@Override
public void run() {
try {
ObjectInputStream in = new ObjectInputStream((c.getInputStream()));
MyMessage newMes = (MyMessage) in.readObject();
while (flag) {
switch (newMes.getMesType()) {
case MyMessage.MES_TYPE_PLAIN:
append(newMes.getContent());//将得到的消息添加进聊天框
break;
case MyMessage.MES_TYPE_UPDATE_CLIENTLIST:
updateListClient(newMes.getClientList());//更新聊天人信息
break;
}
//将输入流和message对象初始化供下次使用
in = new ObjectInputStream((c.getInputStream()));
newMes = (MyMessage) in.readObject();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void stopRun() {
flag = false;
}
}
class PrivateDialog extends JDialog{//单独对话框
private JTextPane jtpPriMes = new JTextPane();
private JScrollPane jspPriMes = new JScrollPane(jtpPriMes);
private JButton btnPriSend = new JButton("Send");
private JButton btnselect = new JButton("select");
private JPanel panFun = new JPanel();
private String ip;
public PrivateDialog(String ip) {
// TODO Auto-generated constructor stub
this.ip = ip;
this.setTitle(ip);
this.setSize(400, 300);
this.setLocationRelativeTo(null);
init();
this.add(panFun);
}
private void init() {
panFun.add(jtpPriMes);
panFun.add(btnPriSend);
panFun.add(btnselect);
btnPriSend.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
String str = jtpPriMes.getText().trim();
if(str!=null&&str.length()>0&&socket!=null){
MyMessage mes = new MyMessage(false,MyMessage.MES_TYPE_PLAIN);
mes.setIp(ip);
mes.setContent(jtpPriMes.getStyledDocument());
sendMes(mes);
}
}
});
btnselect.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
JFileChooser fc = new JFileChooser("d:");
FileNameExtensionFilter filter = new FileNameExtensionFilter("JPG & GIF Image", "jpg","gif");//文件筛选器
fc.setFileFilter(filter);
int i = fc.showOpenDialog(SocketFrame.this);
if(i == JFileChooser.APPROVE_OPTION){
try {
Image img = ImageIO.read(fc.getSelectedFile());
ImageIcon icon = new ImageIcon(img);
SimpleAttributeSet sas = new SimpleAttributeSet();//容器
StyleConstants.setIcon(sas, icon);//把图标放入sas容器
jtpPriMes.getStyledDocument().insertString(jtpPriMes.getStyledDocument().getLength(), "icon", sas);//把sas插入文本格式,属性定义
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (BadLocationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
});
}
}
}
ServerSocketTest代码
package objtalk;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import org.omg.CORBA.SystemException;
public class ServerSocketTest {
ServerSocket server;
HashSet<Socket> clientSet = new HashSet<>();
public ServerSocketTest() {
try {
server = new ServerSocket(12345);
} catch (IOException e) {
e.printStackTrace();
}
}
public void work() {
int no = 0;//连接服务器的个数
try {
while (true) {
Socket client = server.accept();
clientSet.add(client);
SendUpdateClientList();
no++;
new ClientThread(client, no).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void SendUpdateClientList() {//发送用户变更的消息,用户退出和加入的监听
MyMessage mes = new MyMessage(true,MyMessage.MES_TYPE_UPDATE_CLIENTLIST);
mes.setClientList(getClientList());
massMes(mes);
}
public void massMes(MyMessage mes) {//群发消息
Iterator<Socket> it = clientSet.iterator();
while (it.hasNext()) {
sendMes(it.next(), mes);
}
}
public void singleMes(MyMessage mes){//单发消息
for(Socket s : clientSet){
if(s.getRemoteSocketAddress().toString().equals(mes.getIp())){//String判等必须用equals
sendMes(s, mes);
break;
}
}
}
public void sendMes(Socket s, MyMessage mes) {
ObjectOutputStream out;
try {
out = new ObjectOutputStream(s.getOutputStream());
out.writeObject(mes);
out.flush();
} catch (IOException e1) {
e1.printStackTrace();
}
}
public ArrayList<String> getClientList() {
ArrayList<String> list = null;
if (clientSet.size() > 0) {
list = new ArrayList<String>();
Iterator<Socket> it = clientSet.iterator();
int index = 0;
while (it.hasNext()) {
list.add(it.next().getRemoteSocketAddress().toString());
}
}
return list;
}
class ClientThread extends Thread {
Socket c;
int no;
public ClientThread(Socket c, int no) {
super();
this.c = c;
this.no = no;
}
@Override
public void run() {
try (ObjectInputStream in = new ObjectInputStream((c.getInputStream()));) {
MyMessage newMes = (MyMessage) in.readObject();
while (newMes.getContent()!=null) {//不断接收发来的消息
if(newMes.getisIfmass()==true){
massMes(newMes);
System.out.println(newMes.getContent().getText(0,newMes.getContent().getLength() ));
}
else{
singleMes(newMes);
}
newMes = (MyMessage) in.readObject();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
c.close();
} catch (IOException e) {
e.printStackTrace();
}
clientSet.remove(c);//用户退出后
SendUpdateClientList();
}
}
}
public static void main(String[] args) {
new ServerSocketTest().work();
}
}
四:使用方法:
开启ServerSocketTest类(打开服务器)
开启signUp类,注册,登录,连接,开始聊天(可以多开几个实现群聊)
五:项目github地址
https://github.com/Chaos1874/javaTalk
---------------------
【转载,仅作分享,侵删】
作者:Chaos1874
原文:https://blog.csdn.net/weixin_41467912/article/details/88605696
版权声明:本文为博主原创文章,转载请附上博文链接!
|
|