Socket支持下的点对点通信的实现(服务端与客户端注释)

发布时间 2023-11-20 17:45:42作者: Cloudservice

服务端程序

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;

import javax.swing.*;

public class Server extends JFrame implements ActionListener {
	ServerSocket ss = null; // ServerSocket对象
	Socket s = null; // Socket对象
	private DataInputStream is = null; // 输入流
	private DataOutputStream os = null; // 输出流
	private boolean connected = false; // 是否连接标志
	JLabel ipLabel = new JLabel(); // 显示本机IP地址的标签
	JLabel portLabel = new JLabel("端口"); // 显示端口号的标签
	JTextField portTextField = new JTextField("8000"); // 端口号输入框
	JButton listenButton = new JButton("监听"); // 监听按钮
	JButton closeButton = new JButton("关闭"); // 关闭按钮
	JPanel panel = new JPanel(); // 包含各种控件的面板
	JTextField inputTextField = new JTextField(); // 输入框
	JTextArea contentTextArea = new JTextArea(); // 显示接收到的数据的文本区域
	Thread recvThread = new Thread(new RecvThread()); // 接收数据的线程

	// 构造函数
	public Server() {
		setTitle("服务器端"); // 窗口标题
		setLayout(new BorderLayout()); // 设置布局为边界布局
		panel.setLayout(new FlowLayout()); // 面板使用流式布局
		try {
			// 获取本机IP地址并显示
			ipLabel.setText("本机IP信息:" + InetAddress.getLocalHost().toString());
		} catch (HeadlessException e) {
			e.printStackTrace();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		panel.add(ipLabel); // 添加IP地址标签到面板
		panel.add(portLabel); // 添加端口号标签到面板
		panel.add(portTextField); // 添加端口号输入框到面板
		panel.add(listenButton); // 添加监听按钮到面板
		panel.add(closeButton); // 添加关闭按钮到面板
		add(panel, BorderLayout.NORTH); // 将面板添加到窗口北部
		add(new JScrollPane(contentTextArea), BorderLayout.CENTER); // 将文本区域添加到窗口中央,可以滚动
		add(inputTextField, BorderLayout.SOUTH); // 将输入框添加到窗口南部
		listenButton.addActionListener(this); // 监听按钮添加事件监听器
		closeButton.addActionListener(this); // 关闭按钮添加事件监听器
		inputTextField.addActionListener(this); // 输入框添加事件监听器

		addWindowListener(new WindowAdapter() { // 窗口关闭事件监听器,关闭程序时同时断开连接
			@Override
			public void windowClosing(WindowEvent e) {
				disconnect();
			}
		});
		setSize(500, 300); // 窗口大小
		setLocationRelativeTo(null); // 将窗口位于屏幕的中央
		setVisible(true); // 显示窗口
	}

	// 开始监听
	public void start() {
		try {
			int port = Integer.parseInt(portTextField.getText()); // 获取输入的端口号
			connected = true; // 标记已连接
			ss = new ServerSocket(port); // 创建ServerSocket对象
			s = ss.accept(); // 等待客户端连接
			os = new DataOutputStream(s.getOutputStream()); // 创建输出流
			is = new DataInputStream(s.getInputStream()); // 创建输入流
			recvThread.start(); // 启动接收数据的线程
		} catch (IOException e) {
		} finally {
			try {
				ss.close(); // 关闭ServerSocket
			} catch (IOException e) {
			}
		}
	}

	// 断开连接
	public void disconnect() {
		try {
			if (os != null)
				os.close(); // 关闭输出流
			if (is != null)
				is.close(); // 关闭输入流
			if (s != null)
				s.close(); // 关闭Socket
		} catch (IOException e) {
		}
		System.exit(0); // 退出程序
	}

	// 使用内部类创建接收数据线程
	private class RecvThread implements Runnable {
		public void run() {
			try {
				while (connected) { // 如果连接上了
					String str = is.readUTF(); // 接收客户端发送的数据
					contentTextArea.setText(contentTextArea.getText() + "client:" + str + '\n'); // 在文本区域显示接收到的数据
				}
			} catch (EOFException e) { // 客户端关闭了连接
				contentTextArea.setText("Client closed!");
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
	}

	// 事件处理函数
	public void actionPerformed(ActionEvent e) {
		// 如果按下的是监听按钮
		if (e.getSource() == listenButton) {
			listenButton.setEnabled(false); // 禁用监听按钮
			start(); // 开始监听
		} else if (e.getSource() == closeButton) { // 如果按下的是关闭按钮
			disconnect(); // 断开连接
		} else if (e.getSource() == inputTextField) { // 如果按下的是输入框回车键
			String str = inputTextField.getText().trim(); // 获取输入框中的文本
			inputTextField.setText(""); // 清空输入框
			try {
				os.writeUTF(str); // 发送数据给客户端
				os.flush(); // 刷新输出流
				contentTextArea.setText(contentTextArea.getText() + "server:" + str + '\n'); // 在文本区域显示发送的数据
			} catch (IOException e1) {
			}
		}
	}

	// 主函数,创建窗口对象
	public static void main(String[] args) {
		new Server();
	}
}

客户端程序

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;

import javax.swing.*;

public class Client extends JFrame implements ActionListener {
	Socket s = null; // Socket对象
	DataOutputStream os = null; // 输出流
	DataInputStream is = null; // 输入流
	private boolean connected = false; // 是否连接标志
	JLabel ipLabel = new JLabel("IP"); // 显示IP地址的标签
	JTextField ipTextField = new JTextField("127.0.0.1"); // IP地址输入框
	JLabel portLabel = new JLabel("端口"); // 显示端口号的标签
	JTextField portTextField = new JTextField("8000"); // 端口号输入框
	JButton connectButton = new JButton("连接"); // 连接按钮
	JButton closeButton = new JButton("关闭"); // 关闭按钮
	JPanel panel = new JPanel(); // 包含各种控件的面板
	JTextField inputTextField = new JTextField(); // 输入框
	JTextArea contentTextArea = new JTextArea(); // 显示接收到的数据的文本区域
	Thread recvThread = new Thread(new RecvThread()); // 接收数据的线程

	// 构造函数
	public Client() {
		setTitle("客户端"); // 窗口标题
		setLocation(400, 300); // 窗口位置
		setSize(500, 500); // 窗口大小
		setLayout(new BorderLayout()); // 设置布局为边界布局
		panel.setLayout(new FlowLayout()); // 面板使用流式布局
		panel.add(ipLabel); // 添加IP地址标签到面板
		panel.add(ipTextField); // 添加IP地址输入框到面板
		panel.add(portLabel); // 添加端口号标签到面板
		panel.add(portTextField); // 添加端口号输入框到面板
		panel.add(connectButton); // 添加连接按钮到面板
		panel.add(closeButton); // 添加关闭按钮到面板
		add(panel, BorderLayout.NORTH); // 将面板添加到窗口北部
		add(new JScrollPane(contentTextArea), BorderLayout.CENTER); // 将文本区域添加到窗口中央,可以滚动
		add(inputTextField, BorderLayout.SOUTH); // 将输入框添加到窗口南部
		connectButton.addActionListener(this); // 连接按钮添加事件监听器
		closeButton.addActionListener(this); // 关闭按钮添加事件监听器
		inputTextField.addActionListener(this); // 输入框添加事件监听器

		addWindowListener(new WindowAdapter() { // 窗口关闭事件监听器,关闭程序时同时断开连接
			@Override
			public void windowClosing(WindowEvent e) {
				disconnect();
			}
		});
		setSize(500, 300); // 窗口大小
		setLocationRelativeTo(null); // 将窗口位于屏幕的中央
		setVisible(true); // 显示窗口
	}

	// 连接服务器
	public void connect() {
		try {
			String host, port;
			host = ipTextField.getText(); // 获取输入的IP地址
			port = portTextField.getText(); // 获取输入的端口号
			s = new Socket(host, Integer.parseInt(port)); // 创建Socket对象并连接服务器
			os = new DataOutputStream(s.getOutputStream()); // 创建输出流
			is = new DataInputStream(s.getInputStream()); // 创建输入流
			connected = true; // 标记已连接
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 断开连接
	public void disconnect() {
		try {
			if (os != null)
				os.close(); // 关闭输出流
			if (is != null)
				is.close(); // 关闭输入流
			if (s != null)
				s.close(); // 关闭Socket
		} catch (IOException e) {
		}
		System.exit(0); // 退出程序
	}

	// 使用内部类创建接收数据线程
	private class RecvThread implements Runnable {
		public void run() {
			try {
				while (connected) { // 如果连接上了
					String str = is.readUTF(); // 接收服务器发送的数据
					contentTextArea.setText(contentTextArea.getText() + "server:" + str + '\n'); // 在文本区域显示接收到的数据
				}
			} catch (EOFException e) { // 服务器关闭了连接
				contentTextArea.setText("Server closed!");
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	// 事件处理函数
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == connectButton) { // 如果按下的是连接按钮
			connectButton.setEnabled(false); // 禁用连接按钮
			connect(); // 连接服务器
			recvThread.start(); // 启动接收数据的线程
		} else if (e.getSource() == closeButton) { // 如果按下的是关闭按钮
			disconnect(); // 断开连接
		} else if (e.getSource() == inputTextField) { // 如果按下的是输入框回车键
			String str = inputTextField.getText().trim(); // 获取输入框中的文本
			inputTextField.setText(""); // 清空输入框
			try {
				os.writeUTF(str); // 发送数据给服务器
				os.flush(); // 刷新输出流
				contentTextArea.setText(contentTextArea.getText() + "client:" + str + '\n'); // 在文本区域显示发送的数据
			} catch (IOException e1) {
			}
		}
	}

	// 主函数,创建窗口对象
	public static void main(String[] args) {
		new Client();
	}
}