【问题标题】:TCP read and write from client and serverTCP 从客户端和服务器读取和写入
【发布时间】:2016-07-20 13:23:36
【问题描述】:

我正在尝试制作一个可以在彼此之间来回读取和写入信息的客户端和服务器。我可以从客户端写入,服务器读取它,但反之亦然,我不知道为什么。这里似乎没有人知道我之前什么时候问过,而且我在网上找不到任何有效的东西。如果你知道,请告诉我,不要告诉我去阅读有关 TCP 的文章,因为它根本没有帮助。

客户:

namespace ExampleClient
{
public partial class Form1 : Form
{
public static bool IsConnected;
public static NetworkStream Writer;
public static NetworkStream Receiver;

public Form1()
{
    InitializeComponent();
}

private void BtnConnect_Click(object sender, EventArgs e)
{
    TcpClient Connector = new TcpClient();
    Connector.Connect(TxtIP.Text, Convert.ToInt32(TxtPort.Text));
    IsConnected = true;
    Writer = Connector.GetStream();
    Console.WriteLine("Connected");

    System.Threading.Thread Rec = new System.Threading.Thread(new System.Threading.ThreadStart(Receive));
    Rec.Start();
}

private void BtnWrite_Click(object sender, EventArgs e)
{
    try{
        byte[] Packet = Encoding.ASCII.GetBytes("Client Write Test");
        Writer.Write(Packet, 0, Packet.Length);
        Writer.Flush();}
    catch{
        try { Writer.Close();} catch { }}
}

public static void Receive()
{
    while (true){
        try{
            byte[] RecPacket = new byte[1000];
            Receiver.Read(RecPacket, 0, RecPacket.Length);
            Receiver.Flush();
            string Message = Encoding.ASCII.GetString(RecPacket);
            MessageBox.Show(Message);
        }
        catch { break;}
    }
}
}
}

接收方/服务器:

namespace ExampleServer
{
public partial class Form1 : Form
{
public static NetworkStream Writer;
public static NetworkStream Receiver;

public Form1()
{
    InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
    TcpListener Server = new TcpListener(2002);

    Server.Start();
    TcpClient Connection = Server.AcceptTcpClient();
    Receiver = Connection.GetStream();
    System.Threading.Thread Rec = new System.Threading.Thread(new System.Threading.ThreadStart(Receive));
    Rec.Start();
}

public static void Receive()
{
    while (true)
    {
        try
        {
            byte[] RecPacket = new byte[1000];
            Receiver.Read(RecPacket, 0, RecPacket.Length);
            Receiver.Flush();
            string Message = Encoding.ASCII.GetString(RecPacket);
            MessageBox.Show(Message);

            try{
                byte[] Packet = Encoding.ASCII.GetBytes("Server Write Test");
                Writer.Write(Packet, 0, Packet.Length);
                Writer.Flush();}
            catch{
                try { Writer.Close(); } catch { }}
        }
        catch { break; }
    }
}
}
}

因此,当服务器从客户端读取消息时,它会显示它工作正常,但是当它向客户端写回消息时,它会崩溃并出现错误“system.nullreferenceeexception: object reference not set to an instance一个对象。在 exampleserver.form1.receive()"。

【问题讨论】:

  • 你究竟是从哪里得到 NullReferenceException 的?
  • 不,我知道空引用是什么,我不知道为什么它在这里给我这个错误并告诉我学习如何修复空引用根本不会帮助我。我需要专门针对我的示例的帮助
  • 当服务器应用程序在“byte[] packet.... 和 writer.flush()”之间发送消息时,我收到了该错误
  • 这就是调试器的用途。我建议您彻底阅读链接问题的最佳答案并应用其中的信息。如果你不能确切地告诉我哪些对象是空的,那么你还没有完全应用帖子中的信息

标签: c# sockets tcp tcpclient networkstream


【解决方案1】:

您的客户从来没有收到响应的问题是因为您从不让它。

您永远不会初始化客户端的 Receiver 流,应用程序没有中断,因为 A) 错误是在不同的线程中引发的,B) 您只是中断了发生错误时的while循环,您确实需要以某种方式记录错误。

只使用一个NetworkStream。流支持同时接收和发送,一个比两个容易得多 - 你不会忘记初始化它。

将来您还应该考虑处理您的消息。由于 TCP 是基于流的,因此您可以通过任何可能的方式获取消息;三分之一的消息,半条消息,或一次只有一个字节(等等)。有几种方法可以解决这个问题,但最好的方法是长度前缀(或长度前缀)。

长度前缀与标题前缀相结合,使您的消息在正确接收它们时尽可能可靠。如果操作正确,您始终可以确保收到的消息包含正确且正确的数据量。

长度和标头前缀意味着您为每条消息添加前缀,它具有正确的长度,并且标头说明消息的用途。它是这样组成的:

  1. 读取消息的长度(字节数)
  2. 将长度存储在整数中。
  3. 选择一个标头(通常一个字节的标头就足够了)。
  4. 将长度与标头连接起来,然后将其与消息的其余部分连接起来。

这将产生以下结构:

[Length (4 bytes)][Header (1 byte)][Message (?? byte(s))]

阅读:

  1. 读取前 4 个字节,并将返回的数组转换为整数。
  2. 读取下一个字节以获取标头。
  3. 阅读直到您阅读了整个消息长度。根据标头确定您将如何处理邮件。

阅读该序列,您将始终知道从另一个端点读取和期望多少,因此您不会遇到部分消息或消息集中在一起的问题。

希望这会有所帮助!

【讨论】:

    【解决方案2】:

    每个套接字地址(协议/网络地址/端口)通常只允许使用一次...不要启动 TcpListener。

    【讨论】:

    • 欢迎来到 StackOverflow。您的帖子已被标记为可能的垃圾邮件。为避免删除,请考虑使用更多详细信息编辑您的答案以帮助解决 OP 的问题。谢谢!
    【解决方案3】:

    我不认为你在初始化Writer。确保在调用它之前对其进行初始化。

    您捕获了第一个 NullReferenceException,但随后在捕获中,您再次调用 Writer,抛出一个新异常。

    【讨论】:

    • WriterReceiver 都是同一类型 (NetworkStream)。这意味着它们都有相同的方法。事实上,您根本不需要它们是单独的对象。您可以使用单个流来读取和写入
    • ReceiverWriter 都是网络流。两者都可以读取和写入。我假设您的服务器正在从一个端点读取并中继到另一个端点。听起来您只是想对现有流做出响应。我认为你根本不需要Writer。只需在您的 Receiver 对象上调用 'Write()
    • 谢谢,我在网络方面很糟糕,所以我不太明白你说的那么多。它不再崩溃,看起来像是在发送它,但我没有再次在客户端显示消息框
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-21
    • 2020-01-28
    • 2021-09-05
    • 1970-01-01
    • 2017-05-28
    • 2012-01-25
    相关资源
    最近更新 更多