Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如果我们不从TcpClient流中读取数据,接收到的数据会发生什么变化_C#_Sockets_Tcp - Fatal编程技术网

C# 如果我们不从TcpClient流中读取数据,接收到的数据会发生什么变化

C# 如果我们不从TcpClient流中读取数据,接收到的数据会发生什么变化,c#,sockets,tcp,C#,Sockets,Tcp,我对tcp流媒体中的一个案例很好奇 假设我们创建了一个TcpClient,并将一些有意义的请求字符串写入传出流。例如Http请求 try { string requestString = "GET /Api/Test HTTP/1.1 \r\n" + "Host: 192.168.2.45 \r\n" + "Conne

我对tcp流媒体中的一个案例很好奇

假设我们创建了一个TcpClient,并将一些有意义的请求字符串写入传出流。例如Http请求

try
        {
            string requestString = "GET /Api/Test HTTP/1.1 \r\n" +
                                   "Host: 192.168.2.45 \r\n" +
                                   "Connection:close \r\n\r\n";

            TcpClient client = new TcpClient();

            client.Connect(new IPEndPoint(IPAddress.Parse("192.168.2.45"), 80));

            NetworkStream stream = client.GetStream();

            byte[] reqBuffer = System.Text.Encoding.Default.GetBytes(requestString);

            stream.Write(reqBuffer, 0, reqBuffer.Length);
因此,NIC会立即从目标套接字接收响应

以下是我的问题:

  • 传入的字节存储在哪里?(NIC是否有存储器,还是存储在RAM中?)
  • 如果我们不从应用程序读取这些字节,NIC会如何处理这些字节
  • 阅读:

  • 它存储在RAM中,使用的RAM量为
    client.ReceiveBufferSize
    。网卡上还有少量缓冲存储器,这取决于网卡

  • 一旦RAM缓冲区满了,操作系统将停止从网卡读取数据,这将导致网卡的缓冲区满了,并停止确认传入的数据包。这将导致收缩到0,发送方将停止发送,直到收到来自网卡的ACK(只有当数据离开网卡缓冲区时才会发生这种情况)

  • 它存储在RAM中,使用的RAM量为
    client.ReceiveBufferSize
    。网卡上还有少量缓冲存储器,这取决于网卡

  • 一旦RAM缓冲区满了,操作系统将停止从网卡读取数据,这将导致网卡的缓冲区满了,并停止确认传入的数据包。这将导致收缩到0,发送方将停止发送,直到收到来自网卡的ACK(只有当数据离开网卡缓冲区时才会发生这种情况)


  • 与许多其他事情一样,答案在于分层

    那么,让我们从硬件开始:

    所有NIC都有一些内部缓冲区。这是第一个集合任何响应的地方——但在这个层次上,像TCP这样的东西实际上意义不大;NIC只关心自己的网络协议,例如以太网或PPP。在这个级别上,IP只是一个无差别的有效负载,而IP又将TCP作为有效负载(尽管应该注意的是,分层还远远不够完美:)例如,TCP和IP之间有很多耦合

    必须先解释这些传入数据,然后才能执行任何操作;让我们跳过细节,假设NIC缓冲区现在包含一个漂亮的小TCP/IP数据包。现在,NIC驱动程序开始发挥作用——您机器上的每个打开的端口都有一块相关的内存用于接收数据。基本上,这是您在设置
    ReceiveBufferSize
    SendBufferSize
    时控制的内容。驱动程序将指示NIC如何处理传入数据-通常,NIC将使用DMA将数据直接发送到RAM。这是非常快的-现代NIC并不需要有大的板载内存芯片;即使对于服务器NIC,数量通常也在32 MiB左右

    这两个RAM缓冲区对于您的问题来说是最重要的-当它们已满时,NIC将简单地丢弃任何进一步到达的数据包。对于TCP,它有流量控制,它会告诉对方停止传输,谢谢。实际上,这模拟了缓冲流的通常行为——发送方将被阻止,直到有可能再次发送另一段数据。当这种情况发生时,发送方将重新传输上次未传输的数据。在像UDP这样的协议中,没有流量控制,也没有重传,因此您就无法挽回地丢失数据(甚至没有告诉您有问题)

    如果您有一个挂起的发送/接收操作(例如,
    NetworkStream.Read
    ),您也将使用自己的缓冲区-这是另一层,但它实际上是最不重要的。这里发生的一切是,当操作系统从NIC驱动程序获取信息时,它将用内部缓冲区的数据填充缓冲区,并向您发送信号。在同步场景中(如您的情况),这只会导致
    Read
    调用返回。在.NET和操作系统交互生成回调的情况下,异步场景有点棘手,但其余情况基本相同。例如,这与从本地硬盘读取文件没有根本区别


    在.NET中需要注意的一点是,在这些发送/接收中使用的缓冲区在操作期间是固定的。这意味着禁止在内存中移动缓冲区,这会降低垃圾收集器的有效性(防止正确的堆压缩);如果您有很多长时间运行的操作,您确实希望尽可能多地重用缓冲区-如果您总是为每个操作创建一个新的缓冲区,您可能会面临堆碎片问题。

    与其他许多事情一样,答案是分层

    那么,让我们从硬件开始:

    所有NIC都有一些内部缓冲区。这是第一个集合任何响应的地方——但在这个层次上,像TCP这样的东西实际上意义不大;NIC只关心自己的网络协议,例如以太网或PPP。在这个级别上,IP只是一个无差别的有效负载,而IP又将TCP作为有效负载(尽管应该注意的是,分层还远远不够完美:)例如,TCP和IP之间有很多耦合

    必须先解释这些传入数据,然后才能执行任何操作;让我们跳过细节,假设NIC缓冲区现在包含一个漂亮的小TCP/IP数据包。现在,NIC驱动程序开始发挥作用——您机器上的每个打开的端口都有一块相关的内存用于接收数据。基本上,这是您在设置
    ReceiveBufferSize
    SendBufferSize
    时控制的内容。驱动程序将指示NIC如何处理传入数据-通常,NIC将使用DMA将数据直接发送到RAM。这是非常重要的
    if (stream.CanRead)
    {
       bufferInt = stream.Read(buffer, 0, client.ReceiveBufferSize);
    }
    
    public class Program
    {
        public static void Main(string[] args)
        {
            TcpListener server = new TcpListener(IPAddress.Any, 8989);
            server.Start();
            server.BeginAcceptSocket(AcceptSocket, server);
    
            TcpClient client = new TcpClient();
            client.Connect("127.0.0.1", 8989);
    
            FileStream dataToSend = File.OpenRead("c:\\temp\\videoUpload.rar");
    
            byte[] tempSendBuffer = new byte[4096];
            int numberOfBytesRead = 0;
            while ((numberOfBytesRead = dataToSend.Read(tempSendBuffer, 0, tempSendBuffer.Length)) > 0)
            {
                client.GetStream().Write(tempSendBuffer, 0, numberOfBytesRead);
                Console.WriteLine("{0} bytes sent", numberOfBytesRead);
            }
            Console.ReadLine();
        }
    
        private static void AcceptSocket(IAsyncResult asyncResult)
        {
            Socket localSocket = (asyncResult.AsyncState as TcpListener).EndAcceptSocket(asyncResult);
            while (true)
            {
                if (localSocket.Available > 0)
                {
                    Console.WriteLine("Local Socket has {0} bytes pending to be received. Enter to receive", localSocket.Available);
                    Console.ReadLine();
    
                    byte[] tempReadBuffer = new byte[4096];
                    int numberOfReceivedBytes = localSocket.Receive(tempReadBuffer);
                    Console.WriteLine("{0} bytes RECEIVED", numberOfReceivedBytes);
                }
                Thread.Sleep(500);
            }
        }
    }