C# 如果我们不从TcpClient流中读取数据,接收到的数据会发生什么变化
我对tcp流媒体中的一个案例很好奇 假设我们创建了一个TcpClient,并将一些有意义的请求字符串写入传出流。例如Http请求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
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会立即从目标套接字接收响应
以下是我的问题:
client.ReceiveBufferSize
。网卡上还有少量缓冲存储器,这取决于网卡client.ReceiveBufferSize
。网卡上还有少量缓冲存储器,这取决于网卡与许多其他事情一样,答案在于分层 那么,让我们从硬件开始: 所有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);
}
}
}