目录
一、Socket简介
Socket是计算机网络通信中的一种机制,它允许在不同计算机之间或同一计算机的不同进程之间进行数据通信。在Java中,Socket编程是通过java.net.Socket
和java.net.ServerSocket
类实现的,这些类提供了底层的网络通信支持。
1.Socket工作原理
Socket是通过网络连接传输数据的一种方式,它提供了一个端到端的通信管道,允许客户端和服务器之间进行数据交换。服务器端将负责处理客户端的连接请求,并转发消息。客户端将允许用户连接到服务器并发送消息。
2.主要方法和概念
Socket(String host, int port); | 创建客户端Socket,连接到指定的主机和端口。 |
Socket.accept( ); | 在服务器Socket上调用,用于接受客户端连接请求并返回一个新的Socket对象。 |
Socket.getInputStream( ); | 获取Socket的输入流。 |
Socket.getOutputStream( ); | 获取Socket的输出流。 |
Socket.close( ); | 关闭Socket连接。 |
ServerSocket(int port); | 创建服务器Socket,绑定到指定的端口。 |
ServerSocket.accept( ); | 等待客户端连接请求,返回一个新的Socket对象。 |
ServerSocket.close( ); | 关闭ServerSocket。 |
二.Socket架构图
三.项目类图
四.技术栈
- Java:作为主要编程语言。
- Socket编程:用于在客户端和服务器之间建立连接。
- 多线程:处理多个客户端连接。
- Swing:用于创建基本的图形用户界面(GUI)。
五.服务器过程
1. 服务器启动
public static void main(String[] args) throws IOException {
Server server = new Server(9999);
server.execute();
}
在main
方法中,首先创建了一个Server
对象,并传入了端口号9999来初始化服务器。然后调用execute
方法来启动服务器。
2. execute方法
public void execute() throws IOException {
while (true) {
System.out.println("服务器启动,等待连接...");
// 监听客户端套接字,若没有客户端连接,则代码不会继续执行。
Socket socket = serverSocket.accept();
System.out.println("客户端连接成功");
new MyServerThread(socket).start();
}
}
2.1 监听客户端连接
serverSocket.accept()
:在execute
方法中,serverSocket.accept()
会一直阻塞,直到有客户端连接到服务器。一旦有客户端连接,它会返回一个新的Socket
对象,该对象代表与客户端的通信通道。
2.2 处理客户端连接
Socket socket = serverSocket.accept();
:当客户端连接成功后,会打印出"客户端连接成功"。然后,服务器会为每个连接创建一个新的线程,用于处理客户端发送的消息和向客户端发送消息。
new MyServerThread(socket).start();
:创建并启动一个MyServerThread
线程,将连接的Socket
对象作为参数传入,用于处理客户端的数据交换。
3. MyServerThread 线程
public class MyServerThread extends Thread {
private final Socket socket;
private DataInputStream dis;
private DataOutputStream dos;
public MyServerThread(Socket socket) {
this.socket = socket;
}
public void run(){...}
//其他方法
}
六.客户端过程
1. 客户端连接与初始化
public class Client {
private final Socket socket;
private final ChatJFrame chatJFrame;
private final String userName;
private DataOutputStream dos;
public Client(String ip, int port, String userName) throws IOException {
this.socket = new Socket(ip, port);
this.chatJFrame = new ChatJFrame(this, userName);
this.userName = userName;
}
//其他代码
}
- Socket连接:客户端通过
new Socket(ip, port)
连接到指定的服务器地址和端口号。 - ChatJFrame:客户端使用
ChatJFrame
类创建一个聊天界面窗口,传入当前客户端对象和用户名。
2. 创建线程池
public static ThreadPoolExecutor getPool() {
// 1. corePoolSize: 核心线程数
// 2. maximumPoolSize: 最大线程数
// 3. keepAliveTime: 空闲线程存活时间
// 4. 3的单位
// 5. workQueue: 任务队列
// 6. threadFactory: 线程工厂,表示使用默认的线程工厂来创建新线程。
// 7. handler: 拒绝策略,使用了 ThreadPoolExecutor.AbortPolicy(),表示当线程池无法接受新的任务时,将抛出一个 RejectedExecutionException 异常。
return new ThreadPoolExecutor(
3,
Runtime.getRuntime().availableProcessors() * 2,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
}
- 线程池初始化:创建一个线程池,用于优化客户端的多线程处理。这里使用了固定大小为3的核心线程数,最大线程数是处理器核心数的两倍,任务队列大小为2,当线程池无法接受新任务时,使用了
AbortPolicy
拒绝策略。
3. 客户端连接方法
public static void ClientLogin(String userName) throws IOException {
Client client = new Client("127.0.0.1", 9999, userName);
client.execute();
}
- ClientLogin方法:用于创建客户端对象,并执行
execute()
方法。
4. 客户端执行方法
public void execute() {
sendMessage2Server("USER_NAME" + userName + "USER_NAME");
chatJFrame.buildChatWindow();
//线程池优化
//开启线程
getPool().submit(new MyClientThread(socket, chatJFrame));
}
- execute方法:
- 发送用户名给服务器。
- 构建聊天窗口界面。
- 使用线程池提交一个
MyClientThread
线程,传入服务器的Socket和聊天界面对象。
七.完整代码
github地址:完整代码