如何通过Angular请求停止Spring Boot中运行的ServerSocket?
解决方案:通过API请求终止Spring Boot中的ServerSocket服务
你的核心问题有两个:一是当前startServer()控制器方法是同步执行的,启动服务器后会一直卡在while(true)循环里,占用Spring Boot的请求处理线程,导致后续的停止请求根本无法被处理;二是你的ServerSocket服务没有设计可外部触发的终止机制。下面是具体的修复方案:
1. 重构Server类,添加可终止的控制逻辑
首先要把ServerSocket实例和循环控制变量提取为类成员(避免用static方法引发的线程安全问题),用volatile关键字保证多线程下的变量可见性,确保停止信号能被监听线程及时感知:
import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; public class Server { private static final int PORT = 2014; private ServerSocket serverSocket; private ArrayList<Room> listRooms; // 用volatile保证多线程下的可见性,控制循环是否继续 private volatile boolean isRunning = false; public void start() throws IOException { serverSocket = new ServerSocket(PORT); Log.log("Server initialized on port " + PORT); listRooms = new ArrayList<>(); Room room = new Room("Principal"); addRoom(room); isRunning = true; // 启动监听循环 while (isRunning) { try { Socket socket = serverSocket.accept(); Log.log("Established connection with " + socket.getInetAddress().getHostAddress()); new Thread(new User(socket, room)).start(); } catch (IOException e) { // 如果是因为serverSocket被关闭导致的异常,说明是正常终止,无需报错 if (!isRunning) { Log.log("Server stopped gracefully"); } else { Log.log("Server error: " + e.getMessage()); } } } } public void stop() throws IOException { isRunning = false; // 关闭ServerSocket会中断accept()方法,抛出IOException,从而退出循环 if (serverSocket != null && !serverSocket.isClosed()) { serverSocket.close(); } Log.log("Server shutdown requested"); } public boolean isRunning() { return isRunning; } private void addRoom(Room room) { listRooms.add(room); } // 保留你的Room、User等其他类的实现 }
2. 修改Spring Boot控制器,异步启动服务器并保存实例
现在需要把Server实例保存为控制器的成员,并且异步启动服务器,避免阻塞请求处理线程,这样后续的停止请求才能被正常处理:
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; @RestController @RequestMapping("/api/server") public class ServerController { // 保存Server实例,用于后续停止操作 private Server server; @PostMapping("/start") public String startServer() { if (server != null && server.isRunning()) { return "Server is already running"; } try { server = new Server(); // 用异步线程启动服务器,避免阻塞控制器线程 new Thread(server::start).start(); return "Server started successfully"; } catch (IOException e) { return "Failed to start server: " + e.getMessage(); } } @PostMapping("/stop") public String stopServer() { if (server == null || !server.isRunning()) { return "Server is not running"; } try { server.stop(); server = null; // 重置实例,允许重新启动 return "Server stopped successfully"; } catch (IOException e) { return "Failed to stop server: " + e.getMessage(); } } }
注意:这里用
@PostMapping而非@GetMapping,因为启动/停止服务器是修改系统状态的操作,符合RESTful规范;同时给Server类添加了isRunning()方法来暴露运行状态,避免重复启动。
3. Angular端发送请求的示例
在Angular中,你可以通过HttpClient来调用这两个控制接口,实现前端的启动/停止操作:
服务层代码
import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class ServerService { private baseUrl = 'http://localhost:8080/api/server'; // 替换为你的Spring Boot端口 constructor(private http: HttpClient) { } startServer() { return this.http.post(`${this.baseUrl}/start`, {}); } stopServer() { return this.http.post(`${this.baseUrl}/stop`, {}); } }
组件代码
import { Component } from '@angular/core'; import { ServerService } from './server.service'; @Component({ selector: 'app-server-control', template: ` <button (click)="start()" [disabled]="isRunning">Start Server</button> <button (click)="stop()" [disabled]="!isRunning">Stop Server</button> <p class="status">{{statusMessage}}</p> `, styles: [` .status { margin-top: 10px; color: #666; } button { margin-right: 10px; padding: 8px 16px; } `] }) export class ServerControlComponent { isRunning = false; statusMessage = ''; constructor(private serverService: ServerService) { } start() { this.serverService.startServer().subscribe({ next: (res: any) => { this.statusMessage = res; this.isRunning = true; }, error: (err) => { this.statusMessage = 'Error starting server: ' + err.message; } }); } stop() { this.serverService.stopServer().subscribe({ next: (res: any) => { this.statusMessage = res; this.isRunning = false; }, error: (err) => { this.statusMessage = 'Error stopping server: ' + err.message; } }); } }
额外优化建议
- 优雅关闭用户连接:当前的stop方法仅关闭了ServerSocket,不再接受新连接,但已建立的用户连接会继续运行。如果需要完全终止服务,可以在Server类中维护所有User线程的列表,在stop方法中遍历关闭每个用户的Socket。
- Spring异步支持:可以使用Spring的
@Async注解替代手动创建线程,配合自定义线程池,更好地管理异步任务。 - 状态持久化:如果是多实例部署场景,建议用分布式锁或Redis存储服务器运行状态,避免多实例冲突。
内容的提问来源于stack exchange,提问作者jeeves
相关产品推荐
相关产品推荐

