JAVA GUI聊天室
涉及的知识点:Java网络编程、GUI、多线程、IO流
服务端:
- 服务端界面设计
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.swing.*;
public class ServiceView extends JFrame implements ActionListener{
/**
*
*/
private static final long serialVersionUID = 1L;
private static final int port = 9090;
private JButton openBtn, closeBtn;
private JButton JBExport = new JButton("导出");
//创建文本输入框, 参数分别为行数和列数
public static JTextArea jtMessage = new JTextArea();
//创建聊天消息框
public static JTextArea jtMember = new JTextArea();
JScrollPane JSMessage = new JScrollPane(jtMessage);
JScrollPane JSMember = new JScrollPane(jtMember);
private Service service = null;
private static ServiceView view;
public static ArrayList<ClientView> clientViews = new ArrayList<>();
public static ArrayList<String> user = new ArrayList<String>();
public static ServiceView getView() {
return view;
}
public static void main(String[] args) {
view = new ServiceView();
}
public ServiceView() {
this.setTitle("服务端");
this.setLayout(new BorderLayout());
this.setSize(500, 500);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel panel_north = new JPanel();
panel_north.setLayout(null);
panel_north.setPreferredSize(new Dimension(500, 50));
panel_north.setBackground(Color.WHITE);
JLabel label = new JLabel("端口:");
JTextField JTPort = new JTextField(8);
JTPort.setText("9090");
openBtn = new JButton("打开服务端");
closeBtn = new JButton("关闭服务端");
label.setBounds(10, 10, 60, 30);
JTPort.setBounds(60, 10, 100, 30);
openBtn.setBounds(240, 10, 100, 30);
closeBtn.setBounds(360, 10, 100, 30);
closeBtn.setEnabled(false);
openBtn.addActionListener(this);
closeBtn.addActionListener(this);
JPanel panel_south = new JPanel();
panel_south.setLayout(null);
panel_south.setPreferredSize(new Dimension(500, 450));
JLabel log = new JLabel("日志");
JLabel online = new JLabel("在线用户");
log.setFont(new Font(null,9, 18));
log.setBounds(10, 60, 60, 20);
JBExport.setBounds(300, 80, 60, 20);
online.setBounds(390, 60, 60, 20);
JBExport.addActionListener(this);
//设置文本输入框大小和位置
JSMessage.setBounds(10, 80, 350, 300);
//设置文本输入框字体
jtMessage.setFont(new Font("楷体", Font.BOLD, 14));
//聊天消息框自动换行
jtMessage.setLineWrap(true);
//聊天框不可编辑,只用来显示
jtMessage.setEditable(false);
JSMember.setBounds(370, 80, 100, 300);
jtMember.setEditable(false);
panel_north.add(label);
panel_north.add(JTPort);
panel_north.add(openBtn);
panel_north.add(closeBtn);
panel_south.add(log);
panel_south.add(JBExport);
panel_south.add(online);
panel_south.add(JSMessage);
panel_south.add(JSMember);
this.add(panel_north, BorderLayout.NORTH);
this.add(panel_south, BorderLayout.SOUTH);
this.setVisible(true);
}
//时间监听
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == openBtn) {
open();
}
if (e.getSource() == closeBtn) {
stop();
}
if (e.getSource() == JBExport) {
String msg = jtMessage.getText();
//得到实时时间
Date date = new Date();
SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd-hh-mm-ss");
System.out.println(ft.format(date));
//日志写入txt文件(以时间命名)
String fileName = "E:\\"+ft.format(date)+".txt";
try {
FileWriter writer = new FileWriter(fileName,true);
//写入日志文件
writer.write(msg);
writer.close();
} catch (IOException e1) {
//e1.printStackTrace();
}
}
}
public void open() { //开启服务器
service = new Service(port);
Thread thread = new Thread(service);
thread.start();
JOptionPane.showMessageDialog(null, "服务器打开!");
System.out.println("服务器打开");
openBtn.setEnabled(false);
closeBtn.setEnabled(true);
}
public void stop() {
//关闭服务器
openBtn.setEnabled(true);
closeBtn.setEnabled(false);
try {
synchronized (Service.sockets) {
//关闭各个连接
for (ChatSocket socket : Service.sockets) {
socket.getInputStream().close();
socket.getOutputStream().close();
}
Service.sockets.removeAllElements();
}
for (ClientView view : clientViews) {
view.getInputStream().close();
view.getOutputStream().close();
}
service.getServerSocket().close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 服务端多线程循环等待连接
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
import javax.swing.JOptionPane;
/**
* 服务端,使用线程达到循环等待连接的目的
* @author Administrator
*
*/
public class Service implements Runnable{
private ServerSocket serverSocket = null;
private int port = 9090;
//客户端集合
public static Vector<ChatSocket> sockets = new Vector<ChatSocket>();
public Service(int port){
this.port = port;
}
@Override
public void run() {
try {
serverSocket = new ServerSocket(port);
while (true) {
//循地接收客户端的连接
Socket socket = serverSocket.accept();
//新客户端连接,创建ChatSocket对象(包含InputStream和OutputStream)
ChatSocket chatSocket = new ChatSocket(socket);
//往客户端管理器里添加客户
sockets.add(chatSocket);
//启用线程使服务器开始不断接收客户端信息
Thread thread = new Thread(chatSocket);
thread.start();
}
} catch (IOException e) {
//e.printStackTrace();
JOptionPane.showMessageDialog(null, "服务器关闭!");
}
}
public ServerSocket getServerSocket() {
return serverSocket;
}
}
- 服务端功能
import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Vector;
/**
* 功能:使用传入的socket获得数据流,达到传输数据的目的
* @author Administrator
*
*/
@SuppressWarnings("unused")
public class ChatSocket implements Runnable{
private Socket socket = null;
private DataInputStream inputStream = null;
private DataOutputStream outputStream = null;
public ChatSocket(Socket socket) {
this.socket = socket;
try {
this.inputStream = new DataInputStream(socket.getInputStream());
this.outputStream = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
//get方法
public DataInputStream getInputStream() {
return inputStream;
}
public DataOutputStream getOutputStream() {
return outputStream;
}
//向客户端发送数据
public void send(String message) {
try {
outputStream.writeUTF(message);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
String message = null;
//循环读取客户端发来的数据
while (true) {
try {
//读取到的数据
message = inputStream.readUTF();
String[] s = message.split("#");
String name = s[0];
String msg = s[1];
String state = s[2];
Date date = new Date();
SimpleDateFormat ft = new SimpleDateFormat ("HH:MM:ss");
String time = ft.format(date);
if(state.equals("1")) {
//清空列表
ServiceView.jtMember.setText(null);
//添加用户到用户列表
ServiceView.user.add(name);
//添加用户上线通知
ServiceView.jtMessage.append(time+" "+name+msg+"\n");
//列表更新
Iterator<String> it = ServiceView.user.iterator();
while(it.hasNext()) {
String u = it.next();
ServiceView.jtMember.append(u+"\n");
}
}
else if(state.equals("2")){
//发送给其他客户端(完整的输入的信息包含:用户名#信息#状态)
//添加聊天信息
ServiceView.jtMessage.append(time+" "+name+":"+msg+"\n");
for (ChatSocket socket : Service.sockets) {
if (!this.equals(socket)) {
socket.send(message);
}else {
continue;
}
}
}else if(state.equals("3")) {
//清空列表
ServiceView.jtMember.setText(null);
//移除下线用户
ServiceView.user.remove(name);
//添加下线信息
ServiceView.jtMessage.append(time+" "+s[0]+s[1]+"\n");
//列表更新
Iterator<String> it = ServiceView.user.iterator();
while(it.hasNext()) {
String u = it.next();
ServiceView.jtMember.append(" "+u+"\n");
}
}
} catch (IOException e) {
//客户端已退出,将其移出客户端集合
Service.sockets.remove(this);
}
}
}
}
客户端:
import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.*;
public class ClientView extends JFrame implements ActionListener, Runnable, KeyListener {
/**
* 聊天室测试
*/
private static final long serialVersionUID = 1L;
private JTextArea textArea;
private JTextField textField, Name;
private JButton btnSend;
private JLabel label;
private JPanel panel_north, panel_south;
private Socket socket = null;
private DataInputStream inputStream = null;
private DataOutputStream outputStream = null;
private JScrollPane scrollPane;
private Date date = new Date();
int state = 1;
private static String name = null;
public JTextArea getTextArea() {
return textArea;
}
public DataInputStream getInputStream() {
return inputStream;
}
public DataOutputStream getOutputStream() {
return outputStream;
}
public static void main(String[] args) {
name = JOptionPane.showInputDialog(null, "昵称:");
if(name == null) {
return;
}
ClientView view = new ClientView();
Thread thread = new Thread(view);
thread.start();
}
public ClientView() {
try {
//连接服务器,建立TCP服务
socket = new Socket("127.0.0.1", 9090);
UserView();
state = 1;
String s = "上线";
File file = new File("music/01.wav");
URI uri = file.toURI();
URL url = uri.toURL();
AudioClip ac = Applet.newAudioClip(url);
ac.play();
sendMessage(s);
} catch (UnknownHostException e) {
JOptionPane.showMessageDialog(null, "未知的服务器地址!");
e.printStackTrace();
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "服务器未开启!");
System.exit(0);
}
}
private void UserView() {
textArea = new JTextArea(20, 20);
textArea.setEditable(false);
scrollPane = new JScrollPane(textArea);
textField = new JTextField(15);
textField.addKeyListener(this);
btnSend = new JButton("发送");
btnSend.addActionListener(this);
label = new JLabel("昵称:");
Name = new JTextField(name);
panel_north = new JPanel();
panel_south = new JPanel();
panel_north.add(label);
panel_north.add(Name);
panel_north.setLayout(new FlowLayout(FlowLayout.LEFT));
panel_south.add(textField);
panel_south.add(btnSend);
panel_south.setLayout(new FlowLayout(FlowLayout.CENTER));
add(panel_north, BorderLayout.NORTH);
add(scrollPane, BorderLayout.CENTER);
add(panel_south, BorderLayout.SOUTH);
setTitle("聊天室测试——2.0");
setSize(500, 500);
setLocation(450, 150);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
addWindowListener(new WindowAdapter() { //窗口关闭后断开连接
@Override
public void windowClosing(WindowEvent e) {
try {
state = 3;
sendMessage("下线");
if (socket != null)
socket.close();
if (inputStream!= null)
inputStream.close();
if (outputStream != null)
outputStream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == btnSend) {
String s = textField.getText();
state = 2;
sendMessage(s);
}
}
//回车键监听
public void keyPressed(KeyEvent arg0) {
if (arg0.getKeyCode() == KeyEvent.VK_ENTER) {
String s = textField.getText();
state = 2;
sendMessage(s);
}
}
@Override
public void keyTyped(KeyEvent e) {}
@Override
public void keyReleased(KeyEvent e) {}
/**
* 循环接收服务端发送的消息
*/
@Override
public void run() {
try {
inputStream = new DataInputStream(socket.getInputStream());
while (true) {
String[] s = inputStream.readUTF().split("#");
SimpleDateFormat ft = new SimpleDateFormat ("HH:MM:ss");
String time = ft.format(date);
if(s[2].equals("2")) {
File file = new File("music/02.wav");
URI uri = file.toURI();
URL url = uri.toURL();
AudioClip ac = Applet.newAudioClip(url);
ac.play();
//解析出信息内容:s[0] 该用户名 s[1] 信息
textArea.append(time+" "+s[0] + ":\n"+ s[1] + "\n\n");
//手动设置光标的位置未最后一行
textArea.setCaretPosition(textArea.getDocument().getLength());
}
}
} catch (IOException e) {
}
}
/**
* 发送信息给服务端
*/
private void sendMessage(String s) {
try {
if (!s.equals("")) {
SimpleDateFormat ft = new SimpleDateFormat ("HH:MM:ss");
String time = ft.format(date);
if(state == 2) {
textField.setText("");
textArea.append(time+" 我【" + Name.getText() + "】:\n" + s + "\r\n\n");
}
outputStream = new DataOutputStream(socket.getOutputStream());
//发送的格式:用户名#消息
outputStream.writeUTF(Name.getText() + "#" + s+"#"+state);
outputStream.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果: