java聊天室群聊及私聊实现!

java聊天室群聊及私聊实现!

一:业务逻辑

  1. 连接数据库,实现注册登录账号的功能
  2. 使用serverSocket接收并发送消息实现服务器功能
  3. 客户端使用socket与服务器交互

二:类设计及代码结构

  1. MyMessage类:该类主要用于封装发送的消息内容
  2. ServerSocketTest类:服务器段实现类
  3. signup类:通过数据库实现登录注册
  4. 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();
	}

}

四:使用方法:

  1. 开启ServerSocketTest类(打开服务器)
  2. 开启signUp类,注册,登录,连接,开始聊天(可以多开几个实现群聊)
    五:项目github地址
    https://github.com/Chaos1874/javaTalk
  • 8
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值