0

我遇到了一个问题,那就是当我尝试从服务器写入多个客户端时,它一直在写入空字符串。我正在共享我的代码及其多个客户端服务器应用程序,其中客户端可以写入服务器,而服务器可以写入所有正在运行的客户端。写入服务器的客户端安静得很好。但服务器不是:(

服务器代码:

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

namespace ServerGui
{
    public partial class Form1 : Form
    {
        TcpListener tcpListen;
        Thread listenThread;
        String read = "";
        ArrayList collect = new ArrayList();
        int counter = 0;
        ArrayList NS = new ArrayList();
        TcpClient general = null;
        readonly ManualResetEvent mre = new ManualResetEvent(false);

        public Form1()
        {
            InitializeComponent();
        }

        private void connectServerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            tcpListen = new TcpListener(IPAddress.Any, 3000);
            listenThread = new Thread(new ThreadStart(ListenForClients));
            listenThread.Start();
            connectServerToolStripMenuItem.Enabled = false;
        }

        public void ListenForClients()
        {
            tcpListen.Start();

            while (true)
            {

                TcpClient client = tcpListen.AcceptTcpClient();
                collect.Insert(counter, client);
                general = (TcpClient)collect[counter];
                if (textBox1.InvokeRequired)
                {
                    textBox1.Invoke(new MethodInvoker(delegate { textBox1.AppendText(client.Client.LocalEndPoint.ToString() + "  Connected.");
                    textBox1.AppendText(Environment.NewLine); }));
                }
                else
                {
                    textBox1.AppendText(client.Client.LocalEndPoint.ToString() + "  Connected.");
                    textBox1.AppendText(Environment.NewLine);
                }
                Thread clientThread = new Thread(new ParameterizedThreadStart(Reader));
                clientThread.Start(collect[counter]);
                Thread clientThread1 = new Thread(new ParameterizedThreadStart(Writer));
                clientThread1.Start(collect[counter]);
                counter++;
            }
        }
        public void Reader(object client)
        {
            TcpClient tcpClient = (TcpClient)client;
            NetworkStream clientStream = tcpClient.GetStream();
            StreamReader sr = new StreamReader(clientStream);
            while (true)
            {
                try
                {
                    read = sr.ReadLine();
                }
                catch
                {
                    if (textBox1.InvokeRequired)
                    {
                        textBox1.Invoke(new MethodInvoker(delegate
                        {
                              textBox1.AppendText(tcpClient.Client.LocalEndPoint.ToString() +
 " disconnected.");
                            textBox1.AppendText(Environment.NewLine);
                        }));
                    }
                    else
                    {
                        textBox1.AppendText(tcpClient.Client.LocalEndPoint.ToString() + " disconnected.");
                        textBox1.AppendText(Environment.NewLine);
                    }
                    return;
                }
                if (textBox1.InvokeRequired)
                {
                    textBox1.Invoke(new MethodInvoker(delegate
                    {
                        textBox1.AppendText("<" +         tcpClient.Client.LocalEndPoint.ToString() + "> saying: " + read);
                        textBox1.AppendText(Environment.NewLine);
                    }));
                }
                else 
                {
                    textBox1.AppendText("<" + tcpClient.Client.LocalEndPoint.ToString() + "> saying: " + read);
                    textBox1.AppendText(Environment.NewLine);
                }
            }

            tcpClient.Close();
        }

        public void Writer(object client)
        {
            TcpClient tcpClient = (TcpClient)client;
            NetworkStream ns = tcpClient.GetStream();
            mre.WaitOne();
            while (true)
            {
                StreamWriter sw = new StreamWriter(ns);

                    string str = textBox2.Text;
                    sw.WriteLine(str);
                    sw.Flush();

                if (textBox2.InvokeRequired)
                {
                    textBox2.Invoke(new MethodInvoker(delegate 
                        {
                            textBox2.Clear();
                        }));
                }
                else
                textBox2.Clear();
                }
            tcpClient.Close();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            mre.Set();
            textBox1.AppendText(textBox2.Text);
            textBox1.AppendText(Environment.NewLine);
            textBox2.Clear();
        }

        private void CheckKeys(object sender, System.Windows.Forms.KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)13)
            {
                mre.Set();
                textBox1.AppendText(textBox2.Text);
                textBox1.AppendText(Environment.NewLine);
                textBox2.Clear();
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
    }
}

客户端代码:

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

namespace ClientGcui
{
    public partial class Form1 : Form
    {
        NetworkStream general = null;
        public Form1()
        {
            InitializeComponent();

        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void connectToServerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            String str = "";
            TcpClient client = new TcpClient();
            IPEndPoint server = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3000);
            client.Connect(server);
            textBox1.AppendText("Connected to server");
            textBox1.AppendText(Environment.NewLine);
            NetworkStream clientStream = client.GetStream();
            general = clientStream;
            StreamReader sr = new StreamReader(clientStream);
            StreamWriter sw = new StreamWriter(clientStream);
            Thread reader = new Thread(new ParameterizedThreadStart(readserver));
            reader.Start(sr);
        }
        public void readserver(object client)
        {
            StreamReader sr = (StreamReader)client;
            string read = "";
            while (true)
            {
                try
                {
                    read = sr.ReadLine();
                }
                catch
                {
                    if (textBox1.InvokeRequired)
                    {
                        textBox1.Invoke(new MethodInvoker(delegate
                        {
                            textBox1.AppendText("Disconnected from Server.");
                            textBox1.AppendText(Environment.NewLine);
                        }));
                    }
                    else
                    {
                        textBox1.AppendText("Disconnected from Server.");
                        textBox1.AppendText(Environment.NewLine);
                    }
                }
                if (textBox1.InvokeRequired)
                {
                    textBox1.Invoke(new MethodInvoker(delegate
                    {
                        textBox1.AppendText(sr.ReadLine());
                        textBox1.AppendText(Environment.NewLine);
                    }));
                }
                else
                {
                    textBox1.AppendText(sr.ReadLine());
                    textBox1.AppendText(Environment.NewLine);
                }
                }
            sr.Close();
            }


        private void button1_Click(object sender, EventArgs e)
        {
            StreamWriter sw = new StreamWriter(general);
            sw.WriteLine(textBox2.Text);
            sw.Flush();
            textBox1.AppendText(textBox2.Text);
            textBox1.AppendText(Environment.NewLine);
            textBox2.Clear();
        }

        private void CheckKeys(object sender, System.Windows.Forms.KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)13)
            {
                StreamWriter sw = new StreamWriter(general);
                sw.WriteLine(textBox2.Text);
                sw.Flush();
                textBox1.AppendText(textBox2.Text);
                textBox1.AppendText(Environment.NewLine);
                textBox2.Clear();
            }
        }
    }
}
4

1 回答 1

2

'textBox1'、'textbox2'、'button1' 是什么?在这段代码中似乎没有一个特定于应用程序的组件名称 - 你知道,这些东西让有经验的开发人员快速浏览帖子时可以轻松快速地理解它。为什么在此处发布之前没有将组件名称编辑为有意义的内容?并不是说你的英语不好,(我可以理解),你的问题很清楚。

无论如何,您的作家没有调用()读取 textbox2 的内容,然后第一个清除 textBox2 的作家赢得了比赛,并塞满了所有其他没有阅读内容的作家。

..在 CheckKeys 中,您正在向 mre 发出信号,然后在赢得比赛的作家甚至有机会阅读它之前清除文本框2。

我看不到您在哪里重置 mre,无论如何,这是使用错误的同步对象。

您不能吝啬多线程代码。你必须正确地做到这一点。

与您的作者线程进行通信有一些可能性。

1) 使用一个受锁保护、引用计数的对象来保留来自 textbox2 的传出文本,直到所有作者都完成了它。这意味着您知道有多少作家 - 目前您无法跟踪。这样做的问题是,如果不删除所有 refCount 被初始化为包含的“持有”实例的 refCount,编写者就不能退出。我怀疑你会做对。

2) 使用一个对象来保留来自 textbox2 的传出文本。此外,将此实例提交到一个超时队列,该队列会保留一个引用,例如 10 分钟,到那时所有作者肯定都会使用它。这是一种合理的做法。

3) 使用一个对象为每个作者保留来自 textbox2 的传出文本,并在生产者-消费者队列上向每个作者提交不同的副本。这意味着您需要在客户端连接和断开连接时必须保持最新的编写器向量。

4)我在写其他三个的时候想到了第四个方法,但我现在忘记了。

我会选择(2) - 最少的事情不工作的机会。

哦 - 忘了,发信号给所有客户端线程。如果您使用 MRE,您将在哪里重置它?您如何判断所有客户何时已阅读内容并即将再次等待?你不能,我怀疑这就是你的代码中没有 resetEvent 的原因——你不知道把它放在哪里。

考虑一下,考虑到你有一种 GC 语言,给每个编写器自己的 BlockingCollection 并将每个文本对象的引用排队给所有编写器可能是最简单和最安全的。再次,我们回到一个(线程安全的)写入器集合,这些写入器在连接时放入新条目,在断开连接时删除。即使使用线程安全集合,您也必须期待并捕获奇怪的异常,因为主线程试图发出一个文本对象 ref。对于刚刚断开连接的作家,还没有完全从收藏中删除自己。

此外,每个作者的线程有点矫枉过正。对于最大。客户,您最好使用 threadPool 进行写入。然后,您只需要一个“SeverClientSocket”类,它有自己的读取线程、一个用于添加主线程的 queueUpForWrite() 方法和一个用于调用池线程的 writeQueue() 方法。

于 2012-04-15T08:44:43.243 回答