Java 实现聊天室

效果:
在这里插入图片描述在这里插入图片描述

源码地址https://github.com/ikebo/SimpleChat
客户端安装包下载地址https://download.csdn.net/download/k_runtu/10886345

同时发于我的个人博客https://ikebo.cn

客户端代码-Client.java

package main;

/*省略引入的包*/
import java.awt.EventQueue;

import javax.swing.JFrame;
import java.awt.BorderLayout;
import javax.swing.JTextPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.JLabel;
import javax.swing.ImageIcon;
import java.awt.Font;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.Toolkit;
import java.awt.Color;
import java.awt.Dimension;

import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class Client implements Runnable {

	private JFrame frmSimplechat;
	private JTextField textField;
	private JTextArea textArea;
	private JTextArea textArea_1;
	
	private Socket connection;
    private DataInputStream in;
    private DataOutputStream out;
    private ObjectOutputStream objOut;
    private ObjectInputStream objIn;
    
    private NickNameDialog dialog1;
    
    private Thread MessageHandler;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					Client window = new Client();
					//window.frame.setVisible(true);
					
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 * @throws IOException 
	 * @throws UnknownHostException 
	 * @throws InterruptedException 
	 */
	public Client() throws UnknownHostException, IOException, InterruptedException {
		this.dialog1 = new NickNameDialog(this);
		this.MessageHandler = new Thread(this);
		this.connection = new Socket ("ikebo.cn", 6000);
        System.out.println("Connection established.");
        this.in = new DataInputStream(connection.getInputStream());
        this.out = new DataOutputStream(connection.getOutputStream());
        this.objOut = new ObjectOutputStream(connection.getOutputStream());
        this.objIn = new ObjectInputStream(connection.getInputStream());
        initialize();
        
		this.frmSimplechat.setVisible(true);
		dialog1.setVisible(true);
        this.MessageHandler.start();  
	}
	
	
	public void run() {
		String[] arr;
        while(true) {
			try {
				if (this.in.available() > 0 || this.objIn.available() > 0) {
				    arr = (String[])this.objIn.readObject();
				    if (arr[1].equals("0")) {
				    	System.out.println(arr[0]);
					    String preText = this.textArea.getText();
					    String str = new String("");
					    if (preText.equals("")) {
					    	str = arr[0];
					    } else {
					    	str = preText + "\n" + arr[0];
					    }
					    this.textArea.setText(str);
					    this.textArea.setCaretPosition(this.textArea.getDocument().getLength());
				    } else if (arr[1].equals("1")) {
				    	System.out.println("广播在线用户...");
				    	this.textArea_1.setText(arr[0]);
				    }
				}
				Thread.sleep(500);
			} catch (ClassNotFoundException | IOException | InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
        }
	}
	
	
	// 未输入用户名直接退出的情况, 需关闭对应的Server线程
	public void handleCloseDialog() throws IOException {
		postMessage("", "-1");
		System.exit(-1);
	}
	
	public void postMessage(String word, String type) throws IOException {
		this.objOut.writeObject(new String[] {word, type});
		this.objOut.flush();
		System.out.println("sent");
	}
	
	public void handleEnterNickName(String nickName) throws IOException {
		// 将昵称发到Server端广播
		postMessage(nickName, "0");
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		Client that = this;
		frmSimplechat = new JFrame();
		frmSimplechat.setBackground(Color.LIGHT_GRAY);
		frmSimplechat.setIconImage(Toolkit.getDefaultToolkit().getImage(Client.class.getResource("/javax/swing/plaf/metal/icons/ocean/computer.gif")));
		frmSimplechat.setTitle("SimpleChat");
		frmSimplechat.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				try {
					postMessage("", "-1");
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});
		frmSimplechat.setResizable(false);
		frmSimplechat.setBounds(100, 100, 740, 488);
		frmSimplechat.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frmSimplechat.getContentPane().setLayout(null);
		
		
		JLabel lblNewLabel_1 = new JLabel("输入:");
		lblNewLabel_1.setBounds(15, 375, 53, 21);
		frmSimplechat.getContentPane().add(lblNewLabel_1);
		
		textField = new JTextField();
		textField.setFont(new Font("楷体", Font.PLAIN, 20));
		textField.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				// 回车键发送消息
				if (e.getKeyCode() != 10) {
					return ;
				}
				String str = that.textField.getText();
				if (str.trim().equals("")) {
					return ;
				}
				try {
					postMessage(str, "1");
					that.textField.setText("");
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});
		textField.setBounds(65, 366, 388, 38);
		frmSimplechat.getContentPane().add(textField);
		textField.setColumns(10);
		
		JButton btnNewButton = new JButton("发送");
		btnNewButton.setContentAreaFilled(false);   // 设为透明
		// 监听发送消息事件
		btnNewButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String str = that.textField.getText();
				if (str.trim().equals("")) {
					return ;
				}
				try {
					postMessage(str, "1");
					that.textField.setText("");
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});
		btnNewButton.setBounds(468, 366, 83, 38);
		frmSimplechat.getContentPane().add(btnNewButton);
		
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(36, 73, 468, 250);
		frmSimplechat.getContentPane().add(scrollPane);
		
		textArea = new JTextArea();
		scrollPane.setViewportView(textArea);
		textArea.setFont(new Font("Monospaced", Font.PLAIN, 20));
		textArea.setEditable(false);
		
		JLabel label = new JLabel("消息列表");
		label.setFont(new Font("隶书", Font.PLAIN, 22));
		label.setBounds(26, 15, 96, 32);
		frmSimplechat.getContentPane().add(label);
		
		JLabel label_1 = new JLabel("在线用户");
		label_1.setFont(new Font("隶书", Font.PLAIN, 22));
		label_1.setBounds(508, 22, 96, 21);
		frmSimplechat.getContentPane().add(label_1);
		
		JScrollPane scrollPane_1 = new JScrollPane();
		scrollPane_1.setBounds(540, 73, 144, 250);
		frmSimplechat.getContentPane().add(scrollPane_1);
		
		textArea_1 = new JTextArea();
		textArea_1.setForeground(new Color(60, 179, 113));
		textArea_1.setFont(new Font("隶书", Font.PLAIN, 23));
		textArea_1.setEditable(false);
		scrollPane_1.setViewportView(textArea_1);
		
		// 设置窗体居中显示
		int[] loca = getCenterLocation(frmSimplechat.getWidth(), frmSimplechat.getHeight());
		frmSimplechat.setLocation(loca[0],loca[1]);
	}
	
	// 获取屏幕中间位置的坐标值
	public int[] getCenterLocation(int windowWidth, int windowHeight) {
		Toolkit kit = Toolkit.getDefaultToolkit();
		Dimension screenSize = kit.getScreenSize();
		int screenWidth = screenSize.width;
		int screenHeight = screenSize.height;
		return new int[] {screenWidth/2-windowWidth/2, (screenHeight/2-windowHeight/2)-(screenHeight*15/100)};
	}
}

客户端用户名对话框-NickNameDialog.java

package main;

import java.awt.BorderLayout;
import java.awt.FlowLayout;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JLabel;
import javax.swing.JTextField;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Window.Type;
import java.awt.Dialog.ModalExclusionType;
import java.awt.Dialog.ModalityType;
import java.awt.Font;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;

public class NickNameDialog extends JDialog {

	private final JPanel contentPanel = new JPanel();
	private JTextField textField;
	private Client window;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		try {
			NickNameDialog dialog = new NickNameDialog(null);
			dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			dialog.setVisible(true);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Create the dialog.
	 */
	public NickNameDialog(Client window) {
		this.window = window;
		NickNameDialog that = this;
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				closeFrame();
				try {
					that.window.handleCloseDialog();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});
		setModalityType(ModalityType.APPLICATION_MODAL);
		setTitle("提示");
		setResizable(false);
		setBounds(100, 100, 450, 217);
		getContentPane().setLayout(new BorderLayout());
		contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
		getContentPane().add(contentPanel, BorderLayout.CENTER);
		contentPanel.setLayout(null);
		{
			JLabel lblNewLabel = new JLabel("请输入用户名:");
			lblNewLabel.setBounds(15, 15, 126, 35);
			contentPanel.add(lblNewLabel);
		}
		{
			textField = new JTextField();
			textField.setFont(new Font("隶书", Font.PLAIN, 22));
			textField.setBounds(45, 54, 319, 47);
			contentPanel.add(textField);
			textField.setColumns(10);
		}
		{
			JPanel buttonPane = new JPanel();
			buttonPane.setBounds(0, 116, 444, 46);
			contentPanel.add(buttonPane);
			{
				JButton okButton = new JButton("确定");
				okButton.setBounds(250, 5, 73, 41);
				okButton.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						String str = textField.getText();
						System.out.println(str);
						try {
							that.window.handleEnterNickName(str);
						} catch (IOException e1) {
							// TODO Auto-generated catch block
							e1.printStackTrace();
						}
						closeFrame();
					}
				});
				buttonPane.setLayout(null);
				okButton.setActionCommand("OK");
				okButton.setContentAreaFilled(false);   // 设为透明
				buttonPane.add(okButton);
				getRootPane().setDefaultButton(okButton);
			}
			{
				JButton cancelButton = new JButton("取消");
				cancelButton.setContentAreaFilled(false);   // 设为透明
				cancelButton.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						closeFrame();
						try {
							that.window.handleCloseDialog();
						} catch (IOException e1) {
							// TODO Auto-generated catch block
							e1.printStackTrace();
						}
					}
				});
				cancelButton.setBounds(338, 5, 87, 41);
				cancelButton.setActionCommand("Cancel");
				buttonPane.add(cancelButton);
			}
		}
		// 设置窗体居中显示
		int[] loca = this.window.getCenterLocation(this.getWidth(),this.getHeight());
		this.setLocation(loca[0], loca[1]);
		
	}
	
	public void closeFrame() {
		this.dispose();
	}

}

服务端-Server.java

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Iterator;
import java.util.LinkedList;

public class Server {
    protected LinkedList<ServerThread> handlers;
    private ServerSocket server;

    public Server() throws IOException {
        this.server = new ServerSocket(6000);
        this.handlers = new LinkedList<>();
        System.out.println("Server started on " + server.getLocalPort());
        startServer();
    }


    public void startServer() throws IOException {
        while (true) {
            Socket connection = this.server.accept();
            System.out.println("New Thread.");
            ServerThread handler = new ServerThread(this, connection);
            this.handlers.add(handler);
        }
    }

    // 广播
    public void brodCast(String word, String type) throws IOException {
        for (Iterator<ServerThread> it = this.handlers.iterator(); it.hasNext(); ) {
            ServerThread serverThread = (ServerThread) it.next();
            if (type.equals("0") && word.split("说")[0].equals(serverThread.getNickName())) {
            	String[] arr = word.split("说");
            	String xword = "";
            	for (int i=1; i<arr.length; i++) {
            		xword += arr[i];
            	}
            	serverThread.say(serverThread.getNickName()+"(我)说"+xword, type);
            	continue;
            }
            serverThread.say(word, type);
        }
    }

    // 广播用户列表
    public void showAllUsers() throws IOException {
        StringBuffer word = new StringBuffer("");
        for (Iterator<ServerThread> it = this.handlers.iterator(); it.hasNext();) {
            word.append(((ServerThread)it.next()).getNickName()+"\n");
        }
        this.brodCast(word.toString(),"1");
    }
    
    public void removeHandler(ServerThread serverThread) {
    	for (Iterator<ServerThread> it = this.handlers.iterator(); it.hasNext();) {
    		if (((ServerThread)it.next()) == serverThread) {
    			this.handlers.remove(serverThread);
    		}
    	}
    }
    
    public void printHandlersCount() {
    	System.out.println("当前Server线程:" + this.handlers.size() + " 个");
    }

    public static void main(String[] args) throws IOException {
        Server server = new Server();
    }

}

服务端-ServerThread.java

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Scanner;

public class ServerThread extends Thread {
    private Socket connection;
    private Server Server;
    private DataInputStream in;
    private ObjectInputStream objIn;
    private DataOutputStream out;
    private ObjectOutputStream objOut;
    private String nickName;
    private boolean running;

    public ServerThread(Server Server,Socket connection) throws IOException {
        this.Server = Server;
        this.connection = connection;
        this.in = new DataInputStream(connection.getInputStream());
        this.objIn = new ObjectInputStream(connection.getInputStream());
        this.out = new DataOutputStream(connection.getOutputStream());
        this.objOut = new ObjectOutputStream(connection.getOutputStream());
        this.running = true;
        start();
    }


    public void run() {
    	String[] arr;
        while (this.running) {
            try {
				if (this.objIn.available() > 0 || this.in.available() > 0) {
					System.out.println("yes");
				    arr = (String[])this.objIn.readObject();
				    if (arr[1].equals("0")) {
				    	this.Server.brodCast(arr[0] + "已加入群聊", "0");
				        this.nickName = arr[0];
				        this.Server.showAllUsers();  // 广播在线用户
				    } else if (arr[1].equals("1")) {
				    	this.Server.brodCast(this.nickName+"说: " + arr[0], "0");
				    } else if (arr[1].equals("-1")) {
				    	this.running = false;
				    	System.out.println(this.nickName + "下线");
				    	if (this.nickName != null) {
				    		this.Server.brodCast(this.nickName + "已退出群聊", "0");
				    	}
				    	this.in.close();
				    	this.out.close();
				    	this.Server.removeHandler(this);
				    	this.Server.showAllUsers();   // 广播在线用户
				    	this.Server.printHandlersCount();
				    }
				}
				sleep(500);
			} catch (IOException | InterruptedException | ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
            
        }
    }

    public void say(String word, String type) throws IOException {
        this.objOut.writeObject(new String[] {word, type});
        this.objOut.flush();
    }

    protected String getNickName() {
        return this.nickName;
    }

}

[完]

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值