c# - 使用 TcpClient 类 C# 的异步套接字客户端

标签 c# sockets asynchronous tcp tcpclient

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

我也研究了 Socket 类的异步等待功能,但我无法理解“SocketAsyncEventArgs”,为什么它首先在那里。 为什么我不能只是: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 秒才找到你本可以解决的最大问题:

public void BeginRead()
{
    var buffer = new byte[4096];
    var ns = tcpClient.GetStream();
    ns.BeginRead(buffer, 0, buffer.Length, EndRead, buffer);
}

但别担心,这就是我们采用 SO 的原因。


首先让我解释一下为什么它是一个问题。

假设您发送的消息长度为 4097 字节。您的缓冲区只能接受 4096 字节,这意味着您不能将整个消息打包到此缓冲区中。

假设您要发送12 字节 长的消息。您仍在内存中分配 4096 字节 只是为了存储 12 字节

如何处理?

每次使用网络时,您都应该考虑制定某种协议(protocol)(有些人称之为消息框架,但它只是一种协议(protocol)),这将帮助您识别传入的包。

协议(protocol)示例可以是:

[1B = type of message][4B = length][XB = message]
- where X == BitConvert.ToInt32(length);

  • 接收者:

    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 ...
        }
    }
    
  • 发件人:

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

您的其余代码看起来没问题。但在我看来,在你的情况下使用异步操作是没有用的。您可以对同步调用执行相同的操作。

关于c# - 使用 TcpClient 类 C# 的异步套接字客户端,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41718342/

相关文章:

c# - 解析字符串 - 有比检查每一行更有效的方法吗?

python - 客户端(python程序)没有收到服务器(c程序)返回的响应?

c - 将 STDIN 重定向到套接字并将套接字重定向到 STDOUT

swift 4 : How to asynchronously use URLSessionDataTask but have the requests be in a timed queue?

.net - 流多播 - 一次读取一个流,但以不同的方式处理它,缓冲最少

javascript - 与 Q.all(promises) 同步解决 promise

c# - 使用 net.tcp 绑定(bind)在 IIS 7 上托管 WCF 服务 - 使用服务时出错

c# - Silverlight 启用的 WCF 服务与 Web 服务与 ADO.NET 数据服务

c# - 敌人脚本在我的塔防游戏 c# 上无法正常工作

c# - 异步套接字将多条消息作为一条消息读取