简体   繁体   English

C#TcpClient无法发送或读取100%的数据?

[英]C# TcpClient not sending or reading 100% of data?

Hey all. 大家好。 I'm writing a simple client/server application (just for the experience, networking is fairly new to me) where the client sends the server data and the server outputs it to a textbox. 我正在编写一个简单的客户端/服务器应用程序(就经验而言,联网对我来说还很新),客户端在其中发送服务器数据,然后服务器将其输出到文本框。 It all works fine, except for one small detail... It seems sometimes a connection is made, but the data isn't being sent or read (can't work out which) and thus nothing is being outputted in the textbox. 一切正常,除了一个小细节...似乎有时建立了连接,但是没有发送或读取数据(无法计算出哪个),因此文本框中未输出任何内容。 Every time a connection is made a counter is incremented, same thing when a data block is received. 每次建立连接时,计数器都会增加,接收数据块时也是如此。 When you compare the two, the number of connections is correct but the data counter is usually lower, sometimes by as much as half. 当您将两者进行比较时,连接数是正确的,但数据计数器通常较低,有时多达一半。 Anyway, if anyone can give me some advice or point me in the right direction, it would be greatly appreciated! 无论如何,如果有人可以给我一些建议或指出正确的方向,将不胜感激!

Here's the code if you require it: 如果需要的话,下面是代码:

(SERVER_CODE) (SERVER_CODE)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Server
{
    public partial class Form1 : Form
    {
        public int Connections = 0;
        public int blocks = 0;
        public int threads = 0;
        public Thread MasterThread;
        public TcpListener Master;
        public volatile bool Run;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        public void StartMaster()
        {
            Master = new TcpListener(IPAddress.Any, 1986);
            Master.Start();
            MasterThread = new Thread(new ThreadStart(RunMaster));
            MasterThread.Start();
        }

        public void RunMaster()
        {
            threads++;
            label6.Text = String.Format("{0}", threads);

            while (Run)
            {
                TcpClient client = Master.AcceptTcpClient();
                Connections++;
                label4.Text = String.Format("{0}", Connections);
                Thread ClientThread = new Thread(new ParameterizedThreadStart(RunClient));
                ClientThread.Start(client);
            }

            Master.Stop();

            threads--;
            label6.Text = String.Format("{0}", threads);
        }

        public void RunClient(object tcpClient)
        {
            TcpClient client = (TcpClient)tcpClient;
            byte[] buffer = new byte[4096];
            int byteCount = 0;
            NetworkStream stream = client.GetStream();
            threads++;
            label6.Text = String.Format("{0}", threads);

            while (Run)
            {
                try
                {
                    byteCount = stream.Read(buffer, 0, 4096);
                }
                catch
                {
                    //Connections--;
                    break;
                }

                if (byteCount == 0)
                {
                    //Connections--;
                    break;
                }

                blocks++;
                label5.Text = String.Format("{0}", blocks);

                textBox1.AppendText(Encoding.ASCII.GetString(buffer, 0, byteCount) + "\r\n");
            }

            client.Close();

            threads--;
            label6.Text = String.Format("{0}", threads);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Run = true;
            StartMaster();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Run = false;
        }
    }
}

(CLIENT_CODE) (CLIENT_CODE)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Client
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1986);
            TcpClient client = new TcpClient();
            try
            {
                client.Connect(endPoint);
            }
            catch
            {
                MessageBox.Show("Connect Error");
            }

            NetworkStream stream = client.GetStream();
            byte[] data = Encoding.ASCII.GetBytes(textBox1.Text);
            stream.Write(data, 0, data.Length);
            stream.Flush();
            client.Close();
        }
    }
}

Thank-you, 谢谢,
Tristan!. 特里斯坦!

Well, to start with you're crippling your own diagnostics with this: 好吧,首先,您要破坏自己的诊断程序:

catch
{
    //Connections--;
    break;
}

Why are you swallowing exceptions without any logging etc? 为什么在没有任何日志记录的情况下吞下异常? Maybe an exception is being thrown, and you have no way of knowing. 可能会引发异常,您无法知道。 Ideally you should catch specific exceptions, and when you do catch an exception at least log what's going on. 理想情况下你应该捕获特定的异常,而当捕捉异常至少登录是怎么回事。

At the other end of the spectrum, Wireshark should help you to work out whether the data is being sent or not. 另一方面, Wireshark应该帮助您确定是否正在发送数据。

I haven't had a thorough look at your code yet, but after a quick glance, you access variables from multiple threads without proper locking. 我还没有彻底了解您的代码,但是快速浏览后,您可以在没有适当锁定的情况下从多个线程访问变量。 A statement like x++; x++;这样的语句x++; has to read the value of x , increment it, and write it back. 必须读取x的值,对其进行递增并写回。 Now if you have two threads doing this, you might run into this situation: 现在,如果有两个线程执行此操作,则可能会遇到这种情况:

x = 0

Thread 1           Thread 2
------------------------
Read (0)
                   Read (0)
Increment (1)
                   Increment (1)
Write (1)
                   Write (1)

=> x = 1 instead of 2

If you need to access variables from multiple threads, ALWAYS synchronize unless you know exactly what you're doing. 如果您需要从多个线程访问变量,请始终进行同步,除非您确切地知道自己在做什么。 For example, create and use a synchronization object like this: 例如,创建并使用如下所示的同步对象:

int threads = 0;
object threadSync = new object();

...

lock (threadSync) {
    threads++;
}

Then only one thread may access the variable at a time and values are incremented correctly. 然后一次只能有一个线程访问该变量,并且值会正确递增。

Edit: Another problem is that you access visible controls from a different thread than the one that created them. 编辑:另一个问题是您从与创建控件不同的线程访问可见控件。 Early .NET versions allowed this, but the newer don't. 早期的.NET版本允许这样做,但较新的版本不允许。 If you need to update status messages, you need to look at the control's InvokeRequired property and if set to true, use Control.Invoke(...) to call a method that sets the property. 如果需要更新状态消息,则需要查看控件的InvokeRequired属性,如果将其设置为true,请使用Control.Invoke(...)调用设置该属性的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM