| 交易類型 | 請求格式 | 回應格式 |
| 查詢餘額 | check-帳號-密碼 | +Your account balance is: 1000 / -Wrong account or password |
| 存款 | credit-帳號-金額 | +Transaction complete / -Error |
| 提款 | debit-帳號-密碼-金額 | +Transaction complete / -Wrong password or haven't enough money |
| 轉帳 | virement-帳號-密碼-目標帳號-金額 | +Transaction complete / -Otherside account not exist |
關鍵程式碼片段
伺服器主類別
public class BankServer {
private static int PORT = 8848;
public static void main(String[] args) throws IOException {
if (args.length > 0) PORT = Integer.parseInt(args[0]);
try (ServerSocket server = new ServerSocket(PORT)) {
System.out.println("Bank server started on port " + PORT);
while (true) {
Socket client = server.accept();
new ServerThread(client).start(); // 每連線一執行緒
}
}
}
}
交易處理核心 (以轉帳為例)
private String virement(String request) {
// 解析:virement-帳號-密碼-目標帳號-金額
String[] parts = request.split("-", 5);
String fromAcc = parts[1];
String pwd = parts[2];
String toAcc = parts[3];
BigDecimal amount = new BigDecimal(parts[4]);
// 檢查餘額
String sqlBalance = "SELECT Balance FROM Account WHERE AccountNo=? AND Password=?";
try (PreparedStatement ps = conn.prepareStatement(sqlBalance)) {
ps.setString(1, fromAcc);
ps.setString(2, pwd);
ResultSet rs = ps.executeQuery();
if (!rs.next() || amount.compareTo(rs.getBigDecimal("Balance")) > 0) {
return "-Wrong password or haven't enough money";
}
}
// 檢查目標帳號存在
String sqlExist = "SELECT 1 FROM Account WHERE AccountNo=?";
try (PreparedStatement ps = conn.prepareStatement(sqlExist)) {
ps.setString(1, toAcc);
if (!ps.executeQuery().next()) {
return "-Otherside account not exist";
}
}
// 扣款 + 入款 + 記錄 (需事務)
conn.setAutoCommit(false);
try {
updateBalance(fromAcc, amount.negate());
updateBalance(toAcc, amount);
recordTransaction(fromAcc, amount.negate(), toAcc);
recordTransaction(toAcc, amount, fromAcc);
conn.commit();
return "+Transaction complete";
} catch (SQLException e) {
conn.rollback();
throw e;
} finally {
conn.setAutoCommit(true);
}
}
資料庫結構 (MySQL)
CREATE TABLE Account (
AccountNo VARCHAR(20) PRIMARY KEY,
Password VARCHAR(20) NOT NULL,
Balance DECIMAL(15,2) DEFAULT 0
);
CREATE TABLE Record (
Id BIGINT AUTO_INCREMENT PRIMARY KEY,
AccountNo VARCHAR(20),
TransDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
TransMoney DECIMAL(15,2),
TransNo VARCHAR(20) -- 轉帳對方帳號
);
設定檔 (config.data)
com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/bank?user=root&password=your_password&useUnicode=true&characterEncoding=UTF-8
安全性與健壯性增強建議
| 問題 | 改善方案 |
| 明文傳輸密碼 | SSL/TLS 加密通道 (JSSE) 或至少 SHA-256 雜湊後傳輸 |
| SQL 注入風險 | 全面使用 PreparedStatement 參數化查詢 |
| 執行緒無限制 | 執行緒池 (ExecutorService) 限制並發數 |
| 資料庫連線洩漏 | 連線池 (HikariCP/Druid) + try-with-resources |
| 無心跳/閒置斷線 | SO_TIMEOUT / 應用層心跳機制 |
| 協議無版本/擴展性 | 採用 JSON/Protobuf + 版本欄位 |
結語
這個銀行伺服器範例展示了 Socket 程式設計、多執行緒模型、JDBC 事務處理、協議解析的核心技術。實際生產系統需引入安全框架、連線池、監控、日誌、叢集等企業級基礎設施,建議採用 Netty、Spring Boot、gRPC 等成熟框架加速開發。
- 上一篇文章: Java URL 下載遠端檔案完整實作:HTTP/HTTPS/FTP 通用方案
- 下一篇文章: Struts Token 機制防重複提交:完整實作與原理深度解析
Copyright ©
程式碼之家 All Rights Reserved