使用TcpClient类C#的异步套接字客户端 [英] Async socket client using TcpClient class C#

查看:79
本文介绍了使用TcpClient类C#的异步套接字客户端的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用TcpClient类实现了套接字客户端.这样我就可以发送和接收数据,一切正常.但是我问那里的一些专家:)我的实现有问题吗?也许有更好的做事方法.特别是如何处理断开连接?是否有一些指示器(或者我可以自己写一个)告诉我套接字已断开连接?

I have implemented a socket client using TcpClient class. 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?

我也研究了Socket类的异步等待功能,但是无法绕过"SocketAsyncEventArgs",为什么它首先出现在这里.为什么我不能:等待Client.SendAsync("data"); ?

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. Why cant i just: await 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));
    }
}

以及用法:

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();
}

推荐答案

好吧,我花了10秒钟才发现您可以解决的最大问题:

Okay it took me 10 seconds to find biggest issue you could've done :

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.

首先让我解释一下为什么这是一个问题.

First let me explain why it's such a big issue.

假设您要发送的邮件长度为 4097字节.您的缓冲区只能接受 4096字节,这意味着您无法将整个邮件打包到该缓冲区中.

Assume that you're sending message which is 4097 bytes long. Your buffer can only accept 4096 bytes meaning that you cannot pack whole message into this buffer.

假设您要发送的消息长度为 12个字节.您仍在内存中分配 4096字节,仅用于存储 12字节.

Assume you're sending message which is 12 bytes long. You're still allocating 4096 bytes in your memory just to store 12 bytes.

如何处理?

每次与网络打交道时,都应考虑制作某种协议(有人称其为消息框架,但这只是一个协议),它将帮助您识别进来的包裹.

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.

协议示例可能是:

[1B =消息类型] [4B =长度] [XB =消息]
-其中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.

      这篇关于使用TcpClient类C#的异步套接字客户端的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆