167 lines
5.6 KiB
C#
167 lines
5.6 KiB
C#
using Windows.Storage.Streams;
|
||
using Windows.Networking;
|
||
using Windows.Networking.Sockets;
|
||
using System;
|
||
using System.Threading.Tasks;
|
||
using System.Threading;
|
||
|
||
namespace Plane.Communication
|
||
{
|
||
public partial class UdpThroughDtuServiceConnection
|
||
{
|
||
#region Fields
|
||
private IOutputStream _outputStream;
|
||
private DatagramSocket _socket;
|
||
private DataWriter _writer;
|
||
private readonly AutoResetEvent _loginResultSignal = new AutoResetEvent(false);
|
||
private bool _isLogined; //是否已登录成功
|
||
private byte[] loginResultDatagram; //记录登陆时返回的报文数据
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 初始化 <see cref="UdpThroughDtuServiceConnection"/> 的实例。
|
||
/// </summary>
|
||
/// <param name="ip"></param>
|
||
/// <param name="port"></param>
|
||
public UdpThroughDtuServiceConnection(string ip, int port)
|
||
{
|
||
DtuServiceIP = ip;
|
||
DtuServicePort = port;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 登陆到Dtu服务端。
|
||
/// 登陆方式极其简单,只需发送一个数据包(内含希望连接到的飞机ID)
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public virtual async Task OpenAsync()
|
||
{
|
||
if (IsOpen)
|
||
{
|
||
return;
|
||
}
|
||
|
||
await Task.Run(async () =>
|
||
{
|
||
var bytes = ConstructHandShakeDatagramToDTUService();
|
||
if (_socket == null)
|
||
{
|
||
var hostName = new HostName(DtuServiceIP);
|
||
_socket = new DatagramSocket();
|
||
|
||
//对于WP平台,若没有打开网络连接(比如WIFI),下面语句会报:
|
||
//“No such host is known.(Exception from HRESULT:0x80072AF9)”
|
||
try
|
||
{
|
||
await _socket.ConnectAsync(hostName, DtuServicePort.ToString());
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_socket = null;
|
||
if ((uint)ex.HResult == 0x80072AF9)
|
||
{
|
||
throw new Exception("请检查是否已打开网络连接。");
|
||
}
|
||
else
|
||
{
|
||
throw ex;
|
||
}
|
||
}
|
||
_socket.MessageReceived += _socket_MessageReceived;
|
||
_outputStream = await _socket.GetOutputStreamAsync(hostName, DtuServicePort.ToString());
|
||
_writer = new DataWriter(_outputStream);
|
||
}
|
||
|
||
_writer.WriteBytes(bytes);
|
||
await _writer.StoreAsync();
|
||
|
||
//等待服务端的应答,获取是否连接成功飞机的结果
|
||
if (_loginResultSignal.WaitOne(10000))
|
||
{
|
||
var ret = AnalyzeHandShakeDatagramFromDTUService(loginResultDatagram);
|
||
if (ret == DTUHandShakeResult.Successful)
|
||
{
|
||
IsOpen = true;
|
||
_isLogined = true;
|
||
}
|
||
else if (ret == DTUHandShakeResult.Occupied)
|
||
{
|
||
throw new Exception("飞机已被占用!");
|
||
}
|
||
else if (ret == DTUHandShakeResult.NotExisted)
|
||
{
|
||
throw new Exception("飞机不存在!");
|
||
}
|
||
else
|
||
{
|
||
throw new Exception("未知状态!");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
IsOpen = false;
|
||
throw new Exception("连接超时!");
|
||
}
|
||
}).ConfigureAwait(false);
|
||
}
|
||
|
||
public virtual void Close()
|
||
{
|
||
IsOpen = false;
|
||
_outputStream?.Dispose();
|
||
_writer?.Dispose();
|
||
if (_socket != null)
|
||
{
|
||
try
|
||
{
|
||
_socket.MessageReceived -= _socket_MessageReceived;
|
||
_socket?.Dispose();
|
||
_socket = null;
|
||
}
|
||
catch (Exception) { }
|
||
}
|
||
}
|
||
|
||
private async Task SendAsync(byte[] datagram, int bytes)
|
||
{
|
||
if (datagram.Length != bytes)
|
||
{
|
||
Array.Resize(ref datagram, bytes);
|
||
}
|
||
try
|
||
{
|
||
_writer.WriteBytes(datagram);
|
||
await _writer.StoreAsync();
|
||
}
|
||
catch
|
||
{
|
||
//发送异常可以内部消化之,因为Udp发送失败也无办法,同时也不用冒泡异常
|
||
}
|
||
}
|
||
|
||
private void _socket_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
|
||
{
|
||
try
|
||
{
|
||
var reader = args.GetDataReader();
|
||
if (!_isLogined)
|
||
{
|
||
loginResultDatagram = new byte[reader.UnconsumedBufferLength];
|
||
reader.ReadBytes(loginResultDatagram);
|
||
_loginResultSignal.Set();
|
||
}
|
||
else
|
||
{
|
||
//以后收到的直接透传
|
||
var data = new byte[reader.UnconsumedBufferLength];
|
||
reader.ReadBytes(data);
|
||
EnqueueDatagram(data);
|
||
}
|
||
}
|
||
catch (Exception)
|
||
{
|
||
}
|
||
}
|
||
}
|
||
}
|