效果
:
源码地址
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;
}
}
[完]