[英]Async socket client using TcpClient class C#
I have implemented a socket client using TcpClient class. 我已经使用TcpClient类实现了一个套接字客户端。 So I can send and receive data, everything works good.
所以我可以发送和接收数据,一切都很好。 But I ask some of the guru's out there :) Is there something wrong with my implementation?
但我问一些大师在那里:)我的实施有什么问题吗? maybe there is a better way of doing things.
也许有更好的做事方式。 In particular, how do I handle disconnects?
特别是,我该如何处理断开连接? Is there some indicator (or maybe I can write one myself) that tells me that socket has disconnected?
是否有一些指示器(或者我可以自己编写一个)告诉我套接字已断开连接?
I also have looked into async await features of Socket class, but can't wrap my head around "SocketAsyncEventArgs", why is it there in the first place. 我也研究过异步等待Socket类的功能,但不能把我的脑袋包裹起来“SocketAsyncEventArgs”,为什么它首先出现在那里。 Why cant i just: await Client.SendAsync("data");
为什么我不能: 等待Client.SendAsync(“data”); ?
?
public class Client
{
private TcpClient tcpClient;
public void Initialize(string ip, int port)
{
try
{
tcpClient = new TcpClient(ip, port);
if (tcpClient.Connected)
Console.WriteLine("Connected to: {0}:{1}", ip, port);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Initialize(ip, port);
}
}
public void BeginRead()
{
var buffer = new byte[4096];
var ns = tcpClient.GetStream();
ns.BeginRead(buffer, 0, buffer.Length, EndRead, buffer);
}
public void EndRead(IAsyncResult result)
{
var buffer = (byte[])result.AsyncState;
var ns = tcpClient.GetStream();
var bytesAvailable = ns.EndRead(result);
Console.WriteLine(Encoding.ASCII.GetString(buffer, 0, bytesAvailable));
BeginRead();
}
public void BeginSend(string xml)
{
var bytes = Encoding.ASCII.GetBytes(xml);
var ns = tcpClient.GetStream();
ns.BeginWrite(bytes, 0, bytes.Length, EndSend, bytes);
}
public void EndSend(IAsyncResult result)
{
var bytes = (byte[])result.AsyncState;
Console.WriteLine("Sent {0} bytes to server.", bytes.Length);
Console.WriteLine("Sent: {0}", Encoding.ASCII.GetString(bytes));
}
}
And the usage: 用法:
static void Main(string[] args)
{
var client = new Client();
client.Initialize("127.0.0.1", 8778);
client.BeginRead();
client.BeginSend("<Names><Name>John</Name></Names>");
Console.ReadLine();
}
Okay it took me 10 seconds to find biggest issue you could've done : 好吧,我花了10秒钟才找到你可以做到的最大问题:
public void BeginRead()
{
var buffer = new byte[4096];
var ns = tcpClient.GetStream();
ns.BeginRead(buffer, 0, buffer.Length, EndRead, buffer);
}
But don't worry that's why we are on SO. 但不要担心这就是为什么我们在SO上。
First let me explain why it's such a big issue. 首先让我解释为什么这是一个很大的问题。
Assume that you're sending message which is 4097 bytes long. 假设您正在发送长度为4097字节的消息。 Your buffer can only accept 4096 bytes meaning that you cannot pack whole message into this buffer.
您的缓冲区只能接受4096个字节,这意味着您无法将整个消息打包到此缓冲区中。
Assume you're sending message which is 12 bytes long. 假设您正在发送长度为12个字节的消息。 You're still allocating 4096 bytes in your memory just to store 12 bytes .
你仍在内存中分配4096个字节只是为了存储12个字节 。
How to deal with this? 怎么处理这个?
Every time you work with networking you should consider making some kind of protocol ( some people call it message framing , but it's just a protocol ) that will help you to identify incomming package. 每次使用网络时,您都应该考虑制定某种协议 (有些人称之为消息框架 ,但它只是一个协议),这将有助于您识别包装。
Example of a protocol could be : 协议示例可以是:
[1B = type of message][4B = length][XB = message]
[1B =消息类型] [4B =长度] [XB =消息]
- where X == BitConvert.ToInt32(length);
Receiver: 接收器:
byte messageType = (byte)netStream.ReadByte(); byte[] lengthBuffer = new byte[sizeof(int)]; int recv = netStream.Read(lengthBuffer, 0, lengthBuffer.Length); if(recv == sizeof(int)) { int messageLen = BitConverter.ToInt32(lengthBuffer, 0); byte[] messageBuffer = new byte[messageLen]; recv = netStream.Read(messageBuffer, 0, messageBuffer.Length); if(recv == messageLen) { // messageBuffer contains your whole message ... } }
Sender: 发件人:
byte messageType = (1 << 3); // assume that 0000 1000 would be XML byte[] message = Encoding.ASCII.GetBytes(xml); byte[] length = BitConverter.GetBytes(message.Length); byte[] buffer = new byte[sizeof(int) + message.Length + 1]; buffer[0] = messageType; for(int i = 0; i < sizeof(int); i++) { buffer[i + 1] = length[i]; } for(int i = 0; i < message.Length; i++) { buffer[i + 1 + sizeof(int)] = message[i]; } netStream.Write(buffer);
Rest of your code looks okay. 其余的代码看起来还不错。 But in my opinion using asynchronous operations in your case is just useless.
但在我看来,在你的情况下使用异步操作是没用的。 You can do the same with synchronous calls.
您可以对同步调用执行相同的操作。
It's hard to answer because theres no exact question here but more some kind of code review. 这很难回答,因为这里没有确切的问题,但更多的是某种代码审查。 But still some hints:
但仍有一些提示:
TcpClient.Connected
will block until the connection was established. TcpClient.Connected
将阻止,直到建立连接。 So it will often just fail as the connection is in progress and then you start all over again. Connect
method. Connect
方法。 Async
methods which return a Task
, because these can be easily combined with async/await. Task
的Async
方法,因为这些方法可以很容易地与async / await结合使用。 The reading side of the code with TPL methods (untested): 使用TPL方法(未经测试)的代码读取方:
public async Task Initialize(string ip, int port)
{
tcpClient = new TcpClient;
await tcpClient.ConnectAsync(ip, port);
Console.WriteLine("Connected to: {0}:{1}", ip, port);
}
public async Task Read()
{
var buffer = new byte[4096];
var ns = tcpClient.GetStream();
while (true)
{
var bytesRead = await ns.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0) return; // Stream was closed
Console.WriteLine(Encoding.ASCII.GetString(buffer, 0, bytesRead));
}
}
In the initialization part you would do: 在初始化部分,您将执行以下操作:
await client.Initialize(ip, port);
// Start reading task
Task.Run(() => client.Read());
For using synchronous methods delete all Async
occurences and replace Task with Thread. 对于使用同步方法,删除所有
Async
并用线程替换任务。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.