💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
多线程服务器:TCP协议是C/S模式,即客户端/服务器模式,一次只能处理一次C/S,但是引入多线程同一台服务器可以处理多个客户端的请求,因为服务器把接收到客户端的请求交给了其它的子线程,这样一来,服务器就总是处于等待状态。<br/> *模拟服务器和客户端方法:分在使用eclipse创建两个独立的工作空间,使用一个空间先运行TCP服务端程序 **`ChatRoomServer.java`**,再使用另一个空间运行TCP客户端程序 **`LinkServerFrame.java`**。* 下面一共用到四个类,它们作用如下: ``` net/server/CharRoomServer.java 服务器(最先运行该程序) net/client/LinkServerFrame.java 客户登录服务器的窗口,是客户端程序的入口,由他调用下面的ClientFrame类 net/client/ChatRoomClient.java 客户端核心程序,负责与服务器进行交互 net/client/ClientFrame.java 客户聊天窗口,由他调用上面的ChatRoomClient类 ``` ![](https://img.kancloud.cn/f3/3f/f33fd4aaf2f72e20d06d93de39dcf450_812x281.png) **`net/server/ChatRoomServer.java`** (服务器) ```java package net.server; import java.io.*; import java.net.*; import java.util.*; /** * 服务器 */ public class ChatRoomServer { private ServerSocket serverSocket; // 服务器端套接字 private HashSet<Socket> allSockets; // 存储所有客户端的套接字 public ChatRoomServer() { try { serverSocket = new ServerSocket(4569); // 服务器端口 } catch (IOException e) { e.printStackTrace(); } allSockets = new HashSet<Socket>(); } /** * 等待来自客户端的请求,并将客户添加到allSockets中 * @throws IOException */ public void startService() throws IOException { System.out.println("服务器已开启!"); while (true) { Socket socket = serverSocket.accept(); // 等待来自客户端的请求,如果一直没有来自客户端的请求,则一直阻塞 allSockets.add(socket); System.out.println("在线人数:" + allSockets.size()); // 服务器将每个客户的请求交给下面的线程,所以服务器总是处于等待状态 new ServerThread(socket).start(); } } /** * 内部类,当有客户端进行请求时,服务器交给该线程去处理 */ private class ServerThread extends Thread { Socket socket; public ServerThread(Socket socket) { this.socket = socket; } public void run() { BufferedReader br = null; try { // 获取客户端的发送的信息 br = new BufferedReader(new InputStreamReader(socket.getInputStream())); while (true) { String str = br.readLine(); if (str.contains("%EXIT%")) { allSockets.remove(socket); sendMessageTOAllClient(str.split(":")[1] + " 已退出聊天室!"); socket.close(); System.out.println("在线人数:" + allSockets.size()); return; } sendMessageTOAllClient(str); } } catch (IOException e) { e.printStackTrace(); } } /** * 发送消息给所有客户端 * @param message String, 服务器要发送的消息 * @throws IOException */ public void sendMessageTOAllClient(String message) throws IOException { for (Socket s : allSockets) { PrintWriter pw = new PrintWriter(s.getOutputStream()); pw.println(message); pw.flush(); } } } public static void main(String[] args) { try { new ChatRoomServer().startService(); } catch (IOException e) { e.printStackTrace(); } } } ``` **`net/client/LinkServerFrame`** (客户端——用户登录服务器窗口,客户端程序入口) ```java package net.client; import javax.swing.*; import javax.swing.border.EmptyBorder; import java.awt.Font; import java.awt.event.*; /** * 服务器登录窗口,这是客户端程序的入口 * */ public class LinkServerFrame extends JFrame { private static final long serialVersionUID = 1L; private JPanel contentPane; private JLabel lblIP; private JLabel lblUserName; private JTextField tfIP; private JTextField tfUserName; private JButton btnLink; public LinkServerFrame() { setTitle("登录服务器"); setResizable(false); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 390, 150); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); contentPane.setLayout(null); setContentPane(contentPane); lblIP = new JLabel("服务器地址"); lblIP.setFont(new Font("Serif", Font.PLAIN, 14)); lblIP.setBounds(20, 15, 100, 15); contentPane.add(lblIP); tfIP = new JTextField("127.0.0.1"); tfIP.setBounds(121, 13, 242, 21); contentPane.add(tfIP); tfIP.setColumns(10); lblUserName = new JLabel("姓名"); lblUserName.setFont(new Font("Serif", Font.PLAIN, 14)); lblUserName.setBounds(60, 40, 60, 15); contentPane.add(lblUserName); tfUserName = new JTextField(); tfUserName.setBounds(121, 42, 242, 21); contentPane.add(tfUserName); tfUserName.setColumns(10); btnLink = new JButton("连接"); btnLink.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { do_btnLink_actionPerformed(e); } }); btnLink.setFont(new Font("Serif", Font.PLAIN, 14)); btnLink.setBounds(140, 80, 120, 23); contentPane.add(btnLink); } public static void main(String[] args) { LinkServerFrame linkServerFrame = new LinkServerFrame(); linkServerFrame.setVisible(true); } protected void do_btnLink_actionPerformed(ActionEvent e) { if (!tfIP.getText().equals("") && !tfUserName.getText().equals("")) { dispose(); ClientFrame clientFrame = new ClientFrame(tfIP.getText().trim(), tfUserName.getText().trim()); clientFrame.setVisible(true); } else { JOptionPane.showMessageDialog(null, "聊天姓名不能为空!", "提示框", JOptionPane.WARNING_MESSAGE); } } } ``` **`net/client/ChatRoomClient.java`** (客户端核心程序) ```java package net.client; import java.io.*; import java.net.*; /** * 客户端。向服务器发送消息、接收服务器返回的消息、关闭客户端与服务器的连接 * */ public class ChatRoomClient { private Socket socket; // 客户端套接字 private BufferedReader bufferReader; // 用于接收服务器的消息 private PrintWriter pWriter; // 用于向服务器发送消息 /** * @param host String, 服务器IP地址 * @param port int, 服务器端口 * @throws UnknownHostException * @throws IOException */ public ChatRoomClient(String host, int port) throws UnknownHostException, IOException { socket = new Socket(host, port); bufferReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); pWriter = new PrintWriter(socket.getOutputStream()); } /** * 向服务器发送消息 * * @param str String, 要发送的文本消息。 */ public void sendMessage(String str) { pWriter.println(str); pWriter.flush(); } /** * 接收服务器返回的消息 * * @return String */ public String reciveMessage() { try { return bufferReader.readLine(); } catch (IOException e) { e.printStackTrace(); } return null; } /** * 关闭与服务器的连接 */ public void close() { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` **`net/client/ClientFrame.java`** (客户端——用户聊天窗口) ```java package net.client; import javax.swing.*; import java.awt.event.*; import java.io.IOException; import java.net.UnknownHostException; import java.text.SimpleDateFormat; import java.util.Date; import javax.swing.SwingConstants; import javax.swing.border.EmptyBorder; /** * 客户端聊天窗口 */ public class ClientFrame extends JFrame { private static final long serialVersionUID = 1L; private JPanel contentPane; private JLabel lblUserName; private JTextField tfMessage; private JButton btnSend; private JTextArea textArea; private String userName; private ChatRoomClient client; /** * * @param ip String, 服务器的IP地址 * @param userName String, */ public ClientFrame(String ip, String userName) { this.userName = userName; try { client = new ChatRoomClient(ip, 4569); } catch (UnknownHostException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } ReadMessageThread messageThread = new ReadMessageThread(); messageThread.start(); // 启动线程 init(); addListener(); } /** * 初始化窗口 */ private void init() { setTitle("客户端"); setResizable(false); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); setBounds(100, 100, 450, 300); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); contentPane.setLayout(null); setContentPane(contentPane); JScrollPane scrollPane = new JScrollPane(); scrollPane.setBounds(5, 5, 434, 229); contentPane.add(scrollPane); textArea = new JTextArea(); scrollPane.setViewportView(textArea); textArea.setEditable(false); JPanel panel = new JPanel(); panel.setBounds(5, 235, 434, 32); contentPane.add(panel); panel.setLayout(null); lblUserName = new JLabel(userName); lblUserName.setHorizontalAlignment(SwingConstants.TRAILING); lblUserName.setBounds(2, 4, 55, 22); panel.add(lblUserName); tfMessage = new JTextField(); tfMessage.setBounds(62, 5, 274, 22); tfMessage.setColumns(10); panel.add(tfMessage); btnSend = new JButton("发送"); btnSend.setBounds(336, 4, 93, 23); panel.add(btnSend); tfMessage.validate(); // 刷新组件 } /** * 为按钮添加事件 */ private void addListener() { btnSend.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Date date = new Date(); SimpleDateFormat df = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); client.sendMessage("\t" + userName + " " + df.format(date) + "\n" + tfMessage.getText()); tfMessage.setText(""); } }); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent atg0) { int op = JOptionPane.showConfirmDialog(ClientFrame.this, "你确认要退出吗?", "退出", JOptionPane.YES_NO_OPTION); if (op == JOptionPane.YES_OPTION) { client.sendMessage("%EXIT%:" + userName); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } client.close(); System.exit(0); } } }); } /** * 这是一个内部类,负责创建线程,使用该线程接收服务端返回的消息,并追加到文本域中 * */ private class ReadMessageThread extends Thread { public void run() { while (true) { String str = client.reciveMessage(); textArea.append(str + "\n"); } } } } ``` 先运行ChatRoomServer,在运行三个或三个以上的LinkServerFrame,效果如下: ![](https://img.kancloud.cn/ac/87/ac87947685b1526417cb4867bd557682_1380x611.gif)