消息未在客户端正确显示

时间:2015-09-29 16:29:10

标签: c# sockets

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

代码中有什么问题?

2 个答案:

答案 0 :(得分:2)

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

代码已挂起int recv2,因为所有数据都将由第一个ns.read收到,并且上述行中的第二个调用正在阻止,因为不再存在数据。
您需要添加一个“高级协议”,以便识别每个数据包的数据 样品:

000007welcome

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

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

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

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

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

答案 1 :(得分:1)

分析

代码:

// 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课程不保证&#34;数据对应&#34;在Stream.Write()Stream.Read()方法调用之间。例如,如果Stream.Write()方法调用写入6个字节,则第一个Stream.Read()方法调用可能返回1个字节(第一个字节)。

概念解决方案

因为&#34;流媒体&#34;有必要将逻辑消息定义为&#34; extract&#34; (&#34;检测&#34;)来自流的。

需要加入&#34;消息使用&#34;分隔符&#34;发送和&#34;拆分&#34;消息使用&#34;分隔符&#34;收到时可以考虑使用以下替代方案之一来实现&#34;分隔符&#34;概念:

  • 介绍&#34;结束消息&#34;标记物。
  • 介绍包含邮件长度的邮件标题。

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