通过 tcp 网络发送数据包
Sending packets over tcp network
我正在尝试在客户端之间发送数据,当通过 tcp 发送数据时,它是一个流而不是 'one send is one receive' 函数。因此我为网络流写了一个小包装器。它有效,但我错过了什么吗?这是解决我的问题的正确方法吗?
我的网络知识不是很好
(本人对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:
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);
}
}
一般来说,这是个好主意。您已经创建了一个简单的协议,它在数据之前写入一个长度前缀,以便您知道要读取多少。这个很常用,简单高效。
你可以把代码简化很多。 BinaryReader
有读写整数和字节数组的方法。您可以读取精确大小的字节数组。 BinaryReader
为您做循环。
如果您不想 BinaryReader
用于教育目的,那么请自己制作一个辅助函数来读取准确的字节数。
我会 TcpMessageStream
基于 Stream
,而不是 TcpClient
。这使它更通用。
异步用法正确。
您使用的处置模式在这里没有用。终结器什么也不做,永远不会有任何继承者。不需要这个布尔 disposed
标志。编写一个正常的 void Dispose()
方法来完成所需的工作。
总而言之,这是一项非常好的工作。网络代码很难。
如果这是真实应用程序的一部分,请考虑完全不要编写自己的网络协议。尝试使用 HTTP 或 websockets。在抽象堆栈中尽可能高。
我正在尝试在客户端之间发送数据,当通过 tcp 发送数据时,它是一个流而不是 'one send is one receive' 函数。因此我为网络流写了一个小包装器。它有效,但我错过了什么吗?这是解决我的问题的正确方法吗?
我的网络知识不是很好
(本人对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:
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);
}
}
一般来说,这是个好主意。您已经创建了一个简单的协议,它在数据之前写入一个长度前缀,以便您知道要读取多少。这个很常用,简单高效。
你可以把代码简化很多。 BinaryReader
有读写整数和字节数组的方法。您可以读取精确大小的字节数组。 BinaryReader
为您做循环。
如果您不想 BinaryReader
用于教育目的,那么请自己制作一个辅助函数来读取准确的字节数。
我会 TcpMessageStream
基于 Stream
,而不是 TcpClient
。这使它更通用。
异步用法正确。
您使用的处置模式在这里没有用。终结器什么也不做,永远不会有任何继承者。不需要这个布尔 disposed
标志。编写一个正常的 void Dispose()
方法来完成所需的工作。
总而言之,这是一项非常好的工作。网络代码很难。
如果这是真实应用程序的一部分,请考虑完全不要编写自己的网络协议。尝试使用 HTTP 或 websockets。在抽象堆栈中尽可能高。