Java: Swing + 网络编程 实现多人聊天室功能
服务端代码
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Iterator;
public class ServerChat extends JFrame {
private static final int PORT = 7608;
private JTextArea serverTa = new JTextArea();
private JScrollPane sp = new JScrollPane(serverTa);
private JPanel btntool = new JPanel();
private JButton stopbtn = new JButton("停止");
private ArrayList<ClientConn> ccList = new ArrayList<>();
private ServerSocket ss = null;
private boolean isStart = false;
public ServerChat() {
this.setTitle("服务器端");
this.add(sp, BorderLayout.CENTER);
this.btntool.add(stopbtn);
this.add(btntool, BorderLayout.SOUTH);
this.setBounds(0, 0, 500, 500);
stopbtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
isStart = false;
try {
if (ss != null) {
ss.close();
}
System.out.println("服务器停止!\n");
serverTa.append("服务器断开!\n");
System.exit(0);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
});
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
isStart = false;
try {
if (ss != null) {
ss.close();
}
System.out.println("服务器停止!\n");
serverTa.append("服务器断开!\n");
System.exit(0);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
});
serverTa.setEditable(false);
this.setVisible(true);
startServer();
}
public void startServer(){
try {
try {
ss = new ServerSocket(PORT);
isStart = true;
} catch (IOException e) {
e.printStackTrace();
}
while (isStart) {
Socket s = ss.accept();
ccList.add(new ClientConn(s));
System.out.println("一个客户端连接服务区:" + s.getInetAddress() + "/" + s.getPort());
serverTa.append("一个客户端连接服务区:" + s.getInetAddress() + "/" + s.getPort() + "\n");
}
} catch (IOException e) {
e.printStackTrace();
}
serverTa.append("服务器启动成功!\n");
}
class ClientConn implements Runnable{
Socket s = null;
public ClientConn(Socket s) {
this.s = s;
(new Thread(this)).start();
}
@Override
public void run() {
try {
DataInputStream dis = new DataInputStream(s.getInputStream());
while (isStart) {
String str = dis.readUTF();
System.out.println(s.getInetAddress() + "/" + s.getPort() +"说: " + str);
serverTa.append(s.getInetAddress() + "/" + s.getPort() +"说: " + str + "\n");
String strSend = s.getInetAddress() + "/" + s.getPort() +"说: " + str + "\n";
Iterator<ClientConn> it = ccList.iterator();
while (it.hasNext()) {
ClientConn o = it.next();
o.send(strSend);
}
}
} catch (SocketException e) {
System.out.println("一个客户端下线了:" + s.getInetAddress() + "/" + s.getPort());
serverTa.append("一个客户端下线了:" + s.getInetAddress() + "/" + s.getPort() + "\n");
}
catch (IOException e) {
System.out.println("服务器中断了!");
e.printStackTrace();
}
}
public void send(String str) {
try {
DataOutputStream dos = new DataOutputStream(this.s.getOutputStream());
dos.writeUTF(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ServerChat sc = new ServerChat();
}
}
客户端代码
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
public class ClientChat extends JFrame {
private JTextArea ta = new JTextArea(10, 20);
private JScrollPane sp = new JScrollPane(ta);
private JTextField tf = new JTextField(20);
private JButton b = new JButton("Send");
private JPanel jp = new JPanel();
private static final String CONNSTR = "localhost";
private static final int CONNPOST = 7608;
private Socket s = null;
private DataOutputStream dos = null;
private boolean isConn = false;
public ClientChat() throws HeadlessException {
super();
}
public void init() {
this.setTitle("欢迎使用 xxx 聊天室应用");
this.add(sp, BorderLayout.CENTER);
jp.add(tf, BorderLayout.CENTER);
jp.add(b, BorderLayout.EAST);
this.add(jp, BorderLayout.SOUTH);
this.setBounds(300, 300, 500, 400);
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String strSend = tf.getText();
if (strSend.trim().length() == 0) {
return;
}
send(strSend);
tf.setText("");
}
});
tf.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String strSend = tf.getText();
if (strSend.trim().length() == 0) {
return;
}
send(strSend);
tf.setText("");
}
});
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ta.setEditable(false);
tf.requestFocus();
try {
s = new Socket(CONNSTR, CONNPOST);
isConn = true;
ta.append("已连接上服务器!\n");
} catch (SocketException e) {
System.out.println("找不到服务器!");
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
this.setVisible(true);
new Thread(new Receive()).start();
}
public void send(String str) {
try {
dos = new DataOutputStream(s.getOutputStream());
dos.writeUTF(str);
} catch (IOException e) {
e.printStackTrace();
}
}
class Receive implements Runnable {
@Override
public void run() {
try {
while (isConn) {
DataInputStream dis = new DataInputStream(s.getInputStream());
String str = dis.readUTF();
ta.append(str);
}
} catch (SocketException e) {
System.out.println("服务器意外终止");
ta.append("服务器意外终止!\n");
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ClientChat cc = new ClientChat();
cc.init();
}
}
运行效果
分析
- 服务器端窗口:
(1)此处用本机(win10系统)作为服务器, 当然也可以不用图形化界面,将其布置在云服务器。
(2)中间文本框显示客户端登入登出信息以及聊天的历史消息, 不可编辑
(3)最底下的"停止"按钮负责退出服务器端, 但不会退出窗口
(4)按窗口右上角 X 即可退出服务器端并关闭窗口
- 客户端窗口
(1)采用BorderLayout布局
(2)中间文本框显示历史消息, 不可编辑
(3)最底下的文本框负责输入即将发送的文字消息, 按Send按钮或者回车键发送
(4)按窗口右上角 X 即可退出客户端并关闭窗口
- 多线程实现
客户端发送信息,服务器端接受信息显示在自己的窗口上,并向其他客户端发送该信息,其他客户端收到信息则会将信息显示在自己的窗口上