Skip to content

Commit 0df84c6

Browse files
committed
Add project Quick.Protocol.Udp
1 parent c7c214b commit 0df84c6

File tree

11 files changed

+515
-0
lines changed

11 files changed

+515
-0
lines changed

Quick.Protocol.Udp/QpUdpClient.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using Quick.Protocol.Utils;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.ComponentModel;
5+
using System.IO;
6+
using System.Linq;
7+
using System.Net;
8+
using System.Net.Sockets;
9+
using System.Text;
10+
using System.Threading.Tasks;
11+
12+
namespace Quick.Protocol.Udp
13+
{
14+
[DisplayName("UDP")]
15+
public class QpUdpClient : QpClient
16+
{
17+
private UdpClient udpClient;
18+
private QpUdpClientOptions options;
19+
20+
public QpUdpClient(QpUdpClientOptions options) : base(options)
21+
{
22+
this.options = options;
23+
}
24+
25+
protected override async Task<Stream> InnerConnectAsync()
26+
{
27+
if (udpClient != null)
28+
Close();
29+
//开始连接
30+
if (string.IsNullOrEmpty(options.LocalHost))
31+
udpClient = new UdpClient();
32+
else
33+
udpClient = new UdpClient(new IPEndPoint(IPAddress.Parse(options.LocalHost), options.LocalPort));
34+
var remoteHostAddresses = await Dns.GetHostAddressesAsync(options.Host);
35+
var remoteEndPoint = new IPEndPoint(remoteHostAddresses.First(), options.Port);
36+
await TaskUtils.TaskWait(Task.Run(() => udpClient.Connect(remoteEndPoint)), options.ConnectionTimeout);
37+
return new UdpClientStream(udpClient, remoteEndPoint);
38+
}
39+
40+
public override void Disconnect()
41+
{
42+
if (udpClient != null)
43+
{
44+
udpClient.Close();
45+
udpClient.Dispose();
46+
udpClient = null;
47+
}
48+
49+
base.Disconnect();
50+
}
51+
}
52+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel;
4+
using System.Text;
5+
6+
namespace Quick.Protocol.Udp
7+
{
8+
public class QpUdpClientOptions : QpClientOptions
9+
{
10+
public const string URI_SCHEMA = "qp.udp";
11+
/// <summary>
12+
/// 主机
13+
/// </summary>
14+
[DisplayName("主机")]
15+
[Category("常用")]
16+
public string Host { get; set; } = "127.0.0.1";
17+
/// <summary>
18+
/// 端口
19+
/// </summary>
20+
[DisplayName("端口")]
21+
[Category("常用")]
22+
public int Port { get; set; } = 3011;
23+
/// <summary>
24+
/// 本地主机
25+
/// </summary>
26+
[DisplayName("本地主机")]
27+
[Category("高级")]
28+
public string LocalHost { get; set; }
29+
/// <summary>
30+
/// 本地端口
31+
/// </summary>
32+
[DisplayName("本地端口")]
33+
[Category("高级")]
34+
public int LocalPort { get; set; } = 0;
35+
36+
public override void Check()
37+
{
38+
base.Check();
39+
if (string.IsNullOrEmpty(Host))
40+
throw new ArgumentNullException(nameof(Host));
41+
if (Port < 0 || Port > 65535)
42+
throw new ArgumentException("Port must between 0 and 65535", nameof(Port));
43+
}
44+
45+
public override Type GetQpClientType() => typeof(QpUdpClient);
46+
47+
protected override void LoadFromUri(Uri uri)
48+
{
49+
Host = uri.Host;
50+
Port = uri.Port;
51+
base.LoadFromUri(uri);
52+
}
53+
54+
protected override string ToUriBasic(HashSet<string> ignorePropertyNames)
55+
{
56+
ignorePropertyNames.Add(nameof(Host));
57+
ignorePropertyNames.Add(nameof(Port));
58+
return $"{URI_SCHEMA}://{Host}:{Port}";
59+
}
60+
61+
public static void RegisterUriSchema()
62+
{
63+
RegisterUriSchema(URI_SCHEMA, typeof(QpUdpClientOptions));
64+
}
65+
}
66+
}

Quick.Protocol.Udp/QpUdpServer.cs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using Quick.Protocol.Utils;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
using System.Net;
6+
using System.Net.Sockets;
7+
using System.Text;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
11+
namespace Quick.Protocol.Udp
12+
{
13+
public class QpUdpServer : QpServer
14+
{
15+
private UdpClient udpServer;
16+
private QpUdpServerOptions options;
17+
public IPEndPoint ListenEndPoint { get; private set; }
18+
public QpUdpServer(QpUdpServerOptions options) : base(options)
19+
{
20+
this.options = options;
21+
}
22+
23+
public override void Start()
24+
{
25+
ListenEndPoint = new IPEndPoint(options.Address, options.Port);
26+
udpServer = new UdpClient(ListenEndPoint);
27+
base.Start();
28+
}
29+
30+
public override void Stop()
31+
{
32+
udpServer?.Close();
33+
udpServer?.Dispose();
34+
udpServer = null;
35+
base.Stop();
36+
}
37+
38+
protected override Task InnerAcceptAsync(CancellationToken token)
39+
{
40+
return udpServer.ReceiveAsync().ContinueWith(task =>
41+
{
42+
if (task.IsCanceled)
43+
return;
44+
if (task.IsFaulted)
45+
return;
46+
var ret = task.Result;
47+
var remoteEndPoint = ret.RemoteEndPoint;
48+
var buffer = ret.Buffer;
49+
50+
try
51+
{
52+
var remoteEndPointStr = "UDP:" + remoteEndPoint.ToString();
53+
if (LogUtils.LogConnection)
54+
LogUtils.Log("[Connection]{0} connected.", remoteEndPointStr);
55+
OnNewChannelConnected(new UdpClientStream(udpServer, remoteEndPoint, buffer), remoteEndPointStr, token);
56+
}
57+
catch (Exception ex)
58+
{
59+
if (LogUtils.LogConnection)
60+
LogUtils.Log("[Connection]Init&Start Channel error,reason:{0}", ex.ToString());
61+
}
62+
});
63+
}
64+
}
65+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Newtonsoft.Json;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Net;
5+
using System.Text;
6+
7+
namespace Quick.Protocol.Udp
8+
{
9+
public class QpUdpServerOptions : QpServerOptions
10+
{
11+
/// <summary>
12+
/// IP地址
13+
/// </summary>
14+
[JsonIgnore]
15+
public IPAddress Address { get; set; }
16+
/// <summary>
17+
/// 端口
18+
/// </summary>
19+
public int Port { get; set; }
20+
21+
public override void Check()
22+
{
23+
base.Check();
24+
if (Address == null)
25+
throw new ArgumentNullException(nameof(Address));
26+
if (Port < 0 || Port > 65535)
27+
throw new ArgumentException("Port must between 0 and 65535", nameof(Port));
28+
}
29+
}
30+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
6+
<PackageProjectUrl>https://github.com/QuickProtocol</PackageProjectUrl>
7+
<RepositoryUrl>https://github.com/QuickProtocol/QuickProtocol_CSharp</RepositoryUrl>
8+
<Version>2.2.5</Version>
9+
<Authors>scbeta</Authors>
10+
<Company />
11+
<Product>Quick.Protocol</Product>
12+
<Description>UDP support for Quick.Protocol</Description>
13+
<PackageIcon>logo.png</PackageIcon>
14+
<PackageTags>QuickProtocol</PackageTags>
15+
</PropertyGroup>
16+
17+
<ItemGroup>
18+
<None Include="..\logo.png">
19+
<Pack>True</Pack>
20+
<PackagePath>\</PackagePath>
21+
</None>
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<ProjectReference Include="..\Quick.Protocol\Quick.Protocol.csproj" />
26+
</ItemGroup>
27+
28+
</Project>
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Net;
6+
using System.Net.Sockets;
7+
using System.Text;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
11+
namespace Quick.Protocol.Udp
12+
{
13+
internal class UdpClientStream : Stream
14+
{
15+
private UdpClient udpClient;
16+
private IPEndPoint remoteEndPoint;
17+
private byte[] firstRecvBuffer;
18+
private int firstRecvBufferOffset = 0;
19+
20+
public override bool CanRead => udpClient.Available > 0;
21+
public override bool CanSeek => false;
22+
public override bool CanWrite => true;
23+
public override long Length => throw new NotImplementedException();
24+
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
25+
26+
public UdpClientStream(UdpClient udpClient, IPEndPoint remoteEndPoint, byte[] firstRecvBuffer = null)
27+
{
28+
this.udpClient = udpClient;
29+
this.remoteEndPoint = remoteEndPoint;
30+
this.firstRecvBuffer = firstRecvBuffer;
31+
}
32+
33+
public override void Flush() { }
34+
35+
public override int Read(byte[] buffer, int offset, int count)
36+
{
37+
return ReadAsync(buffer, offset, count, CancellationToken.None).Result;
38+
}
39+
40+
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
41+
{
42+
var receivedBytes = 0;
43+
if (firstRecvBuffer != null)
44+
{
45+
var copyCount = Math.Min(count, firstRecvBuffer.Length - firstRecvBufferOffset);
46+
for (var i = 0; i < copyCount; i++)
47+
buffer[offset + i] = firstRecvBuffer[firstRecvBufferOffset + i];
48+
49+
offset += copyCount;
50+
firstRecvBufferOffset += copyCount;
51+
count -= copyCount;
52+
receivedBytes = copyCount;
53+
if (firstRecvBuffer.Length - firstRecvBufferOffset <= 0)
54+
{
55+
firstRecvBuffer = null;
56+
}
57+
if (count <= 0)
58+
return copyCount;
59+
}
60+
var ret = await udpClient.ReceiveAsync(cancellationToken);
61+
62+
var ret = await udpClient.Client.ReceiveAsync(
63+
new ArraySegment<byte>(buffer, offset, count),
64+
SocketFlags.None,
65+
cancellationToken);
66+
receivedBytes += ret;
67+
return receivedBytes;
68+
}
69+
70+
public override long Seek(long offset, SeekOrigin origin)
71+
{
72+
throw new NotImplementedException();
73+
}
74+
75+
public override void SetLength(long value)
76+
{
77+
throw new NotImplementedException();
78+
}
79+
80+
public override void Write(byte[] buffer, int offset, int count)
81+
{
82+
udpClient.Send(new ReadOnlySpan<byte>(buffer, offset, count));
83+
}
84+
85+
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
86+
{
87+
await udpClient.SendAsync(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken);
88+
}
89+
}
90+
}

QuickProtocol_2.2.sln

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Console", "Console", "{8E34
4949
EndProject
5050
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTest", "Test\ConsoleTest\ConsoleTest.csproj", "{18B3BC1D-D5F9-4D54-81C2-4B4EF3898AF3}"
5151
EndProject
52+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quick.Protocol.Udp", "Quick.Protocol.Udp\Quick.Protocol.Udp.csproj", "{6D0DF333-FB4D-47EE-828A-4775D67D9DB2}"
53+
EndProject
54+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Udp", "Udp", "{AEC82A87-FD4C-4188-84C0-7365DB96AD00}"
55+
EndProject
56+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UdpClient", "Test\Udp\UdpClient\UdpClient.csproj", "{672A0D86-85D2-4439-95E5-EA329F12419A}"
57+
EndProject
58+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UdpServer", "Test\Udp\UdpServer\UdpServer.csproj", "{B06D3B73-70CA-41A7-AE8E-9C79B014657B}"
59+
EndProject
5260
Global
5361
GlobalSection(SolutionConfigurationPlatforms) = preSolution
5462
Debug|Any CPU = Debug|Any CPU
@@ -123,6 +131,18 @@ Global
123131
{18B3BC1D-D5F9-4D54-81C2-4B4EF3898AF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
124132
{18B3BC1D-D5F9-4D54-81C2-4B4EF3898AF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
125133
{18B3BC1D-D5F9-4D54-81C2-4B4EF3898AF3}.Release|Any CPU.Build.0 = Release|Any CPU
134+
{6D0DF333-FB4D-47EE-828A-4775D67D9DB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
135+
{6D0DF333-FB4D-47EE-828A-4775D67D9DB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
136+
{6D0DF333-FB4D-47EE-828A-4775D67D9DB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
137+
{6D0DF333-FB4D-47EE-828A-4775D67D9DB2}.Release|Any CPU.Build.0 = Release|Any CPU
138+
{672A0D86-85D2-4439-95E5-EA329F12419A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
139+
{672A0D86-85D2-4439-95E5-EA329F12419A}.Debug|Any CPU.Build.0 = Debug|Any CPU
140+
{672A0D86-85D2-4439-95E5-EA329F12419A}.Release|Any CPU.ActiveCfg = Release|Any CPU
141+
{672A0D86-85D2-4439-95E5-EA329F12419A}.Release|Any CPU.Build.0 = Release|Any CPU
142+
{B06D3B73-70CA-41A7-AE8E-9C79B014657B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
143+
{B06D3B73-70CA-41A7-AE8E-9C79B014657B}.Debug|Any CPU.Build.0 = Debug|Any CPU
144+
{B06D3B73-70CA-41A7-AE8E-9C79B014657B}.Release|Any CPU.ActiveCfg = Release|Any CPU
145+
{B06D3B73-70CA-41A7-AE8E-9C79B014657B}.Release|Any CPU.Build.0 = Release|Any CPU
126146
EndGlobalSection
127147
GlobalSection(SolutionProperties) = preSolution
128148
HideSolutionNode = FALSE
@@ -142,6 +162,9 @@ Global
142162
{39497212-8529-4418-9D6E-ED9FC546699D} = {434DDF1C-3D83-4B5B-856D-5608C40B58DA}
143163
{8E34D46D-9D1D-40D1-89CD-DCE8EE07F958} = {4D8B91B9-A589-478E-B579-185AE878E575}
144164
{18B3BC1D-D5F9-4D54-81C2-4B4EF3898AF3} = {8E34D46D-9D1D-40D1-89CD-DCE8EE07F958}
165+
{AEC82A87-FD4C-4188-84C0-7365DB96AD00} = {4D8B91B9-A589-478E-B579-185AE878E575}
166+
{672A0D86-85D2-4439-95E5-EA329F12419A} = {AEC82A87-FD4C-4188-84C0-7365DB96AD00}
167+
{B06D3B73-70CA-41A7-AE8E-9C79B014657B} = {AEC82A87-FD4C-4188-84C0-7365DB96AD00}
145168
EndGlobalSection
146169
GlobalSection(ExtensibilityGlobals) = postSolution
147170
SolutionGuid = {EBE52E31-F20D-4366-8517-584BCD5F79FF}

0 commit comments

Comments
 (0)