1.整体思路
思路图
整体思路如上: 涉及知识点:线程+网络编程+集合+IO等
TCP 协议
2.代码实现过程
服务端
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class Server extends JFrame {//继承窗口来实现窗口功能
/*设置接受客户端的集合,把接入服务器的客户端存入集合,方便后续的消息转发*/
ArrayList<Socket> socketsClients = new ArrayList<>();
/*由于下面线程需要调用窗口的一些东西,所以需要全局变量*/
JTextArea jTextArea;
JTextArea jTextSend = new JTextArea(30,20);//只能设置几列
JButton jButtonSend = new JButton("发送") ;
public Server() {
//创建服务器窗口 一些列属性
//主面板
JPanel priorPanel = new JPanel(new BorderLayout());
//显示消息
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//关闭窗口结束程序
this.setTitle("来自客户端的消息");
this.setSize(500, 500);
jTextArea = new JTextArea();//显示客户端的信息
jTextArea.setEditable(false);//不可以修改 面板信息
JScrollPane clientInformation = new JScrollPane(jTextArea);
priorPanel.add(clientInformation,BorderLayout.CENTER);
//写一个发布公告的窗口
JPanel sendPanel = new JPanel();//默认流式布局
JScrollPane jScrollPane = new JScrollPane(jTextSend);
jScrollPane.setPreferredSize(new Dimension(400,50));
sendPanel.add(jScrollPane);
sendPanel.add(jButtonSend);
priorPanel.add(sendPanel,BorderLayout.SOUTH);
this.add(priorPanel);
//创建监听发送键
jButtonSend.addActionListener(new ActionListener() {
/*用来对每个客户端发送公告*/
@Override
public void actionPerformed(ActionEvent e) {
String announcement = "来自服务器的公告:"+jTextSend.getText()+"\n";
//把公告发布到聊天界面
jTextArea.append(announcement);
/*使用for循环来遍历每个客户端的对象*/
for(Socket soc:socketsClients){
DataOutputStream dataOutputStream = null;
try {
dataOutputStream = new DataOutputStream(soc.getOutputStream());
dataOutputStream.writeUTF(announcement+"\n");
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
jTextSend.setText(null);
}
});
this.setVisible(true);
//===================================================================================
//以上为窗口的创建,下面为功能的实现
try {
//创建服务器对象 设置端口
ServerSocket serverSocket = new ServerSocket(9998);
//使用无限循环来不停接受客户端的连接
while(true){
Socket socketClient = serverSocket.accept();
//接收到一个就向集合加入一个客户端
socketsClients.add(socketClient);
//就收一个连接就在后台提示一遍
System.out.println("有"+socketsClients.size()+"个邓钦文连接到服务器!");
//然后启动该客户端的线程 线程的实现则需要我们使用内部类
new SocketThread(socketClient).start();
}
//处理客户端输入的东西
} catch (IOException e) {
e.printStackTrace();
System.out.println("服务器启动失败!");
}
}
//创建监听
//创建内部类 线程
class SocketThread extends Thread{
DataInputStream dataInputStream;
Socket socket;
/*构建方法 传入当前客户的连接*/
public SocketThread(Socket socket) throws IOException {
/*接受客户端的输入流*/
dataInputStream = new DataInputStream(socket.getInputStream());
this.socket = socket;
}
/*重写run方法 来实现对客户端的操作方法*/
@Override
public void run() {
while(true){
try {
String msg = dataInputStream.readUTF();
/*读取客户端的信息 并打印到自己的窗口上*/
jTextArea.append(msg+"\n");
//给每一个客户端发送信息 实现群聊的效果
for(Socket soc:socketsClients){
DataOutputStream dataOutputStream = new DataOutputStream(soc.getOutputStream());
dataOutputStream.writeUTF(msg+"\n");
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("读取失败!");
socketsClients.remove(socket);
return;
}
}
}
}
}
客户端
登录界面
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.Socket;
public class EnterFrame extends JFrame {
public EnterFrame() throws HeadlessException {
this.setSize(600,500);
this.setTitle("欢迎登录");
this.setResizable(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//关闭窗口结束程序
this.setLocationRelativeTo(this);
JPanel jPanelLast = new JPanel(new GridLayout(4,1));
//头顶部分
JPanel jPanelTop = new JPanel();
JLabel jLabel = new JLabel("欢迎登录");
jLabel.setFont(new Font("宋体",Font.BOLD,30));
//中间部分
JPanel jPanelMiddle = new JPanel(new GridLayout(2,1,0,0));
JPanel jPanelAccount = new JPanel(new FlowLayout());
//账号密码输入端
JLabel jLabelAccount = new JLabel("账号");
JTextField jTextFieldAccount = new JTextField(15);
jPanelAccount.add(jLabelAccount);
jPanelAccount.add(jTextFieldAccount);
JPanel jPanelPassword = new JPanel(new FlowLayout());
JLabel jLabelPassword = new JLabel("密码");
JPasswordField jTextFieldPassword = new JPasswordField(15);
jPanelPassword.add(jLabelPassword);
jPanelPassword.add(jTextFieldPassword);
//底部 登录按钮端
JPanel jPanelFoot = new JPanel();
JPanel jPanelButton = new JPanel(new FlowLayout());
JButton enterButton = new JButton("登录");
JButton signButton = new JButton("注册");
jPanelButton.add(enterButton);
jPanelButton.add(signButton);
jPanelTop.add(jLabel);
jPanelMiddle.add(jPanelAccount);
jPanelMiddle.add(jPanelPassword);
jPanelFoot.add(jPanelButton);
jPanelLast.add(jPanelTop);
jPanelLast.add(jPanelMiddle);
jPanelLast.add(jPanelFoot);
this.add(jPanelLast);
this.setVisible(true);
//================================================================================
//以上为登录界面窗口的设计 可以自己设计
//设计监听事件 来判断用户的读取
enterButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//1.判断不能为空
if(jTextFieldAccount.getText().length()<=0||jTextFieldPassword.getText().length()<=0) {
JOptionPane.showMessageDialog(null, "输入的账号或密码为空!");
return;
}
//2.输入只能为数字与字母
if((jTextFieldAccount.getText().matches("[a-zA-Z0-9]*")&&jTextFieldPassword.getText().matches("[a-zA-Z0-9]*"))==false){
JOptionPane.showMessageDialog(null,"输入的账号或密码只能为字母和数字");
return;
}
//如果都满足了登录成功 创建 客户端对象 输入服务器地址 与端口 关闭登录界面
try {
Socket socket = new Socket("127.0.0.1", 9998);
new ChatFrame(jTextFieldAccount.getText(),socket);
dispose();//关闭了登录窗口
} catch (IOException ioException) {
ioException.printStackTrace();
JOptionPane.showMessageDialog(null,"无法连接服务器");
}
}
});
}
}
聊天界面
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.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ChatFrame extends JFrame {
JTextArea jTextArea;
public ChatFrame(String account , Socket socket) throws HeadlessException {
this.setSize(700, 600);
this.setTitle("欢迎来到"+account+"聊天室");
this.setResizable(true);
this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
JPanel jPanelEnd = new JPanel( new BorderLayout());//最终面板
jTextArea = new JTextArea();
jTextArea.setEditable(false);
JScrollPane jScrollPane1 = new JScrollPane(jTextArea);
jPanelEnd.add(jScrollPane1,BorderLayout.CENTER);
JPanel jPanelInput = new JPanel();
JTextArea jTextArea2 = new JTextArea(5,40);
//带滑动的窗口
JScrollPane jScrollPane2 = new JScrollPane(jTextArea2);//不能通过add加入,只能通过构造方法加入,这样不会出现不显示的问题
jPanelInput.add(jScrollPane2);
JButton jButtonSend = new JButton("发送");
jPanelInput.add(jButtonSend);
jPanelEnd.add(jPanelInput,BorderLayout.SOUTH);
this.add(jPanelEnd);
try {
new ClientThread(socket).start();
} catch (IOException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null,"发送错误");
}
//关闭窗口,显示登录窗口
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("你确定要关闭我妈?");
int res = JOptionPane.showConfirmDialog(null,"你确定要关闭我吗","操作提示",JOptionPane.OK_CANCEL_OPTION);
if(res==0){
//点击确定
new EnterFrame();
dispose();//关闭当前对象
}
}
});
//监听发送事件
try {
DataOutputStream socketOutput = new DataOutputStream(socket.getOutputStream());
String message = socketOutput.toString();
jButtonSend.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(message.length()<=0){
JOptionPane.showMessageDialog(null,"输入消息不能为空!");
return;
}
String msg = account+" "+ new SimpleDateFormat("yyyy-MM HH:mm:ss").format(new Date()) +"\n"+jTextArea2.getText();
try {
socketOutput.writeUTF(msg);
System.out.println(msg);
jTextArea2.setText("");
} catch (IOException ioException) {
ioException.printStackTrace();
JOptionPane.showMessageDialog(null,"发生错误");
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
this.setVisible(true);
//=======================================================================================
//窗口的创建以及窗口功能的实现
}
//创建内部列 创建客户端的线程,解决多个聊天窗口同时实现
class ClientThread extends Thread{
DataInputStream inputStream;
Socket socket;
/*构造方法*/
public ClientThread(Socket socket) throws IOException {
inputStream = new DataInputStream(socket.getInputStream());
this.socket = socket;
}
@Override
public void run() {
try {
while(true){
/*把接受的到信息添加到窗口中*/
String msg = inputStream.readUTF();
jTextArea.append(msg+"\n");
}
} catch (IOException e) {
e.printStackTrace();
return;
}
}
}
}
启动程序
public class RunOfServer {
public static void main(String[] args) {
new Server();
}
}
public class RunOfClient {
public static void main(String[] args) {
new EnterFrame();
}
}
3.实现结果
1.启动服务器
2.尝试启动 3个客户端 并登录
3.测试消息发送接收以及公告发送功能.
4.总结
本次聊天室1.0的简单实现使用了 Java 的网络编程 IO 线程 异常的抛出 集合 内部类 GUI等
希望指出错误并提供改进意见