我编写了一个TCP服务器应用程序,我可以在专用端口上侦听传入连接。有了这个,我就能得到一个“连接”事件,然后接收数据(只有一次)。
如何从端口连续接收数据(也可能检测客户端是否仍然连接)?
我连接了一个NodeMCU(基于Arduino)系统,该系统每秒使用TCP连接发送一些温度数据。
通过UI中的切换开关启动和停止服务器:
public async Task<bool> StartListeningAsync()
{
if (TCPSocket == null)
{
TCPSocket = new StreamSocketListener();
TCPSocket.ConnectionReceived += LocalSocketConnectionReceived;
await TCPSocket.BindServiceNameAsync(CommunicationPort);
return true;
}
return false;
}
public async Task<bool> StopListening()
{
if (connectedSocket != null)
{
connectedSocket.Dispose();
connectedSocket = null;
}
if (TCPSocket != null)
{
await TCPSocket.CancelIOAsync();
TCPSocket.ConnectionReceived -= LocalSocketConnectionReceived;
TCPSocket.Dispose();
TCPSocket = null;
return true;
}
return false;
}
处理新连接并接收数据的事件:
private async void LocalSocketConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
if (connectedSocket != null)
{
connectedSocket.Dispose();
connectedSocket = null;
}
connectedSocket = args.Socket;
await textBox_send.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
textBox_send.IsEnabled = true;
txtConnected.Text = "Client Connected";
});
using (var reader = new DataReader(args.Socket.InputStream))
{
await readTCPDataAsync(reader);
}
}
private async Task readTCPDataAsync(DataReader reader)
{
reader.InputStreamOptions = InputStreamOptions.None;
// Read the length of the payload that will be received.
byte[] payloadSize = new byte[(uint)BitConverter.GetBytes(0).Length];
await reader.LoadAsync((uint)payloadSize.Length);
reader.ReadBytes(payloadSize);
// Read the payload.
int size = BitConverter.ToInt32(payloadSize, 0);
//size = 2;
byte[] payload = new byte[size];
await reader.LoadAsync((uint)size);
reader.ReadBytes(payload);
string data = Encoding.ASCII.GetString(payload);
}
此代码完美地用于在建立连接后接收数据。
我正在考虑在输入缓冲区上有新数据然后处理数据时获取事件的解决方案。
答案 0 :(得分:1)
我想到一个解决方案,一旦新数据出现在输入缓冲区上,然后处理数据,就会获得一个事件。
UWP API中没有可以在每次收到新日期时触发的此类事件。我们通常在这里做的是使用while循环来连续接收数据。例如,您可以在LocalSocketConnectionReceived
方法中添加while循环,如下所示:
using (var reader = new DataReader(args.Socket.InputStream))
{
while (true)
{
await readTCPDataAsync(reader);
}
}
while循环在这里工作,因为DataReader.LoadAsync(UInt32)是一个异步方法。如果没有收到日期,它将在那里等待。
有关详细信息,请参阅GitHub上的StreamSocket sample,尤其是方案1中的OnConnection
方法。
/// <summary>
/// Invoked once a connection is accepted by StreamSocketListener.
/// </summary>
/// <param name="sender">The listener that accepted the connection.</param>
/// <param name="args">Parameters associated with the accepted connection.</param>
private async void OnConnection(
StreamSocketListener sender,
StreamSocketListenerConnectionReceivedEventArgs args)
{
DataReader reader = new DataReader(args.Socket.InputStream);
try
{
while (true)
{
// Read first 4 bytes (length of the subsequent string).
uint sizeFieldCount = await reader.LoadAsync(sizeof(uint));
if (sizeFieldCount != sizeof(uint))
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Read the string.
uint stringLength = reader.ReadUInt32();
uint actualStringLength = await reader.LoadAsync(stringLength);
if (stringLength != actualStringLength)
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Display the string on the screen. The event is invoked on a non-UI thread, so we need to marshal
// the text back to the UI thread.
NotifyUserFromAsyncThread(
String.Format("Received data: \"{0}\"", reader.ReadString(actualStringLength)),
NotifyType.StatusMessage);
}
}
catch (Exception exception)
{
// If this is an unknown status it means that the error is fatal and retry will likely fail.
if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
{
throw;
}
NotifyUserFromAsyncThread(
"Read stream failed with error: " + exception.Message,
NotifyType.ErrorMessage);
}
}