chore:Use English comments instead of Chinese

This commit is contained in:
david_bai
2025-08-31 23:34:52 +08:00
parent 1aa738425f
commit 5af2e8db37
8 changed files with 178 additions and 167 deletions
+9 -9
View File
@@ -18,7 +18,7 @@ export function useFileTransferHandler({
messages,
putMessageInMs,
}: UseFileTransferHandlerProps) {
// 从 store 中获取状态
// Get state from store
const {
shareContent,
sendFiles,
@@ -100,11 +100,11 @@ export function useFileTransferHandler({
}
} else {
let retryCount = 0;
const maxRetries = 3; // 重试次数
const maxRetries = 3; // Retry count
const findAndDownload = async (): Promise<boolean> => {
retryCount++;
// 🔧 关键修复:使用最新的Store状态,而不是闭包中的旧状态
// 🔧 Key fix: Use the latest Store state instead of the old state in the closure
const { retrievedFiles: latestRetrievedFiles } =
useFileTransferStore.getState();
const fileToDownload = latestRetrievedFiles.find(
@@ -119,20 +119,20 @@ export function useFileTransferHandler({
return false;
};
// 首次尝试
// First attempt
const found = await findAndDownload();
if (!found) {
// 如果没找到,启动重试机制
// If not found, start retry mechanism
const retryWithDelay = async (): Promise<void> => {
while (retryCount < maxRetries) {
await new Promise((resolve) => setTimeout(resolve, 50)); // 固定50ms延迟,因为现在状态应该很快同步
await new Promise((resolve) => setTimeout(resolve, 50)); // Fixed 50ms delay, as the state should sync quickly now
const foundInRetry = await findAndDownload();
if (foundInRetry) {
return;
}
}
// 所有重试都失败了
// All retries failed
putMessageInMs(
messages.text.ClipboardApp.fileNotFoundMsg ||
`File '${meta.name}' not found for download.`,
@@ -140,12 +140,12 @@ export function useFileTransferHandler({
);
};
// 异步执行重试,不阻塞主线程
// Execute retry asynchronously without blocking the main thread
retryWithDelay().catch(console.error);
}
}
},
[messages, putMessageInMs] // 🔧 移除retrievedFiles依赖,因为我们现在直接从Store获取最新状态
[messages, putMessageInMs] // 🔧 Remove retrievedFiles dependency as we now get the latest state directly from Store
);
// Reset function specifically for receiver state (for leave room functionality)
+51 -40
View File
@@ -9,7 +9,7 @@ function format_peopleMsg(template: string, peerCount: number) {
return template.replace("{peerCount}", peerCount.toString());
}
// 移除所有 WebRTC 相关的 props 依赖
// Remove all WebRTC related props dependencies
interface UseRoomManagerProps {
messages: Messages | null;
putMessageInMs: (
@@ -23,7 +23,7 @@ export function useRoomManager({
messages,
putMessageInMs,
}: UseRoomManagerProps) {
// store 获取状态
// Get state from store
const {
shareRoomId,
initShareRoomId,
@@ -45,13 +45,13 @@ export function useRoomManager({
resetSenderApp,
} = useFileTransferStore();
// 加入房间方法 - 直接使用 webrtcService
// Join room method - directly use webrtcService
const joinRoom = useCallback(
async (isSenderSide: boolean, roomId: string) => {
if (!messages) return;
try {
// 如果是发送方且房间ID不是初始ID,需要先创建房间
// If it's the sender side and the room ID is not the initial ID, need to create the room first
if (
isSenderSide &&
activeTab === "send" &&
@@ -77,7 +77,7 @@ export function useRoomManager({
}
}
// 确定实际要加入的房间ID
// Determine the actual room ID to join
const actualRoomId =
isSenderSide && roomId !== initShareRoomId
? roomId
@@ -85,7 +85,7 @@ export function useRoomManager({
? shareRoomId
: roomId;
// 直接调用 service 方法,无需依赖注入
// Directly call the service method without dependency injection
await webrtcService.joinRoom(actualRoomId, isSenderSide);
putMessageInMs(
@@ -94,7 +94,7 @@ export function useRoomManager({
6000
);
// 更新分享链接
// Update share link
if (isSenderSide) {
const link = `${window.location.origin}${window.location.pathname}?roomId=${actualRoomId}`;
setShareLink(link);
@@ -103,7 +103,7 @@ export function useRoomManager({
}
}
} catch (error) {
console.error("[RoomManager] 加入房间失败:", error);
console.error("[RoomManager] Failed to join room:", error);
let errorMsg = messages.text.ClipboardApp.joinRoom.failMsg;
if (error instanceof Error) {
errorMsg =
@@ -125,7 +125,7 @@ export function useRoomManager({
]
);
// 生成分享链接并广播
// Generate share link and broadcast
const generateShareLinkAndBroadcast = useCallback(async () => {
if (!messages || !shareRoomId) return;
@@ -133,25 +133,25 @@ export function useRoomManager({
if (sharePeerCount === 0) {
putMessageInMs(messages.text.ClipboardApp.waitting_tips, true);
} else {
// 直接调用 service 的广播方法
// Directly call the service's broadcast method
await webrtcService.broadcastDataToAllPeers();
}
// 更新分享链接
// Update share link
const link = `${window.location.origin}${window.location.pathname}?roomId=${shareRoomId}`;
setShareLink(link);
} catch (error) {
console.error("[RoomManager] 生成分享链接失败:", error);
putMessageInMs("生成分享链接失败", true);
console.error("[RoomManager] Failed to generate share link:", error);
putMessageInMs("Failed to generate share link", true);
}
}, [messages, putMessageInMs, shareRoomId, sharePeerCount, setShareLink]);
// 接收方离开房间
// Receiver leave room
const handleLeaveReceiverRoom = useCallback(async () => {
if (!messages) return;
try {
// 调用后端 API 离开房间
// Call backend API to leave room
if (webrtcService.receiver.roomId && webrtcService.receiver.peerId) {
await leaveRoom(
webrtcService.receiver.roomId,
@@ -161,42 +161,42 @@ export function useRoomManager({
putMessageInMs(messages.text.ClipboardApp.roomStatus.leftRoomMsg, false);
// 重置接收方状态
// Reset receiver state
resetReceiverState();
// 清理 WebRTC 连接
// Clean up WebRTC connection
await webrtcService.leaveRoom(false);
} catch (error) {
console.error("[RoomManager] 接收方离开房间失败:", error);
putMessageInMs("离开房间失败", true);
console.error("[RoomManager] Receiver failed to leave room:", error);
putMessageInMs("Failed to leave room", true);
}
}, [messages, putMessageInMs, resetReceiverState]);
// 发送方重置应用状态
// Sender reset app state
const resetSenderAppState = useCallback(async () => {
try {
// 1. 清理 WebRTC 连接
// 1. Clean up WebRTC connection
await webrtcService.leaveRoom(true);
// 2. 清除分享链接和进度
// 2. Clear share link and progress
resetSenderApp();
// 3. 从后端获取新的房间ID
// 3. Fetch new room ID from backend
const newRoomId = await fetchRoom();
setShareRoomId(newRoomId || "");
setInitShareRoomId(newRoomId || "");
} catch (error) {
console.error("[RoomManager] 重置发送方状态失败:", error);
putMessageInMs("重置发送方状态失败", true);
console.error("[RoomManager] Failed to reset sender state:", error);
putMessageInMs("Failed to reset sender state", true);
}
}, [putMessageInMs, resetSenderApp, setShareRoomId, setInitShareRoomId]);
// 发送方离开房间
// Sender leave room
const handleLeaveSenderRoom = useCallback(async () => {
if (!messages) return;
try {
// 调用后端 API 离开房间
// Call backend API to leave room
if (webrtcService.sender.roomId && webrtcService.sender.peerId) {
await leaveRoom(
webrtcService.sender.roomId,
@@ -206,42 +206,52 @@ export function useRoomManager({
putMessageInMs(messages.text.ClipboardApp.roomStatus.leftRoomMsg, true);
// 重置发送方状态并获取新房间ID
// Reset sender state and get new room ID
await resetSenderAppState();
} catch (error) {
console.error("[RoomManager] 发送方离开房间失败:", error);
putMessageInMs("离开房间失败", true);
console.error("[RoomManager] Sender failed to leave room:", error);
putMessageInMs("Failed to leave room", true);
}
}, [messages, putMessageInMs, resetSenderAppState]);
// 房间ID输入处理
// Room ID input processing
const processRoomIdInput = useCallback(
debounce(async (input: string) => {
if (!input.trim() || !messages) return;
console.log("🔍 processRoomIdInput called with:", input);
if (!input.trim() || !messages) {
console.log("❌ Input empty or no messages, returning");
return;
}
try {
console.log("🌐 Calling checkRoom API for:", input);
const isValid = await checkRoom(input);
console.log("📋 checkRoom result:", isValid, "for input:", input);
if (isValid) {
console.log("✅ Room is valid, setting shareRoomId to:", input);
setShareRoomId(input);
putMessageInMs(
messages.text.ClipboardApp.roomCheck.available_msg,
true
);
} else {
console.log("❌ Room is not valid:", input);
putMessageInMs(
messages.text.ClipboardApp.roomCheck.notAvailable_msg,
true
);
}
} catch (error) {
console.error("[RoomManager] 验证房间失败:", error);
putMessageInMs("验证房间失败", true);
console.error("[RoomManager] Failed to validate room:", error);
putMessageInMs("Failed to validate room", true);
}
}, 750),
[messages, putMessageInMs, setShareRoomId]
);
// 初始化发送方房间ID
// Initialize sender room ID
useEffect(() => {
if (
messages &&
@@ -255,9 +265,10 @@ export function useRoomManager({
setShareRoomId(newRoomId || "");
setInitShareRoomId(newRoomId || "");
} catch (err) {
console.error("[RoomManager] 获取初始房间失败:", err);
console.error("[RoomManager] Failed to fetch initial room:", err);
const errorMsg =
messages.text?.ClipboardApp?.fetchRoom_err || "获取房间ID失败";
messages.text?.ClipboardApp?.fetchRoom_err ||
"Failed to fetch room ID";
putMessageInMs(errorMsg, true);
}
};
@@ -272,7 +283,7 @@ export function useRoomManager({
setInitShareRoomId,
]);
// 房间状态文本更新
// Room status text update
useEffect(() => {
if (!messages) {
if (activeTab === "send") setShareRoomStatusText("");
@@ -317,7 +328,7 @@ export function useRoomManager({
]);
return {
// 状态
// State
shareRoomId,
initShareRoomId,
shareLink,
@@ -329,7 +340,7 @@ export function useRoomManager({
isSenderInRoom,
isReceiverInRoom,
// 方法
// Methods
processRoomIdInput,
joinRoom,
generateShareLinkAndBroadcast,
+7 -7
View File
@@ -3,7 +3,7 @@ import { webrtcService } from "@/lib/webrtcService";
import { useFileTransferStore } from "@/stores/fileTransferStore";
import type { Messages } from "@/types/messages";
// 保留类型定义以保持兼容性
// Retain type definitions for compatibility
export type PeerProgressDetails = { progress: number; speed: number };
export type FileProgressPeers = { [peerId: string]: PeerProgressDetails };
export type ProgressState = { [fileId: string]: FileProgressPeers };
@@ -21,7 +21,7 @@ export function useWebRTCConnection({
messages,
putMessageInMs,
}: UseWebRTCConnectionProps) {
// store 获取状态
// Get state from store
const {
sharePeerCount,
retrievePeerCount,
@@ -31,7 +31,7 @@ export function useWebRTCConnection({
setIsAnyFileTransferring,
} = useFileTransferStore();
// 计算是否有文件正在传输
// Calculate if any file is being transferred
const isAnyFileTransferring = useMemo(() => {
const allProgress = [
...Object.values(sendProgress),
@@ -49,14 +49,14 @@ export function useWebRTCConnection({
}, [isAnyFileTransferring, setIsAnyFileTransferring]);
return {
// 状态从 store 获取
// State obtained from store
sharePeerCount,
retrievePeerCount,
senderDisconnected,
sendProgress,
receiveProgress,
// 方法直接从 service 暴露
// Methods exposed directly from service
broadcastDataToAllPeers:
webrtcService.broadcastDataToAllPeers.bind(webrtcService),
requestFile: webrtcService.requestFile.bind(webrtcService),
@@ -66,11 +66,11 @@ export function useWebRTCConnection({
getReceiverSaveType: webrtcService.getReceiverSaveType.bind(webrtcService),
manualSafeSave: webrtcService.manualSafeSave.bind(webrtcService),
// 重置连接方法
// Reset connection methods
resetSenderConnection: () => webrtcService.leaveRoom(true),
resetReceiverConnection: () => webrtcService.leaveRoom(false),
// 为了兼容性,保留这些属性(但实际上不再需要)
// For compatibility, retain these properties (but they are no longer needed)
sender: webrtcService.sender,
receiver: webrtcService.receiver,
};
+14 -14
View File
@@ -294,7 +294,7 @@ class FileReceiver {
await handler(parsedData, peerId);
} else {
console.warn(
`[DEBUG] ⚠️ FileReceiver 未找到处理器: ${parsedData.type}`
`[DEBUG] ⚠️ FileReceiver Handler not found: ${parsedData.type}`
);
}
} catch (error) {
@@ -316,7 +316,7 @@ class FileReceiver {
private handleFileMetadata(metadata: fileMetadata): void {
if (this.pendingFilesMeta.has(metadata.fileId)) {
console.log(
`[DEBUG] 📥 FileReceiver 文件元数据已存在,忽略: ${metadata.fileId}`
`[DEBUG] 📥 FileReceiver File metadata already exists, ignoring: ${metadata.fileId}`
);
return; // Ignore if already received.
}
@@ -326,7 +326,7 @@ class FileReceiver {
if (this.onFileMetaReceived) {
this.onFileMetaReceived(metadata);
} else {
console.error(`[DEBUG] ❌ FileReceiver onFileMetaReceived 回调不存在!`);
console.error(`[DEBUG] ❌ FileReceiver onFileMetaReceived callback does not exist!`);
}
// Record the file size for folder progress calculation.
if (metadata.folderName) {
@@ -376,19 +376,19 @@ class FileReceiver {
return;
}
// 🔧 关键修复:先完成文件处理,确保文件添加到Store
// 🔧 Key fix: Complete file processing first to ensure the file is added to Store
await this.finalizeFileReceive();
// 🏗️ 架构重构:确保Store状态完全同步后再触发进度回调
// 🏗️ Architecture refactor: Ensure Store state is fully synchronized before triggering progress callback
if (!this.currentFolderName) {
// 🔧 优化的异步确保机制 - 确保Store状态完全同步
await Promise.resolve(); // 确保当前执行栈完成
// 🔧 Optimized async ensure mechanism - ensure Store state is fully synchronized
await Promise.resolve(); // Ensure current execution stack is completed
await new Promise<void>((resolve) => {
// 使用更长的延迟确保Store状态完全更新
// Use longer delay to ensure Store state is fully updated
setTimeout(() => {
this.progressCallback?.(reception.meta.fileId, 1, 0);
resolve();
}, 10); // 增加到10ms确保Store状态完全同步
}, 10); // Increase to 10ms to ensure Store state is fully synchronized
});
}
@@ -554,11 +554,11 @@ class FileReceiver {
}) as CustomFile;
if (this.onFileReceived) {
// 🔧 关键修复:确保 onFileReceived 回调完全同步执行完成
// 🔧 Key fix: Ensure onFileReceived callback is fully synchronized
await this.onFileReceived(customFile);
// 🔧 多重确认机制:确保 Store 状态完全同步
await Promise.resolve(); // 第一层确认
await new Promise<void>((resolve) => setTimeout(() => resolve(), 0)); // 第二层确认
// 🔧 Multiple confirmation mechanism: Ensure Store state is fully synchronized
await Promise.resolve(); // First layer confirmation
await new Promise<void>((resolve) => setTimeout(() => resolve(), 0)); // Second layer confirmation
}
}
// endregion
@@ -586,7 +586,7 @@ class FileReceiver {
});
}
// 🔧 清理所有内部状态,确保重新连接时能正确接收文件元数据
// 🔧 Clean up all internal states to ensure correct file metadata reception upon reconnection
this.pendingFilesMeta.clear();
this.folderProgresses = {};
this.saveType = {};
+62 -62
View File
@@ -25,24 +25,24 @@ class FileSender {
private pendingFolerMeta: Record<string, FolderMeta>;
private speedCalculator: SpeedCalculator;
// 自适应性能监控
// Adaptive performance monitoring
private networkPerformance: Map<
string,
{
avgClearingRate: number; // 平均网络清理速度 KB/s
optimalThreshold: number; // 动态优化的阈值
avgWaitTime: number; // 平均等待时间
sampleCount: number; // 采样次数
avgClearingRate: number; // Average network clearing speed KB/s
optimalThreshold: number; // Dynamically optimized threshold
avgWaitTime: number; // Average wait time
sampleCount: number; // Sample count
}
> = new Map();
// 混合优化配置 - FileReader大块 + 网络小包策略(修复sendData failed
// Hybrid optimization configuration - FileReader large chunks + network small packets strategy (fixes sendData failed)
private static readonly OPTIMIZED_CONFIG = {
CHUNK_SIZE: 4194304, // 4MB - 极致大块,最大化减少FileReader调用次数
BATCH_SIZE: 8, // 8块批量 - 32MB批次处理成功
NETWORK_CHUNK_SIZE: 65536, // 64KB - WebRTC安全发送大小,修复sendData failed
BUFFER_THRESHOLD: 3145728, // 3MB - 阈值
BACKPRESSURE_TIMEOUT: 2000, // 2秒超时 - 为大块处理预留更多时间
CHUNK_SIZE: 4194304, // 4MB - Extreme large chunks, maximally reduce FileReader calls
BATCH_SIZE: 8, // 8 chunks batch - 32MB batch processing success
NETWORK_CHUNK_SIZE: 65536, // 64KB - WebRTC safe sending size, fixes sendData failed
BUFFER_THRESHOLD: 3145728, // 3MB - Threshold
BACKPRESSURE_TIMEOUT: 2000, // 2 second timeout - reserves more time for large chunk processing
} as const;
constructor(WebRTC_initiator: WebRTC_Initiator) {
@@ -51,7 +51,7 @@ class FileSender {
// Maintain independent sending states for each receiver
this.peerStates = new Map(); // Map<peerId, PeerState>
// 统一使用优化参数 - 所有设备共享最佳配置
// Uniformly use optimized parameters - all devices share the best configuration
this.chunkSize = FileSender.OPTIMIZED_CONFIG.CHUNK_SIZE;
this.pendingFiles = new Map(); // All files pending to be sent (by reference) {fileId: CustomFile}
@@ -331,7 +331,7 @@ class FileSender {
const speed = this.speedCalculator.getSendSpeed(peerId);
const progress = totalSize > 0 ? currentBytes / totalSize : 0;
// 持续更新网络性能(从传输速度学习)
// Continuously update network performance (learn from transfer speed)
this.updateNetworkFromSpeed(peerId);
peerState.progressCallback?.(progressFileId, progress, speed);
@@ -346,16 +346,16 @@ class FileSender {
throw new Error("Data channel not found");
}
// 对于ArrayBuffer,如果超过64KB需要分片发送(修复sendData failed
// For ArrayBuffer, if it exceeds 64KB, it needs to be sent in fragments (fixes sendData failed)
if (data instanceof ArrayBuffer) {
await this.sendLargeArrayBuffer(data, peerId);
} else {
// 字符串直接发送
// Send string directly
await this.sendSingleData(data, peerId);
}
}
// 新增:分片发送大ArrayBuffer
// New: Send large ArrayBuffer in fragments
private async sendLargeArrayBuffer(
data: ArrayBuffer,
peerId: string
@@ -363,13 +363,13 @@ class FileSender {
const networkChunkSize = FileSender.OPTIMIZED_CONFIG.NETWORK_CHUNK_SIZE;
const totalSize = data.byteLength;
// 如果数据小于64KB,直接发送
// If data is less than 64KB, send directly
if (totalSize <= networkChunkSize) {
await this.sendSingleData(data, peerId);
return;
}
// 分片发送大块
// Send large chunks in fragments
let offset = 0;
let fragmentIndex = 0;
@@ -377,7 +377,7 @@ class FileSender {
const chunkSize = Math.min(networkChunkSize, totalSize - offset);
const chunk = data.slice(offset, offset + chunkSize);
// 发送分片
// Send fragment
await this.sendSingleData(chunk, peerId);
offset += chunkSize;
@@ -385,7 +385,7 @@ class FileSender {
}
}
// 新增:单个数据包发送含主动轮询背压控制
// New: Send single data packet with active polling backpressure control
private async sendSingleData(
data: string | ArrayBuffer,
peerId: string
@@ -394,29 +394,29 @@ class FileSender {
if (!dataChannel) {
throw new Error("Data channel not found");
}
// 智能发送控制 - 根据缓冲区状态决定发送策略
// Intelligent send control - decide sending strategy based on buffer status
await this.smartBufferControl(dataChannel, peerId);
// 发送数据
// Send data
if (!this.webrtcConnection.sendData(data, peerId)) {
throw new Error("sendData failed");
}
}
// 初始化网络性能监控(传输开始时调用)
// Initialize network performance monitoring (called at the start of transfer)
private initializeNetworkPerformance(peerId: string): void {
if (!this.networkPerformance.has(peerId)) {
// 使用保守的初始值
// Use conservative initial values
this.networkPerformance.set(peerId, {
avgClearingRate: 5000, // 5MB/s初始估计
avgClearingRate: 5000, // 5MB/s initial estimate
optimalThreshold: FileSender.OPTIMIZED_CONFIG.BUFFER_THRESHOLD,
avgWaitTime: 50, // 50ms初始估计
avgWaitTime: 50, // 50ms initial estimate
sampleCount: 0,
});
}
}
// 从SpeedCalculator获取当前传输速度并更新网络性能
// Get current transfer speed from SpeedCalculator and update network performance
private updateNetworkFromSpeed(peerId: string): void {
const currentSpeed = this.speedCalculator.getSendSpeed(peerId); // KB/s
if (currentSpeed > 0) {
@@ -425,7 +425,7 @@ class FileSender {
perf.avgClearingRate = currentSpeed;
perf.sampleCount++;
// 每10次速度更新时调整阈值
// Adjust threshold every 10 speed updates
if (perf.sampleCount % 10 === 0) {
this.adjustOptimalThreshold(perf);
}
@@ -433,7 +433,7 @@ class FileSender {
}
}
// 调整最优阈值的共用逻辑
// Shared logic for adjusting optimal threshold
private adjustOptimalThreshold(perf: {
avgClearingRate: number;
optimalThreshold: number;
@@ -441,16 +441,16 @@ class FileSender {
sampleCount: number;
}): void {
if (perf.avgClearingRate > 8000) {
// >8MB/s网络很好
// >8MB/s network is good
perf.optimalThreshold = Math.max(
FileSender.OPTIMIZED_CONFIG.BUFFER_THRESHOLD,
6291456
); // 6MB
} else if (perf.avgClearingRate > 4000) {
// >4MB/s网络一般
// >4MB/s network is average
perf.optimalThreshold = FileSender.OPTIMIZED_CONFIG.BUFFER_THRESHOLD; // 3MB
} else {
// 网络较差
// Poor network
perf.optimalThreshold = Math.min(
FileSender.OPTIMIZED_CONFIG.BUFFER_THRESHOLD,
1572864
@@ -458,7 +458,7 @@ class FileSender {
}
}
// 自适应网络性能学习(从背压等待中学习)
// Adaptive network performance learning (learn from backpressure waiting)
private updateNetworkPerformance(
peerId: string,
clearingRate: number,
@@ -470,16 +470,16 @@ class FileSender {
const perf = this.networkPerformance.get(peerId)!;
perf.sampleCount++;
// 指数移动平均,新数据权重更高
// Exponential moving average, with higher weight for new data
const alpha = 0.3;
perf.avgClearingRate =
perf.avgClearingRate * (1 - alpha) + clearingRate * alpha;
perf.avgWaitTime = perf.avgWaitTime * (1 - alpha) + waitTime * alpha;
// 调整最优阈值
// Adjust optimal threshold
this.adjustOptimalThreshold(perf);
}
// 获取自适应阈值
// Get adaptive threshold
private getAdaptiveThreshold(peerId: string): number {
const perf = this.networkPerformance.get(peerId);
return perf
@@ -487,7 +487,7 @@ class FileSender {
: FileSender.OPTIMIZED_CONFIG.BUFFER_THRESHOLD;
}
// 自适应智能发送控制策略
// Adaptive intelligent send control strategy
private async intelligentSendControl(
dataChannel: RTCDataChannel,
peerId: string
@@ -496,7 +496,7 @@ class FileSender {
const adaptiveThreshold = this.getAdaptiveThreshold(peerId);
const utilizationRate = bufferedAmount / adaptiveThreshold;
// 动态调整策略阈值:基于网络性能
// Dynamically adjust strategy thresholds: based on network performance
const perf = this.networkPerformance.get(peerId);
const networkQuality = perf
? perf.avgClearingRate > 6000
@@ -509,14 +509,14 @@ class FileSender {
let cautiousThreshold = 0.9;
if (networkQuality === "good") {
// 网络好:更激进的策略
aggressiveThreshold = 0.4; // 40%以下积极发送
normalThreshold = 0.7; // 70%以下正常发送
// Good network: more aggressive strategy
aggressiveThreshold = 0.4; // Actively send below 40%
normalThreshold = 0.7; // Normal send below 70%
} else if (networkQuality === "poor") {
// 网络差:更保守的策略
aggressiveThreshold = 0.2; // 20%以下才积极发送
normalThreshold = 0.5; // 50%以下正常发送
cautiousThreshold = 0.8; // 80%以上就要等待
// Poor network: more conservative strategy
aggressiveThreshold = 0.2; // Actively send only below 20%
normalThreshold = 0.5; // Normal send below 50%
cautiousThreshold = 0.8; // Wait above 80%
}
if (utilizationRate < aggressiveThreshold) {
return "AGGRESSIVE";
@@ -529,7 +529,7 @@ class FileSender {
}
}
// 智能等待策略 - 根据缓冲区状态调整发送控制
// Intelligent waiting strategy - adjust send control based on buffer status
private async smartBufferControl(
dataChannel: RTCDataChannel,
peerId: string
@@ -537,19 +537,19 @@ class FileSender {
const strategy = await this.intelligentSendControl(dataChannel, peerId);
if (strategy === "AGGRESSIVE") {
// 积极模式:无需等待,立即发送
// Aggressive mode: no need to wait, send immediately
return;
} else if (strategy === "NORMAL") {
await new Promise<void>((resolve) => setTimeout(resolve, 5));
// 正常模式:少许等待
// Normal mode: slight wait
return;
} else if (strategy === "CAUTIOUS") {
// 谨慎模式:短暂等待让网络消费一些数据
// Cautious mode: brief wait to let the network consume some data
await new Promise<void>((resolve) => setTimeout(resolve, 10));
return;
}
// WAIT模式:需要主动轮询等待
// WAIT mode: requires active polling wait
const POLLING_INTERVAL = 5;
const MAX_WAIT_TIME = 3000;
const startTime = Date.now();
@@ -574,20 +574,20 @@ class FileSender {
);
}
// 记录等待结束状态
// Record wait end status
const waitTime = Date.now() - startTime;
const finalBuffered = dataChannel.bufferedAmount;
const clearedBytes = initialBuffered - finalBuffered;
const clearingRate =
waitTime > 0 ? clearedBytes / 1024 / (waitTime / 1000) : 0;
// 更新网络性能学习
// Update network performance learning
if (clearingRate > 0) {
this.updateNetworkPerformance(peerId, clearingRate, waitTime);
}
}
// 读取单个文件块的优化方法
// Optimized method for reading a single file chunk
private readSingleChunk(
fileReader: FileReader,
file: CustomFile,
@@ -609,7 +609,7 @@ class FileSender {
});
}
// 批量读取多个文件块,提升I/O性能
// Batch read multiple file chunks to improve I/O performance
private async readMultipleChunks(
fileReader: FileReader,
file: CustomFile,
@@ -641,7 +641,7 @@ class FileSender {
return chunks;
}
// 统一优化版本 - 使用批量读取+循环,适用于所有设备
// Unified optimized version - uses batch reading + loop, suitable for all devices
private async processSendQueue(
file: CustomFile,
peerId: string
@@ -653,14 +653,14 @@ class FileSender {
let offset = peerState.readOffset || 0;
const batchSize = FileSender.OPTIMIZED_CONFIG.BATCH_SIZE;
// 初始化网络性能监控
// Initialize network performance monitoring
this.initializeNetworkPerformance(peerId);
try {
// 使用批量读取+循环替代传统递归,大幅提升性能
// Use batch reading + loop instead of traditional recursion to greatly improve performance
while (offset < file.size && peerState.isSending) {
// 批量读取多个大块 - 充分利用内存优势
// Batch read multiple large chunks - fully utilize memory advantages
const chunks = await this.readMultipleChunks(
fileReader,
file,
@@ -673,14 +673,14 @@ class FileSender {
for (const chunk of chunks) {
if (!peerState.isSending || offset >= file.size) break;
// 使用标准的智能控制发送
// Use standard intelligent control sending
await this.sendWithBackpressure(chunk, peerId);
// 更新进度
// Update progress
offset += chunk.byteLength;
peerState.readOffset = offset;
// 更新文件和文件夹进度
// Update file and folder progress
await this.updateProgress(
chunk.byteLength,
fileId,
@@ -689,7 +689,7 @@ class FileSender {
);
}
}
// 文件发送完毕
// File sending completed
if (offset >= file.size && !peerState.currentFolderName) {
peerState.progressCallback?.(fileId, 1, 0);
}
+14 -14
View File
@@ -41,7 +41,7 @@ class WebRTCService {
}
private initializeEventHandlers(): void {
// 发送方事件处理
// Sender event handling
this.sender.onConnectionStateChange = (state, peerId) => {
useFileTransferStore.getState().setShareConnectionState(state as any);
useFileTransferStore
@@ -59,7 +59,7 @@ class WebRTCService {
this.sender.onDataChannelOpen = (peerId) => {
useFileTransferStore.getState().setIsSenderInRoom(true);
// 自动广播当前内容
// Automatically broadcast current content
this.broadcastDataToAllPeers();
};
@@ -72,10 +72,10 @@ class WebRTCService {
};
this.sender.onError = (error) => {
console.error("[WebRTC Service] 发送方错误:", error.message);
console.error("[WebRTC Service] Sender error:", error.message);
};
// 接收方事件处理
// Receiver event handling
this.receiver.onConnectionStateChange = (state, peerId) => {
useFileTransferStore.getState().setRetrieveConnectionState(state as any);
useFileTransferStore
@@ -123,10 +123,10 @@ class WebRTCService {
};
this.fileReceiver.onFileReceived = async (file) => {
// 🔧 增强修复:确保Store状态更新完全同步,使用多重验证
// 🔧 Enhanced fix: Ensure Store state updates are fully synchronized with multiple verifications
const store = useFileTransferStore.getState();
// 检查文件是否已经存在,避免重复添加
// Check if file already exists to avoid duplicates
const existingFile = store.retrievedFiles.find(
(f) => f.name === file.name && f.size === file.size
);
@@ -135,7 +135,7 @@ class WebRTCService {
store.addRetrievedFile(file);
}
// 🔧 额外确保:立即验证状态更新是否成功,并重试机制
// 🔧 Additional ensure: Immediately verify if state update was successful with retry mechanism
let verificationAttempts = 0;
const maxVerificationAttempts = 3;
@@ -152,12 +152,12 @@ class WebRTCService {
}
};
// 立即进行第一次验证
// Perform first verification immediately
verifyFileAdded();
};
}
// 业务方法
// Business methods
public async joinRoom(roomId: string, isSender: boolean): Promise<void> {
const peer = isSender ? this.sender : this.receiver;
await peer.joinRoom(roomId, isSender);
@@ -184,7 +184,7 @@ class WebRTCService {
const { shareContent, sendFiles } = useFileTransferStore.getState();
const peerIds = Array.from(this.sender.peerConnections.keys());
if (peerIds.length === 0) {
console.warn("[WebRTC Service] 没有连接的对等端进行广播");
console.warn("[WebRTC Service] No connected peers to broadcast to");
return false;
}
@@ -201,7 +201,7 @@ class WebRTCService {
);
return true;
} catch (error) {
console.error("[WebRTC Service] 广播失败:", error);
console.error("[WebRTC Service] Broadcast failed:", error);
return false;
}
}
@@ -229,15 +229,15 @@ class WebRTCService {
}
public async cleanup(): Promise<void> {
console.log("[WebRTC Service] 开始清理...");
console.log("[WebRTC Service] Starting cleanup...");
try {
await Promise.all([
this.sender.cleanUpBeforeExit(),
this.receiver.cleanUpBeforeExit(),
]);
console.log("[WebRTC Service] 清理完成");
console.log("[WebRTC Service] Cleanup completed");
} catch (error) {
console.error("[WebRTC Service] 清理过程中出错:", error);
console.error("[WebRTC Service] Error during cleanup:", error);
}
}
}
+1 -1
View File
@@ -81,7 +81,7 @@ export default class WebRTC_Initiator extends BaseWebRTC {
});
// this.log('log', `Created data channel for peer ${peerId}`);
dataChannel.bufferedAmountLowThreshold = 262144; //256 KB -- 可以根据需要调整
dataChannel.bufferedAmountLowThreshold = 262144; //256 KB -- Can be adjusted as needed
this.setupDataChannel(dataChannel, peerId);
this.dataChannels.set(peerId, dataChannel);
} catch (error) {
+20 -20
View File
@@ -2,14 +2,14 @@ import { create } from "zustand";
import { CustomFile, FileMeta } from "@/types/webrtc";
interface FileTransferState {
// 房间相关状态
// Room-related state
shareRoomId: string;
initShareRoomId: string;
shareLink: string;
shareRoomStatusText: string;
retrieveRoomStatusText: string;
// WebRTC 连接状态 - 发送方
// WebRTC connection state - Sender
shareConnectionState:
| "idle"
| "connecting"
@@ -19,7 +19,7 @@ interface FileTransferState {
isSenderInRoom: boolean;
sharePeerCount: number;
// WebRTC 连接状态 - 接收方
// WebRTC connection state - Receiver
retrieveConnectionState:
| "idle"
| "connecting"
@@ -30,36 +30,36 @@ interface FileTransferState {
retrievePeerCount: number;
senderDisconnected: boolean;
// 文件传输状态
// File transfer state
shareContent: string;
sendFiles: CustomFile[];
retrievedContent: string;
retrievedFiles: CustomFile[];
retrievedFileMetas: FileMeta[];
// 传输进度状态
// Transfer progress state
sendProgress: Record<string, any>;
receiveProgress: Record<string, any>;
isAnyFileTransferring: boolean;
// UI 状态
// UI state
activeTab: "send" | "retrieve";
retrieveRoomIdInput: string;
isDragging: boolean;
// 消息状态
// Message state
shareMessage: string;
retrieveMessage: string;
// Actions
// 房间相关 actions
// Room-related actions
setShareRoomId: (id: string) => void;
setInitShareRoomId: (id: string) => void;
setShareLink: (link: string) => void;
setShareRoomStatusText: (text: string) => void;
setRetrieveRoomStatusText: (text: string) => void;
// WebRTC 连接相关 actions
// WebRTC connection-related actions
setShareConnectionState: (
state: "idle" | "connecting" | "connected" | "disconnected" | "failed"
) => void;
@@ -72,7 +72,7 @@ interface FileTransferState {
setRetrievePeerCount: (count: number) => void;
setSenderDisconnected: (disconnected: boolean) => void;
// 文件传输相关 actions
// File transfer-related actions
setShareContent: (content: string) => void;
setSendFiles: (files: CustomFile[]) => void;
addSendFiles: (files: CustomFile[]) => void;
@@ -82,7 +82,7 @@ interface FileTransferState {
setRetrievedFileMetas: (metas: FileMeta[]) => void;
addRetrievedFile: (file: CustomFile) => void;
// 传输进度相关 actions
// Transfer progress-related actions
setSendProgress: (progress: Record<string, any>) => void;
setReceiveProgress: (progress: Record<string, any>) => void;
updateSendProgress: (
@@ -99,23 +99,23 @@ interface FileTransferState {
clearReceiveProgress: (fileId: string, peerId: string) => void;
setIsAnyFileTransferring: (transferring: boolean) => void;
// UI 状态相关 actions
// UI state-related actions
setActiveTab: (tab: "send" | "retrieve") => void;
setRetrieveRoomIdInput: (input: string) => void;
setIsDragging: (dragging: boolean) => void;
// 消息相关 actions
// Message-related actions
setShareMessage: (message: string) => void;
setRetrieveMessage: (message: string) => void;
setRetrieveRoomId: (input: string) => void;
// 重置相关 actions
// Reset-related actions
resetReceiverState: () => void;
resetSenderApp: () => void;
}
export const useFileTransferStore = create<FileTransferState>()((set, get) => ({
// 初始状态
// Initial state
shareRoomId: "",
initShareRoomId: "",
shareLink: "",
@@ -142,14 +142,14 @@ export const useFileTransferStore = create<FileTransferState>()((set, get) => ({
shareMessage: "",
retrieveMessage: "",
// Actions 实现
// Actions implementation
setShareRoomId: (id) => set({ shareRoomId: id }),
setInitShareRoomId: (id) => set({ initShareRoomId: id }),
setShareLink: (link) => set({ shareLink: link }),
setShareRoomStatusText: (text) => set({ shareRoomStatusText: text }),
setRetrieveRoomStatusText: (text) => set({ retrieveRoomStatusText: text }),
// WebRTC 连接相关 actions
// WebRTC connection-related actions
setShareConnectionState: (state) => set({ shareConnectionState: state }),
setIsSenderInRoom: (isInRoom) => set({ isSenderInRoom: isInRoom }),
setSharePeerCount: (count) => set({ sharePeerCount: count }),
@@ -238,18 +238,18 @@ export const useFileTransferStore = create<FileTransferState>()((set, get) => ({
setRetrieveMessage: (message) => set({ retrieveMessage: message }),
resetReceiverState: () => {
// 🔧 清理 FileReceiver 的内部状态(通过 Service 层)
// 🔧 Clean up FileReceiver's internal state (via Service layer)
try {
const { webrtcService } = require("@/lib/webrtcService");
webrtcService.fileReceiver.gracefulShutdown();
} catch (error) {
console.warn(`[DEBUG] ⚠️ 清理 FileReceiver 状态失败:`, error);
console.warn(`[DEBUG] ⚠️ Failed to clean up FileReceiver state:`, error);
}
set({
retrievedContent: "",
retrievedFiles: [],
retrievedFileMetas: [], // 清空 Store 中的文件元数据
retrievedFileMetas: [], // Clear file metadata in Store
retrievePeerCount: 0,
senderDisconnected: false,
receiveProgress: {},