消息没有正确地显示在客户端
本文关键字:客户端 显示 正确地 消息 | 更新日期: 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客户机-服务器应用程序:与字符串消息交换,可能有助于理解上述替代方案。
标题>