消息没有正确地显示在客户端

本文关键字:客户端 显示 正确地 消息 | 更新日期: 2023-09-27 18:15:20

我用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();
        }
    }
}

代码有什么问题?

消息没有正确地显示在客户端

再次看了你的帖子后,我确信我的评论是实际的答案:

代码已经挂起在int recv2,因为所有数据将被第一个ns.read接收,并且上述行的第二个调用阻塞,因为没有更多的数据存在。
你需要添加一个"高级协议",让你识别每个数据包的数据。示例:

000007welcome

在每条消息的开始处使用6个字节(4个就足够了)指定用户数据的长度。这样你就可以很容易地把数据分开就像

000007welcome0000011000014.cpp,.jpg,.png

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

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

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

分析

代码:

// 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)方法有以下返回值:

返回值

类型:系统。Int32

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

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

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

<标题>概念性解决方案

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

要求发送时使用"分隔符"连接"消息,接收时使用"分隔符"分离"消息。可以考虑以下替代方案之一来实现"分隔符"概念:

  • 引入"end-of- message"标记。
  • 引入包含消息长度的消息头。

小文章,TCP/IP客户机-服务器应用程序:与字符串消息交换,可能有助于理解上述替代方案。