JAVA简易聊天室2.0版本(2)

声明:本文参考多篇java聊天室设计的文章。

java简易聊天室制作要求

1. 具有简易的GUI界面

2. 能实现一个聊天室中多人聊天

3. 可以两人私聊

提示:使用socket通信

1.0版本只考虑到了多人聊天室的功能,升级版本则考虑加入私聊功能和展现在线用户的页面。

  1. 所应用的原理:TCP协议--Socket通信

基于TCP的聊天室,支持多个用户同时登陆服务器进行聊天。(相当于群)

Socket编程,做一对多的多线程,必然要用到多线程,保证多个客户端(并行)登陆服务器时同时进行聊天。

TCP协议:用户传输协议、可靠连接、三次握手四次分手。

socket提供一组函数接口,套接字(socket)是一个抽象层,应用程序可以通过它发送或接受数据,可对其进行像对文件一样打开、读写和关闭等操作。网络套接字是IP地址与端口的组合。套接字Socket=(IP地址:端口号)

TCP通信步骤:

服务器端:

  • 创建socket套接字;
  • 确定端口号;
  • 绑定socket套接字和网络通信地址;
  • 设置监听listen();
  • 等待客户端连接;
  • 连接后,一个客户端对应一个线程。

客户端:

  • 创建socket套接字;
  • 准备网络通信地址(服务器的IP和端口);
  • 连接服务器。

二话不说,先上代码。

Client.java

package client;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.StringTokenizer;

import javax.swing.ButtonGroup;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;


import server.User;

/**
 * @功能描述:多人聊天室客户端
 * @创建作者:炸奶糕
 * @创建日期:2021年12月22日上午10:53:58
 */

public class Client {
	private JFrame frame;
	private JTextArea textArea;

	private JTextField textField;

	private JTextField txt_port;
	private JTextField txt_hostIp;
	private JTextField txt_name;

	private JButton start;//启动按钮
	private JButton stop;//停止按钮
	private JButton send;//发送按钮

	private JRadioButton groupchat;//群聊按钮
	private JRadioButton privatechat;//私聊按钮
	private ButtonGroup   buttongroup;//按钮组
	private JPanel buttonPanel;
	private JPanel northPanel;
	private JPanel southPanel;

	private JScrollPane rightScroll;//右滚动窗口
	private JScrollPane leftScroll;//左滚动窗格
	private JSplitPane centerSplit;//分割面板


	private JList<String> userList;//用户列表可多行显示
	private DefaultListModel<String> listModel;//用一个特别的类构造JList,对这个类的内容添加或减少,JList可以很快做出响应

	private boolean isConnected = false;//客户端连接上服务器的判断符号

	private Socket socket;
	private PrintWriter writer;//写入
	private BufferedReader reader;//读入
	private MessageThread messageThread;// 负责接收消息的线程
	private Map<String, User> onLineUsers = new HashMap<String, User>();// 所有在线用户,可以和他们私聊


	//主方法
	public static void main(String[] args) {
		new Client();
	}
	//构造方法
	public Client(){
		//初始化客户端窗口
		textArea = new JTextArea();
		textArea.setEditable(false);
		textField = new JTextField();
		txt_port = new JTextField("12345");//端口号
		txt_hostIp = new JTextField("127.0.0.1");//IP地址
		Random rand = new Random();

		
		txt_name = new JTextField("用户" + rand.nextInt(100));//默认昵称为:“用户+100内随机数”。
		start = new JButton("登录");
		stop = new JButton("注销");
		send = new JButton("发送");
		listModel = new DefaultListModel<String>();
		userList = new JList<String>(listModel);
		
		//添加北部容器的内容
		northPanel = new JPanel();
		northPanel.setLayout(new GridLayout(1, 7));
		northPanel.add(new JLabel("端口"));
		northPanel.add(txt_port);
		northPanel.add(new JLabel("服务器IP"));
		northPanel.add(txt_hostIp);
		northPanel.add(new JLabel("昵称"));
		northPanel.add(txt_name);
		northPanel.add(start);
		northPanel.add(stop);
		northPanel.setBorder(new TitledBorder("连接信息"));
		
		//定义滚动窗口的内容(右:消息显示区;左:在线用户列表)
		rightScroll = new JScrollPane(textArea);
		rightScroll.setBorder(new TitledBorder("消息显示区"));
		leftScroll = new JScrollPane(userList);
		leftScroll.setBorder(new TitledBorder("在线用户"));
		
		//禁止textArea里用光标输入文字,文字显示区域不能编辑
		textArea.setEditable(false);
		
		//定义群聊or私聊二选一选项按钮
		groupchat=new JRadioButton("群聊");
		privatechat=new JRadioButton("私聊");
		
		//将按钮归纳为按钮组并将其容器定为流布局
		privatechat.setSelected(true);
		buttongroup=new ButtonGroup();
		buttongroup.add(groupchat);
		buttongroup.add(privatechat);
		buttonPanel=new JPanel();
		buttonPanel.setLayout(new FlowLayout());
		buttonPanel.add(groupchat);
		buttonPanel.add(privatechat);
		
		//添加组件至南部容器
		southPanel = new JPanel(new BorderLayout());
		southPanel.add(buttonPanel, "North");
		southPanel.add(textField, "Center");
		southPanel.add(send, "East");
		southPanel.setBorder(new TitledBorder("写消息"));
		

		//左右分隔栏
		centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftScroll,rightScroll);
		centerSplit.setDividerLocation(100);
		
		//用JFrame的子类frame定义布局大小及位置
		frame = new JFrame("客户端");
		
		frame.setLayout(new BorderLayout());
		
		frame.add(northPanel, "North");
		frame.add(centerSplit, "Center");
		frame.add(southPanel, "South");
		frame.setSize(600, 400);
		frame.setVisible(true);

		 
				// 单击 发送 按钮事件
				send.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						send();
					}
				});
		 
				// 单击 连接 按钮事件
				start.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						int port;
						if (isConnected) {
							JOptionPane.showMessageDialog(frame, "已处于连接上状态,不要重复连接!",
									"错误", JOptionPane.ERROR_MESSAGE);
							return;
						}
						try {
							try {
								port = Integer.parseInt(txt_port.getText().trim());
							} catch (NumberFormatException e2) {
								throw new Exception("端口号不符合要求!端口为整数!");
							}
							String hostIp = txt_hostIp.getText().trim();
							String name = txt_name.getText().trim();
							if (name.equals("") || hostIp.equals("")) {
								throw new Exception("昵称、服务器IP不能为空!");
							}
							boolean flag = connectServer(port, hostIp, name);					
							if (flag == false) {
								throw new Exception("与服务器连接失败!");
							}
							frame.setTitle(name);
							JOptionPane.showMessageDialog(frame, "成功连接!");
						} catch (Exception exc) {
							JOptionPane.showMessageDialog(frame, exc.getMessage(),
									"错误", JOptionPane.ERROR_MESSAGE);
						}
					}
				});
				
				// 单击 断开 按钮时事件
				stop.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						if (!isConnected) {
							JOptionPane.showMessageDialog(frame, "已处于断开状态,不要重复断开!",
									"错误", JOptionPane.ERROR_MESSAGE);
							return;
						}
						try {
							boolean flag = closeConnection();// 断开连接
							if (flag == false) {
								throw new Exception("断开连接发生异常!");
							}
							JOptionPane.showMessageDialog(frame, "成功断开!");
						} catch (Exception exc) {
							JOptionPane.showMessageDialog(frame, exc.getMessage(),
									"错误", JOptionPane.ERROR_MESSAGE);
						}
					}
				});
				
				// 关闭 窗口时 事件
				frame.addWindowListener(new WindowAdapter() {
					public void windowClosing(WindowEvent e) {
						if (isConnected) {
							closeConnection();// 关闭与服务器的连接
						}
						System.exit(0);// 退出程序
					}
				});
				

			}

		// 执行 发送
		public void send() {
			if (!isConnected) {
				JOptionPane.showMessageDialog(frame, "还没有连接服务器,无法发送消息!", "错误",
						JOptionPane.ERROR_MESSAGE);
				return;
			}
			String message=textField.getText().trim();
			String selectUser="所有人";
				
			if (message == null || message.equals("")) {
				JOptionPane.showMessageDialog(frame, "消息不能为空!", "错误",
					JOptionPane.ERROR_MESSAGE);
			}
			//如果点击群聊按钮
			if(groupchat.isSelected()){

				sendMessage("TOALL@"+frame.getTitle()+"@"+selectUser+"@"+message);
				
			//	System.out.println("群聊发送");
				textField.setText("");
			}
			//如果点击私聊按钮
			if(privatechat.isSelected()){
				//选择私聊用户
				selectUser=(String)userList.getSelectedValue();
				//必须点击一个在线用户才能进行私聊,因此此处进行判断
				if(selectUser==null){
					JOptionPane.showMessageDialog(frame, "请选择想私聊的用户!");
					return;
				}
				sendMessage("TOONE@"+frame.getTitle()+"@"+selectUser+"@"+message);
				String t="我@"+selectUser+"说:"+message+"\r\n";
				
				textArea.append(t);
				//textArea.setForeground(Color.BLUE);
				textField.setText("");
				
		
			}

		}	
		//此处连接服务器
		public boolean connectServer(int port, String hostIp, String name) {
			// 连接服务器
			try {
				socket = new Socket(hostIp, port);// 根据端口号和服务器IP建立连接(Socket通信)
				writer = new PrintWriter(socket.getOutputStream());
				reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				// 发送客户端用户基本信息至服务器(用户名和IP地址)
				sendMessage(name + "@" + socket.getLocalAddress().toString());
				// 开启接收消息的线程
				messageThread = new MessageThread(reader, textArea);
				messageThread.start();
				isConnected = true;// 已经连接上了,从而点击连接按钮事件时能根据此处进行判断
				return true;
				
			}catch (Exception e) {
				textArea.append("与端口号为:" + port + "    IP地址为:" + hostIp
						+ "   的服务器连接失败!" + "\r\n");
				isConnected = false;//与服务器连接失败
				return false;
			}
		}
		

		//发送消息的方法
				public void sendMessage(String message) {
					
						writer.println(message);
						writer.flush();
					
				}
		//客户端主动关闭连接
		@SuppressWarnings("deprecation")
		public synchronized boolean closeConnection() {
			try {
				sendMessage("CLOSE");// 此处发送断开连接命令给服务器
				messageThread.stop();// 客户端停止接受消息线程
				//客户端 释放资源
				if (reader != null) {
					reader.close();
				}
				if (writer != null) {
					writer.close();
				}
				if (socket != null) {
					socket.close();
				}
				isConnected = false;
				return true;
			} catch (IOException e1) {
				e1.printStackTrace();
				isConnected = true;
				return false;
			}
		}


		// 持续接收消息的线程
		class MessageThread extends Thread {
			private BufferedReader reader;
			private JTextArea textArea;
			String username=textField.getName();
		// 此处为接收消息线程的构造方法
		public MessageThread(BufferedReader reader, JTextArea textArea) {
				this.reader = reader;
				this.textArea = textArea;
			}
	 
			// 客户端被动的关闭连接(即服务器关闭,断开连接)
			public synchronized void closeCon() throws Exception {
				// 清空 用户列表
				listModel.removeAllElements();
				// 被动的关闭连接释放资源
				if (reader != null) {
					reader.close();
				}
				if (writer != null) {
					writer.close();
				}
				if (socket != null) {
					socket.close();
				}
				isConnected = false;// 修改用户状态为断开
			}
			
			public void run() {
				String message = null;
				while (true) {
					try {
						message = reader.readLine();
						StringTokenizer st=new StringTokenizer(message,"/@");
						
						//命令
						String parts=st.nextToken();
						switch(parts){
						case "CLOSE":{
							textArea.append("服务器已关闭!\r\n");
							closeCon();// 被动的关闭连接
							return;//结束线程
						}
						//用户列表事件
						case "USERLIST":{
							
							int size=Integer.parseInt(st.nextToken());
							String username=null;
							String  userIp=null;
							
							for(int i=0;i<size;i++){
							username=st.nextToken();
							userIp=st.nextToken();
							User user=new User(username,userIp);
							onLineUsers.put(username, user);
							listModel.addElement(username);
							}
							break;
						}
						
						//有新用户加入时的事件
						case "ADD":{
							String username = "";
							String userIp = "";
							if ((username = st.nextToken()) != null
									&& (userIp = st.nextToken()) != null) {

								User user = new User(username, userIp);
								onLineUsers.put(username, user);							
								listModel.addElement(username);
								textArea.append("系统提示:"+username+"上线!\r\n");
							}

							break;
						}
						
						//有用户断开连接下线时
						case "DELETE":{
							String username=st.nextToken();
						//	System.out.println(username+"下线");
							User user = (User) onLineUsers.get(username);
							onLineUsers.remove(user);
							listModel.removeElement(username);
							textArea.append("系统提示:"+username+"下线!\r\n");
							userList.setModel(listModel);
							break;
						}
						
						//当有用户选择群聊时
						case "TOALL":{
							textArea.append(st.nextToken()+"\r\n");
							//System.out.println("群聊");
							break;
						}
						
						//当有用户选择两人私聊时
						case "TOONE":{
						
							textArea.append(st.nextToken()+"\r\n");
							//System.out.println("私聊");
							//textArea.setForeground(color.blue);//私聊的消息为蓝色
							break;
						}
						default: 
							textArea.append(message+"\r\n");
							break;
					}

				} catch (IOException e) {
					e.printStackTrace();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

	}
}

Server.java

package server;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;

/**
 * @功能描述:多人聊天室服务端
 * @创建作者:炸奶糕
 * @创建日期:2021年12月22日上午10:56:03
 */
public class Server extends JFrame {
	private static final long serialVersionUID = 1L;
	private ServerSocket serveSocket;
	private ServerThread serverThread;
	private ArrayList<ClientThread> clients;
		
	private JFrame frame;
	private JTextArea txt1;
	private JTextField txt_message;
	private JTextField txt_port;
	private JButton start;
	private JButton send;
	private JButton stop;
	private JPanel northPanle;
	private JPanel southPanle;
	private JScrollPane leftPanle;
	private JScrollPane rightPanle;
	private JSplitPane centerSplit;

	private JList<String> userList;
	private DefaultListModel<String> listModel;
	private boolean isStart=false;
	
	//主函数
	public static void main(String[] args) {
		new Server();
	}
	//构造函数
	public Server(){
		frame=new JFrame("服务器");
		txt1=new JTextArea();
		txt_message=new JTextField(30);
		txt_port=new JTextField("12345");
		start=new JButton("监听此端口");
		stop=new JButton("停止服务器");
		send=new JButton("发送");
		listModel=new DefaultListModel<String>();
		userList=new JList<String>(listModel);
		southPanle=new JPanel(new BorderLayout());
		southPanle.setBorder(new TitledBorder("写消息"));
		southPanle.add(txt_message,BorderLayout.CENTER);
		southPanle.add(send,BorderLayout.EAST);
		
		leftPanle=new JScrollPane(userList);
		leftPanle.setBorder(new TitledBorder("在线用户"));
		
		rightPanle=new JScrollPane(txt1);
		rightPanle.setBorder(new TitledBorder("消息显示区"));
		//禁止txt1里用光标输入文字,文字显示区域不能编辑
		txt1.setEditable(false);
		
		centerSplit=new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,leftPanle,rightPanle);
		centerSplit.setDividerLocation(100);
		
		northPanle=new JPanel();
		northPanle.setLayout(new GridLayout(1,6));
		northPanle.add(start);
		northPanle.add(txt_port);
		northPanle.add(stop);
		
		frame.setLayout(new BorderLayout());
		frame.add(northPanle, BorderLayout.NORTH);
		frame.add(centerSplit, BorderLayout.CENTER);
		frame.add(southPanle, BorderLayout.SOUTH);
		frame.setSize(600, 400);
		frame.setVisible(true);
		
		// 关闭窗口事件
		frame.addWindowListener(new WindowAdapter() {
		@SuppressWarnings("unused")
		public void windowclosing(WindowEvent e){
			if(isStart){
				closeServer();//关闭服务器
			}
				System.exit(0);//退出程序
			}
		});
		@SuppressWarnings("unused")
		int port=Integer.parseInt(txt_port.getText());
		
		
		// 监听端口事件,即启动服务器
		start.addActionListener(new ActionListener() {
		@Override
			public void actionPerformed(ActionEvent e) {
				if(isStart){
					JOptionPane.showMessageDialog(frame, "服务器已处于启动状态","错误",JOptionPane.ERROR_MESSAGE);
					return;
				}
				int port ;
				try{
					try{
						port= Integer.parseInt(txt_port.getText());
						}catch(Exception e1){
							throw new Exception("端口号 为正整数!");
						}
					if (port<= 0) {
						throw new Exception("端口号 为正整数!");
					}
		
						serverStart(port);
						txt1.append("服务器已启动,端口:"+port+",正在等待客户端连接...\r\n");
						JOptionPane.showMessageDialog(frame, "服务器成功启动");
						start.setEnabled(false);
						txt_port.setEnabled(false);
						stop.setEnabled(true);
				} catch (Exception ee) {
					JOptionPane.showMessageDialog(frame, ee.getMessage(),"错误",JOptionPane.ERROR_MESSAGE);
					}
	
				}

		});
		// 单击停止服务器按钮时事件
		stop.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if(!isStart){
					JOptionPane.showMessageDialog(frame, "服务器还未启动!", "错误",JOptionPane.ERROR_MESSAGE);
					return;
				}
				try {
					closeServer();
					start.setEnabled(true);
					txt_port.setEnabled(true);
					stop.setEnabled(false);
					txt1.append("服务器成功停止!\r\n");
					JOptionPane.showMessageDialog(frame, "服务器成功停止!");
				} catch (Exception exc) {
					JOptionPane.showMessageDialog(frame, "停止服务器发生异常!", "错误",
					JOptionPane.ERROR_MESSAGE);
				}
			}
		});
		
		// 单击发送按钮时事件
		send.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				send();
			}
		});
	
	}




		//启动服务器
	public void serverStart(int port)throws java.net.BindException {
		try {
			clients=new ArrayList<ClientThread>();
			serveSocket = new ServerSocket(port);
			serverThread = new ServerThread(serveSocket);
			serverThread.start();
			isStart=true;
			}catch(BindException e){
				isStart=false;
				throw new BindException("端口号已被占用,换一个");
			}catch (Exception e) {
				e.printStackTrace();
				isStart=false;
				throw new BindException("启动服务器异常");
			}

	}
		//关闭服务器
		@SuppressWarnings("deprecation")
		public void closeServer() {
			try {
				if (serverThread != null)
					serverThread.stop();// 停止服务器线程

				for (int i = clients.size() - 1; i >= 0; i--) {
					// 给所有在线用户发送关闭命令
					clients.get(i).getWriter().println("CLOSE");
					clients.get(i).getWriter().flush();
					// 释放资源
					clients.get(i).stop();// 停止此条为客户端服务的线程
					clients.get(i).reader.close();
					clients.get(i).writer.close();
					clients.get(i).socket.close();
					clients.remove(i);
				}
				if (serveSocket != null) {
					serveSocket.close();// 关闭服务器端连接
					}
				listModel.removeAllElements();// 清空用户列表
				isStart=false;
			} catch (IOException e) {
				e.printStackTrace();
				isStart=true;
			}
		}

			// 执行消息发送
		public void send() {
			if(!isStart){
				JOptionPane.showMessageDialog(frame, "服务器未启动,不能发送消息!","错误",JOptionPane.ERROR_MESSAGE);
				return;
	}
			if (clients.size() == 0) {
				JOptionPane.showMessageDialog(frame, "没有用户在线,不能发送消息!", "错误",JOptionPane.ERROR_MESSAGE);
				return;
	}
			String message = txt_message.getText().trim();
				if (message == null || message.equals("")) {
					JOptionPane.showMessageDialog(frame, "消息不能为空!", "错误",JOptionPane.ERROR_MESSAGE);
					return;
				}
				sendServerMessage(message);// 群发服务器消息
				txt1.append("服务器提示:" + txt_message.getText() + "\r\n");//服务器说的话显示在服务器界面
				txt_message.setText(null);
			}

			//把后台消息发送给各个客户端
		public void sendServerMessage(String message) {
			for (int i = clients.size() - 1; i >= 0; i--) {
				clients.get(i).getWriter().println("系统提示:" + message+"(群发)");//服务器获得的输出流发送给客户端界面
				clients.get(i).getWriter().flush();
			}
		}


		//每个连接到服务器的客户端,又有与之对应的一个线程来单独处理,收发消息
		class ClientThread extends Thread{

			Socket socket;
			BufferedReader reader;
			PrintWriter writer;

			private User user;

			public BufferedReader getReader(){
				return reader;
			}

			public PrintWriter getWriter(){
				return writer;
			}
			public User getUser(){
				return user;
			}

			//每个客户端对应一个客户端线程处理
			public ClientThread(Socket socket){
				try {
					this.socket=socket;
					reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
					writer=new PrintWriter(socket.getOutputStream());
					//接收客户端的基本信息
					String line=reader.readLine();
					String[] st=line.split("@");

					user=new User(st[0],st[1]);
					//反馈连接成功消息
					writer.println("系统提示:"+user.getName()+user.getIp()+"与服务器连接成功!");

					//System.out.println(user.getName()+".."+user.getIp());
					//反馈当前用户信息
					if(clients.size()>0){
						String temp="";
						for(int i=clients.size()-1;i>=0;i--){
							temp+=(clients.get(i).getUser().getName()+"/"+clients.get(i).getUser().getIp())+"@";
			
						}
		
						writer.println("USERLIST@"+clients.size()+"@"+temp);
						writer.flush();
		
					}
					System.out.println(st[0]+",服务器显示上线"+st[1]);
					
					//向所有在线用户发送该用户上线命令,即把新上线的用户添加在在线用户列表中
					for(int i=clients.size()-1;i>=0;i--){
						clients.get(i).getWriter().println("ADD@"+user.getName()+"@"+user.getIp());
						clients.get(i).getWriter().flush();
					}	
	
				} catch (Exception e) {
					System.out.println(e.getMessage());
					e.printStackTrace();
				}

			}
			@SuppressWarnings("deprecation")
			public void run(){//不断接受客户端的消息进行处理
				String message=null;
				while(true){
					try {
						message=reader.readLine();//接收客户端消息

						if (message.equals("CLOSE"))// 下线命令
						{
							txt1.append(this.getUser().getName()+ this.getUser().getIp() + "下线!\r\n");
			 
							// 断开连接释放资源
							reader.close();
							writer.close();
							socket.close();
							
							// 向所有在线用户发送该用户的下线命令
							for (int i = clients.size() - 1; i >= 0; i--) {
								clients.get(i).getWriter().println("DELETE@" + user.getName());
								clients.get(i).getWriter().flush();
							}

							listModel.removeElement(user.getName());// 更新在线列表
							
							// 删除此条客户端服务线程
							for (int i = clients.size() - 1; i >= 0; i--) {
								if (clients.get(i).getUser() == user) {
									ClientThread temp = clients.get(i);
									clients.remove(i);// 删除此用户的服务线程
									temp.stop();// 停止这条服务线程
									return;
								}
							}
							}else{
								dispatcherMessage(message);// 转发消息
						}

						} catch (Exception e) {
							// TODO: handle exception
						}
					}
				}

			private void dispatcherMessage(String message) {

				String[] parts=message.split("@");
				String string=parts[1]+"对"+parts[2]+"说:"+parts[3];
				if (parts[0].equals("TOALL")) {// 群发
					for (int i = clients.size() - 1; i >= 0; i--) {
						clients.get(i).getWriter().println("TOALL@"+string);
						clients.get(i).getWriter().flush();
						//System.out.println("群聊消息发送");
					}
					txt1.append(string + "\r\n");
				}
				if(parts[0].equals("TOONE")){//私发
					for(int i=0;i<clients.size();i++){
						if(parts[2].equals((clients.get(i).getUser().getName())))
						{  
							string=parts[1]+"对我说:"+parts[3];
							clients.get(i).getWriter().println("TOONE@"+string);
							clients.get(i).getWriter().flush();
							//System.out.println("私聊消息发送");
						}
						txt1.append( parts[1]+"对"+parts[2]+"说:"+parts[3]+ "\r\n");
		
					}

	
				}	
			}

		}
		
		//服务器线程
		class ServerThread extends Thread {
			private ServerSocket serverSocket;
			// 服务器线程的构造方法
			public ServerThread(ServerSocket serverSocket) {
				this.serverSocket = serverSocket;
	
			}

			public void run() {
				while (true) {// 不停的等待客户端的链接
					try {
						Socket socket = serverSocket.accept();
						ClientThread client = new ClientThread(socket);
						client.start();// 开启对此客户端服务的线程
						clients.add(client);
						listModel.addElement(client.getUser().getName());// 更新在线列表
						txt1.append(client.getUser().getName()+ client.getUser().getIp() + "上线!\r\n");
					}catch (SocketException e) {
						e.printStackTrace();
					}catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
}



User.java

package server;
public class User {
    private String name;
    private String ip;
    public User(String name,String ip){
        this.name=name;
        this.ip=ip;
    }
    public String getName() {
        return name;
    }
    public void setName(String name){
        this.name=name;
    }
    public String getIp() {

        return ip;
    }
    public void setIp(String ip){
        this.ip=ip;
    }
}

1.聊天室的简要功能介绍

(1)用户设置:

功能描述:用户可在客户端页面未按连接按钮之前,自定义自己的昵称,成功设置后,服务端能接受到新设置的用户名;

(2)登录功能:

功能描述:在服务端开启的情况下,用户可以通过自己定义或者默认登录名和登录地址上线聊天室,并在服务器显示登录状态;

  1. 群聊功能:

功能描述:用户在客户端窗口中可以选择“群聊”发送信息,同在聊天室的所有用户进行聊天;

  1. 私聊功能:

功能描述:用户可以点击其他任意一个在线用户进行私聊,且双方的聊天记录是对其他用户不可见的;

  1. 系统消息:

功能描述:服务器正常运行的过程中,在聊天室的每一个用户都能接受服务器端发送的提示信息;

  1. 用户的状态:

功能描述:每一个客户端用户可以实时看到看到其他在线用户的状态;

  1. 用户注销:

功能描述:每一个客户端用户都可以注销自己的账号,重新登记昵称;

  1. 用户退出:

功能描述:用户可以在正常情况下退出自己的聊天室;

  1. 服务端控制:

功能描述:只有在服务端开启的情况下,用户才能登陆成功和实现聊天功能;用户设置的地址端口也必须与服务器的侦听端口一致。

  1. 模块描述

该简易聊天室分为聊天室服务器和聊天室客户端,服务端和客户端之间通过Socket(TCP)连接,进行信息传输及转发。服务器采用多线程以满足多个用户同时在线聊天的需求,并通过创建一个socket对象监听来自客户端的连接请求,默认IP地址为127.0.0.1,默认端口号为12345。

端口服务:启动开始侦听的窗口,默认端口号为12345;

启动服务:启动服务器,准备连接客户端,连接成功后,可通过与服务器通信,建立管道,与其他在线用户进行聊天;

停止服务:关闭服务器,侦听结束。客户端将不能再聊天;

发送系统消息:服务端给所有人发送信息或者命令;

消息显示:可以显示所有用户的聊天记录,和上线、下线的记录;

退出服务器:退出程序,并停止服务。

(2)客户端模块图:

用户设置:在连接服务端之前,用户可以自定义昵称;

连接设置:用户可以设置自己服务器的端口号和IP地址,默认连接地址为127.0.0.1:12345;

用户登录:只有在服务端运行的情况下,用户才能成功登录,用户登录之后才能开始聊天,登录之后就不能再更新用户设置和连接设置;

用户注销(主动断开):用户点击注销后,会显示下线,退出聊天,此时可以修改用户设置和连接设置;

发送、接受:用户可以群发或者私聊,也可以看到其他人发给自己或者所有人的聊天记录;

退出窗口:关闭所有聊天及退出窗口。

4.聊天室服务器端设计

服务器端由一个package.server组成,内含Server.java和User.java、Server.java内有三个类:Server类、ServerThread类和ClientThread类,其中ServerThread、ClientThread类是Server的内部类。User.java只有一个类User.java。

Server类的主要功能是设置服务端窗口的GUI界面、管理窗口关闭事件、管理监听启动事件、管理点击按钮事件、设置启动、停止的方法(方便调用多线程)、发送功能(将服务端输入的信息发送到所有在线用户)、发送系统消息或命令(后台命令)。

ClientThread类作为Server类的内部类,继承自Thread类,每个连接到服务器的客户端,又有与之对应的一个线程来单独处理,收发消息。它可以接收客户端的基本信息、反馈连接成功的信息、向所有在线用户发送该用户上线命令,即把新上线的用户添加在在线用户列表中、不断接受客户端的消息进行处理、对于私聊和群聊两种不同聊天方式采用private定义了dispatcherMessage()方法,在此方法中对这两种方式进行判断,在进行不同的处理。

ServerThread类也是Server类的内部类,同样继承自Thread类,用于等待接收新客户端的连接进而开启其对应的ClientThread客户端线程,更新在线用户列表。

User类是用户类,只有一个构造方法User(),其主要用于Sever.java中登录系统时对端口及IP地址进行判定,该类则为登录程序的模型(model)。

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值