【发布时间】:2019-01-20 11:48:59
【问题描述】:
我正在尝试在客户端之间发送数据,当通过 tcp 发送数据时,它是一个流,而不是“一发一收”功能。因此我为网络流写了一个小包装器。它有效,但我错过了什么吗?这是解决我的问题的正确方法吗?
我的网络知识不是很好。
(我的async知识也很有限,第一次用这个,如果有什么不对的地方请指教。)
为了完整起见,我还包括了我的测试代码:
主要:
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
HandlerServer();
HandleClients();
while (true)
Thread.Sleep(100);
}
处理函数:
private static async void HandleClients()
{
using (TcpClient client = new TcpClient())
{
await client.ConnectAsync(IPAddress.Parse("127.0.0.1"), 8888);
using (TcpMessageStream stream = new TcpMessageStream(client))
{
while (true)
await stream.SendAsync(Encoding.ASCII.GetBytes(Console.ReadLine()));
}
}
}
private async static void HandlerServer()
{
TcpListener server = new TcpListener(IPAddress.Any, 8888);
server.Start();
using (TcpClient cl = await server.AcceptTcpClientAsync())
using (TcpMessageStream stream = new TcpMessageStream(cl))
{
Console.WriteLine("Client connected!!");
while (true)
Console.WriteLine("Received: " + Encoding.ASCII.GetString(await stream.ReceiveAsync()));
}
}
创建的类:
class TcpMessageStream : IDisposable
{
NetworkStream _stream;
public TcpMessageStream(TcpClient tcpClient)
{
_stream = tcpClient.GetStream();
}
public async Task SendAsync(byte[] data)
{
byte[] prefix = BitConverter.GetBytes(data.LongLength);
await _stream.WriteAsync(prefix);
await _stream.WriteAsync(data);
}
public async Task<byte[]> ReceiveAsync()
{
byte[] buffer = new byte[8];
int bytesRead = 0;
do
{
bytesRead += await _stream.ReadAsync(buffer, bytesRead, 8 - bytesRead);
} while (bytesRead < 8);
var dataLength = BitConverter.ToInt64(buffer);
byte[] messageBuffer = new byte[dataLength];
bytesRead = 0;
do
{
bytesRead += await _stream.ReadAsync(messageBuffer, bytesRead, messageBuffer.Length - bytesRead);
} while (bytesRead < messageBuffer.Length);
return messageBuffer;
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
// Dispose managed resources.
_stream.Dispose();
}
// Clean up unmanaged resources here.
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~TcpMessageStream()
{
Dispose(false);
}
}
【问题讨论】:
-
异步代码实际上不在单独的线程上运行。因此,您的 while 循环将阻塞当前线程,并且任何其他“异步”代码将无法运行。考虑启动一个线程来处理客户端和服务器代码。
-
@JessedeWit 但是,while 循环中的“等待”不是将“线程”返回给等待时需要执行的其他代码吗?
-
理论上是的。但是它遇到的其他代码将无法执行,因为当前线程已被占用/工作。
Console.Readline()也会阻塞当前线程,这意味着只有当您向客户端发送数据包时,您的线程才会空闲。您的服务器端代码可能没问题,因为ReceiveAsync将释放当前线程。 -
@JessedeWit 啊,我明白了,谢谢!我会在我的实际项目中这样做。但理论上如果
Console.Readline()也是异步的,那么这个示例代码就可以了吗? -
是的,我认为应该。 (如果您使用
await Task.Delay()而不是Thread.Sleep(),这也是阻塞的)请注意,您将无法捕获任何异常,但是您可能希望在异步函数中添加一些异常处理。
标签: c# networking packet