【问题标题】:Messages aren't properly displayed on the client side消息未在客户端正确显示
【发布时间】:2015-09-29 16:29:10
【问题描述】:

我用 C# 编写了一个简单的服务器和客户端代码。客户端一连接上,服务器就会向客户端一一发送欢迎信息、文件大小和字符串,客户端会显示出来。客户端会将字符串转换为字符串数组并显示出来。之后,客户端将向服务器发送一个 id,服务器将显示它。但问题是,客户端没有正确显示。当我在运行服务器后运行客户端程序时,它显示以下内容,而它应该在一行中显示每条消息。

welcome1.cpp,.jpg,.png

此外,在客户端,我为显示转换后的字符串数组而编写的行根本不起作用,此后的行也没有执行。似乎,代码挂起。我已经在我的代码中标记了它。我的示例代码如下:

服务器:

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;

namespace server
{
    class Program
    {
        static void Main(string[] args)
        {
            // Listen on port 1234.
            try
            {
                TcpListener tcpListener = new TcpListener(IPAddress.Any, 1234);
                tcpListener.Start();              
                byte[] data = new byte[1024];
                // Infinite loop to connect to new clients.
                while (true)
                {
                    // Accept a TcpClient
                    TcpClient tcpClient = tcpListener.AcceptTcpClient();

                    NetworkStream ns = tcpClient.GetStream();
                    //sending welcome message
                    string welcome = "welcome";
                    ns.Write(Encoding.ASCII.GetBytes(welcome), 0, welcome.Length);
                    ns.Flush();

                    //sending file size
                    string fsize = "1";
                    ns.Write(Encoding.ASCII.GetBytes(fsize), 0, fsize.Length);
                    ns.Flush();

                    //sending extensions
                    string[] extensions = { ".cpp", ".jpg", ".png" };
                    string str = string.Join(",", extensions);
                    Console.WriteLine(str);

                    ns.Write(Encoding.ASCII.GetBytes(str), 0, str.Length);
                    ns.Flush();

                    //receiving id
                    int recv = ns.Read(data, 0, data.Length);
                    string id = Encoding.ASCII.GetString(data, 0, recv);

                    Console.WriteLine(id);                   
                }
            }
            catch (Exception e)
            {
                Console.Write(e.Message);
            }
            Console.Read();
        }
    }
}

客户:

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;

namespace client
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {                
                TcpClient tcpClient = new TcpClient("127.0.0.1", 1234);               
                NetworkStream ns = tcpClient.GetStream();
                byte[] data = new byte[1024];

                StreamWriter sWriter = new StreamWriter(tcpClient.GetStream());

                //receiving welcome message
                int recv = ns.Read(data, 0, data.Length);
                string message = Encoding.ASCII.GetString(data, 0, recv);
                Console.WriteLine(message);

                //receive filesize
                int recv2 = ns.Read(data, 0, data.Length);
                string message2 = Encoding.ASCII.GetString(data, 0, recv2);
                Console.WriteLine(message2);

                //receiving extensions
                int recv1 = ns.Read(data, 0, data.Length);
                string message1 = Encoding.ASCII.GetString(data, 0, recv1);
                Console.WriteLine(message1);


                var array2 = message1.Split(',');


                foreach (string s in array2) //from this line the program      isn't working
                {
                    Console.WriteLine(s);
                }

                string input = Console.ReadLine();
                ns.Write(Encoding.ASCII.GetBytes(input), 0, input.Length);
                ns.Flush();
            }
            catch (Exception e)
            {
                Console.Write(e.Message);
            }
            Console.Read();
        }
    }
}

代码有什么问题?

【问题讨论】:

  • 如果您在var array2 ... 设置断点,您将看到该字符串消息可能包含来自服务器的所有数据。 Tcp 连接就像一根水管,你输入的每一个数据都从另一端出来。收集所有数据和处理是您的工作。不能保证 1 个 ns.Write 等于 1 个 ns.Read。
  • @togocoder 我认为代码已经挂在int recv2,因为所有数据都将由第一个ns.read 接收,并且上述行中的第二个调用被阻塞,因为没有更多数据存在。
  • 是的,你是对的。 ns.Read 将阻塞直到有数据或连接关闭

标签: c# sockets


【解决方案1】:

再次看了你的帖子,我确定我的评论才是真正的答案:

代码已经挂在int recv2,因为所有数据都将由第一个ns.read 接收,而上述行中的第二个调用被阻塞,因为没有更多数据存在。
您需要添加一个“高级协议”,以便您识别每个数据包的数据。
示例:

000007welcome

每条消息开头的 6 个字节(虽然 4 个可能就足够了)指定用户数据的长度。因此,您可以轻松地分离看起来像

的数据
000007welcome0000011000014.cpp,.jpg,.png

当然,您必须自己创建用于创建/添加和分离/解释 6 字节标头(实际上只是一个长度信息,但可以增强为包含多个信息)的函数,但这很容易。 一般来说,您应该始终考虑每个操作的时序问题,而不仅仅是添加 2 个整数:文件访问、通过网络或其他媒体传输的数据……实际上是任何类型的硬件访问。因此通过网络发送数据意味着数据包可以

  • 根据需要以正确的顺序到达,但有一些延迟,以便您可以单独阅读它们。不过这不太可能。
  • 以正确的顺序到达,但同时或有很大的延迟(这是几个 100 毫秒,所以对于人类来说并不是很大,但在网络方面确实如此)。这很常见,看你的情况。
  • 部分到达。数据包碎片通常只发生在大数据包(大于 MTU)上,但您应该有一个例程来存储和连接传入的数据,而另一个例程检查存储的数据是否有完整的消息,从存储的数据中删除完整的消息并处理它们。为此,请使用循环(在单独的线程中)并在 ns.DataAvailable 为真时调用 ns.read
  • 以不同的顺序到达。这可能发生在长传输路径上(互联网;很少在 LAN 上)。您必须通过递增的序列号来增强您的协议。
  • 迷路了!在 UDP 上,不会重新发送数据包。在 TCP 上他们会,但实际上我无法判断如果重新发送失败,发送方会发生什么......

这意味着,根据您数据的重要性,您可能需要针对这些情况采取安全措施。
最后,您可能想要处理(意外的)连接丢失...
我还建议您查看我对其他网络问题的回答,以获取有关调试、测试、常见问题的建议......

【讨论】:

【解决方案2】:

分析

代码:

// Receive the welcome message.
int recv = ns.Read(data, 0, data.Length);
string message = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine(message);

// Receive the file size.
int recv2 = ns.Read(data, 0, data.Length);
string message2 = Encoding.ASCII.GetString(data, 0, recv2);
Console.WriteLine(message2);

// Receive the extensions.
int recv1 = ns.Read(data, 0, data.Length);
string message1 = Encoding.ASCII.GetString(data, 0, recv1);
Console.WriteLine(message1);

因为Stream.Read(Byte[], Int32, Int32)方法的返回值如下,所以不能正常工作:

返回值

类型:System.Int32

读入缓冲区的总字节数。 如果当前没有那么多字节可用,这可能小于请求的字节数,如果已到达流的末尾,则为零 (0)。

——Stream.Read Method (Byte[], Int32, Int32), MSDN.

Stream 类不保证Stream.Write()Stream.Read() 方法调用之间的“数据对应”。例如,如果 Stream.Write() 方法调用写入 6 个字节,则第一个 Stream.Read() 方法调用可能返回 1 个字节(第一个字节)。

概念解决方案

由于“流”,有必要定义逻辑消息以从流中“提取”(“检测”)它们。

发送时需要使用“分隔符”“加入”消息,接收时使用“分隔符”“拆分”消息。可以考虑使用以下替代方案之一来实现“分隔符”概念:

  • 引入“消息结束”标记。
  • 引入包含消息长度的消息头。

TCP/IP client-server application: exchange with string messages 这篇小文章可能有助于理解上述替代方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-24
    • 2018-03-03
    • 1970-01-01
    • 2016-10-11
    • 1970-01-01
    • 2012-09-15
    相关资源
    最近更新 更多