diff --git a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs index 3f07548f..722d97f5 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs @@ -119,6 +119,10 @@ public class BaseFmt { dicQuery.Add("seed", UrlEncodeSafe(transport.KcpSeed)); } + if (transport.KcpMtu > 0) + { + dicQuery.Add("mtu", transport.KcpMtu.ToString()); + } break; case nameof(ETransport.ws): @@ -279,10 +283,13 @@ public class BaseFmt case nameof(ETransport.kcp): var kcpSeed = GetQueryDecoded(query, "seed"); + var kcpMtuStr = GetQueryValue(query, "mtu"); + var kcpMtu = int.TryParse(kcpMtuStr, out var mtu) ? mtu : 0; transport = transport with { KcpHeaderType = GetQueryValue(query, "headerType", Global.None), KcpSeed = kcpSeed, + KcpMtu = kcpMtu > 0 ? mtu : null, }; break; diff --git a/v2rayN/ServiceLib/Models/TransportExtraItem.cs b/v2rayN/ServiceLib/Models/TransportExtraItem.cs index 7ffcc9f5..77fbc984 100644 --- a/v2rayN/ServiceLib/Models/TransportExtraItem.cs +++ b/v2rayN/ServiceLib/Models/TransportExtraItem.cs @@ -15,4 +15,5 @@ public record TransportExtraItem public string? KcpHeaderType { get; init; } public string? KcpSeed { get; init; } + public int? KcpMtu { get; init; } } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index b058dfa1..ef22d545 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -328,6 +328,7 @@ public partial class CoreConfigV2rayService var host = string.Empty; var path = string.Empty; var kcpSeed = string.Empty; + var kcpMtu = 0; var headerType = string.Empty; var xhttpExtra = string.Empty; switch (network) @@ -341,6 +342,7 @@ public partial class CoreConfigV2rayService case nameof(ETransport.kcp): kcpSeed = transport.KcpSeed?.TrimEx() ?? string.Empty; headerType = transport.KcpHeaderType?.TrimEx() ?? string.Empty; + kcpMtu = transport.KcpMtu > 0 ? transport.KcpMtu!.Value : _config.KcpItem.Mtu; break; case nameof(ETransport.ws): @@ -441,7 +443,7 @@ public partial class CoreConfigV2rayService case nameof(ETransport.kcp): KcpSettings4Ray kcpSettings = new() { - mtu = _config.KcpItem.Mtu, + mtu = kcpMtu, tti = _config.KcpItem.Tti }; diff --git a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs index 3dbea94c..20439a03 100644 --- a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs @@ -106,6 +106,9 @@ public class AddServerViewModel : MyReactiveObject [Reactive] public string KcpSeed { get; set; } + [Reactive] + public int? KcpMtu { get; set; } + public string TransportHeaderType { get => SelectedSource.GetNetwork() switch @@ -287,26 +290,26 @@ public class AddServerViewModel : MyReactiveObject Cert = SelectedSource?.Cert?.ToString() ?? string.Empty; CertSha = SelectedSource?.CertSha?.ToString() ?? string.Empty; - var protocolExtra = SelectedSource?.GetProtocolExtra(); - var transport = SelectedSource?.GetTransportExtra(); - Ports = protocolExtra?.Ports ?? string.Empty; - AlterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0; - Flow = protocolExtra?.Flow ?? string.Empty; - SalamanderPass = protocolExtra?.SalamanderPass ?? string.Empty; - UpMbps = protocolExtra?.UpMbps; - DownMbps = protocolExtra?.DownMbps; - HopInterval = protocolExtra?.HopInterval ?? string.Empty; - VmessSecurity = protocolExtra?.VmessSecurity?.IsNullOrEmpty() == false ? protocolExtra.VmessSecurity : Global.DefaultSecurity; - VlessEncryption = protocolExtra?.VlessEncryption.IsNullOrEmpty() == false ? protocolExtra.VlessEncryption : Global.None; - SsMethod = protocolExtra?.SsMethod ?? string.Empty; - WgPublicKey = protocolExtra?.WgPublicKey ?? string.Empty; - WgInterfaceAddress = protocolExtra?.WgInterfaceAddress ?? string.Empty; - WgReserved = protocolExtra?.WgReserved ?? string.Empty; - WgMtu = protocolExtra?.WgMtu ?? 1280; - Uot = protocolExtra?.Uot ?? false; - CongestionControl = protocolExtra?.CongestionControl ?? string.Empty; - InsecureConcurrency = protocolExtra?.InsecureConcurrency > 0 ? protocolExtra.InsecureConcurrency : null; - NaiveQuic = protocolExtra?.NaiveQuic ?? false; + var protocolExtra = SelectedSource?.GetProtocolExtra() ?? new(); + var transport = SelectedSource?.GetTransportExtra() ?? new(); + Ports = protocolExtra.Ports ?? string.Empty; + AlterId = int.TryParse(protocolExtra.AlterId, out var result) ? result : 0; + Flow = protocolExtra.Flow ?? string.Empty; + SalamanderPass = protocolExtra.SalamanderPass ?? string.Empty; + UpMbps = protocolExtra.UpMbps; + DownMbps = protocolExtra.DownMbps; + HopInterval = protocolExtra.HopInterval ?? string.Empty; + VmessSecurity = protocolExtra.VmessSecurity?.IsNullOrEmpty() == false ? protocolExtra.VmessSecurity : Global.DefaultSecurity; + VlessEncryption = protocolExtra.VlessEncryption?.IsNullOrEmpty() == false ? protocolExtra.VlessEncryption : Global.None; + SsMethod = protocolExtra.SsMethod ?? string.Empty; + WgPublicKey = protocolExtra.WgPublicKey ?? string.Empty; + WgInterfaceAddress = protocolExtra.WgInterfaceAddress ?? string.Empty; + WgReserved = protocolExtra.WgReserved ?? string.Empty; + WgMtu = protocolExtra.WgMtu ?? 1280; + Uot = protocolExtra.Uot ?? false; + CongestionControl = protocolExtra.CongestionControl ?? string.Empty; + InsecureConcurrency = protocolExtra.InsecureConcurrency > 0 ? protocolExtra.InsecureConcurrency : null; + NaiveQuic = protocolExtra.NaiveQuic ?? false; RawHeaderType = transport.RawHeaderType ?? Global.None; Host = transport.Host ?? string.Empty; @@ -318,6 +321,7 @@ public class AddServerViewModel : MyReactiveObject GrpcMode = transport.GrpcMode.IsNullOrEmpty() ? Global.GrpcGunMode : transport.GrpcMode; KcpHeaderType = transport.KcpHeaderType.IsNullOrEmpty() ? Global.None : transport.KcpHeaderType; KcpSeed = transport.KcpSeed ?? string.Empty; + KcpMtu = transport.KcpMtu; } private async Task SaveServerAsync() @@ -381,6 +385,7 @@ public class AddServerViewModel : MyReactiveObject GrpcMode = GrpcMode.NullIfEmpty(), KcpHeaderType = KcpHeaderType.NullIfEmpty(), KcpSeed = KcpSeed.NullIfEmpty(), + KcpMtu = KcpMtu > 0 ? KcpMtu : null, }; SelectedSource.SetProtocolExtra(SelectedSource.GetProtocolExtra() with diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml index f29afb7f..00bcff1b 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml @@ -831,7 +831,7 @@ x:Name="gridTransportKcp" ColumnDefinitions="300,Auto" IsVisible="False" - RowDefinitions="Auto,Auto"> + RowDefinitions="Auto,Auto,Auto"> + Margin="{StaticResource Margin4}" + HorizontalAlignment="Left" /> + + + Margin="{StaticResource Margin4}" + HorizontalAlignment="Left" /> this.Bind(ViewModel, vm => vm.KcpHeaderType, v => v.cmbHeaderTypeKcp.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.KcpSeed, v => v.txtKcpSeed.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.KcpMtu, v => v.txtKcpMtu.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostWs.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Path, v => v.txtPathWs.Text).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml b/v2rayN/v2rayN/Views/AddServerWindow.xaml index e2dec9bc..4efb6b94 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml @@ -1096,6 +1096,7 @@ + + + diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index 5a8b3bf4..aeb6ceaa 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -216,6 +216,7 @@ public partial class AddServerWindow this.Bind(ViewModel, vm => vm.KcpHeaderType, v => v.cmbHeaderTypeKcp.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.KcpSeed, v => v.txtKcpSeed.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.KcpMtu, v => v.txtKcpMtu.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostWs.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Path, v => v.txtPathWs.Text).DisposeWith(disposables);