在循环中接收网络数据的正确方法

时间:2014-03-26 11:01:31

标签: c# sockets loops networking chunks

我正在编写一个C#客户端应用程序,它将连接到用python编写的服务器。我的问题是关于循环接收数据。应用程序结构都是关于客户端请求服务器 - >服务器响应客户端。当消息低于实际缓冲区大小(在服务器中设置)时,一切正常。例如:服务器端缓冲区:1024,客户端缓冲区大小:256,数据长度< 1KB。我使用以下代码运行我的应用程序:

int datacounter = 0;
byte[] recived = new byte[256];
StringBuilder stb = new StringBuilder();
serverStream.ReadTimeout = 1500;
try
{
    while ((datacounter = serverStream.Read(recived, 0, 256)) > 0)
    {
        Console.WriteLine("RECIVED: " + datacounter.ToString());
        stb.append(System.Text.Encoding.UTF8.GetString(recived, 0, datacounter));
    }
}
catch { Console.WriteLine("Timeout!"); }

然后应用程序以4个循环(每个256字节)接收数据:

RECIVED: 256
RECIVED: 256
RECIVED: 256
RECIVED: 96

然后超时滴答,结束传输并将完整数据传递给以后的分析(来自stb对象)。我不认为使用超时是正确的,但我不知道有任何其他方法可以做到这一点。 但是,这种方式有效。这里我们举例说明,但不是: 服务器端缓冲区:1024,客户端缓冲区:256,数据长度~8kbytes(python端循环发送数据)。

RECIVED: 256
RECIVED: 256
RECIVED: 256
RECIVED: 256

然后超时滴答(显然数据不完整 - 得到1kb的8kb)。有时循环甚至在1次运行后以28个重复的字节结束,那就是在超时之前。 Python说数据已经正确发送。这是我创建socket和serverStream对象的方式:

TcpClient clientSocket = new TcpClient();
clientSocket.Connect("x.y.z.x", 1234);
NetworkStream serverStream = clientSocket.GetStream();

它不是TcpClient错误。尝试使用清晰的套接字,创建如下:

new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

然而,它的作用相似。有没有办法,让我的循环工作没有超时,接收所有数据?如果可能的话,我想保持套接字同步。

2 个答案:

答案 0 :(得分:0)

为什么不转储使您的代码进入catch分支并查找的异常? :)

catch (Exception ex) { Console.WriteLine("Timeout because of... " + ex.Message); }

- 编辑 对不起,我没有看到这个问题。您提出的问题是,是否可以在没有超时的情况下执行此操作。是的,不要设置任何超时并检查接收的字节数是否小于缓冲区大小。

那是:

while ((datacounter = serverStream.Read(recived, 0, 256)) > 0)
    {
        Console.WriteLine("RECIVED: " + datacounter.ToString());
        stb.append(System.Text.Encoding.UTF8.GetString(recived, 0, datacounter));
        if(datacounter < 256) //you're good to go
           break; 
    }

答案 1 :(得分:0)

我认为您的接收代码在功能上没有任何问题。我把测试放在一起,接收器尽可能多地发送它(例如8 MB),只要你在没有1.5秒暂停的情况下继续发送,然后再超时。

所以看起来你的服务器根本就没有“快速”发送。

要回答您的问题,时间并不是了解何时收到完整信息的典型方式。确定何时接收到完整消息的一种常见,简单的方法是在发送侧为完整消息的长度添加前缀(例如,4字节int)。然后在接收端,首先读取4个字节,解码为长度,然后读取更多的字节。

您还可以考虑将消息终止字符串(例如Environment.NewLine)附加到邮件的末尾。这样做的好处是可以调用StreamReader.ReadLine(),它将阻塞直到收到完整的消息。这仅在终止不能包含在消息本身中时才有效。

如果您无法更改服务器协议,是否有其他方式可以知道您已收到完整邮件? (例如,在消息末尾检查NewLine,XML结束标记或其他模式。)如果没有,也许您可​​以等待服务器断开连接,否则看起来您将被迫找到正确的时间平衡。

如果你想玩它,我会在下面加上测试代码。

服务器/发送方:

        IPAddress localAddr = IPAddress.Parse("127.0.0.1");
        TcpListener server = new TcpListener(localAddr, 13579);
        server.Start();
        TcpClient clientSocket = server.AcceptTcpClient();
        NetworkStream stream = clientSocket.GetStream();

        int bytesSent = 0;
        int bytesToSend = 1 << 25;
        int bufferSize = 1024;
        string testMessage = new string('X', bufferSize);
        byte[] buffer = UTF8Encoding.UTF8.GetBytes(testMessage);

        while (bytesSent < bytesToSend)
        {
            int byteToSendThisRound = Math.Min(bufferSize, bytesToSend - bytesSent);
            stream.Write(buffer, 0, byteToSendThisRound);
            bytesSent += byteToSendThisRound;
        }

客户/接收方:

        TcpClient client = new TcpClient("127.0.0.1", 13579);
        NetworkStream serverStream = client.GetStream();

        int totalBytesReceived = 0;
        int datacounter = 0;
        byte[] recived = new byte[256];
        StringBuilder stb = new StringBuilder();
        serverStream.ReadTimeout = 1500;
        try
        {
            while ((datacounter = serverStream.Read(recived, 0, 256)) > 0)
            {
                totalBytesReceived += 256;
                Console.WriteLine("RECIVED: {0}, {1}", datacounter, totalBytesReceived);
                stb.Append(System.Text.Encoding.UTF8.GetString(recived, 0, datacounter));
            }
        }
        catch { Console.WriteLine("Timeout!"); }