【问题标题】:Why server app receives 50% of client messages?为什么服务器应用程序接收 50% 的客户端消息?
【发布时间】:2015-02-04 13:12:02
【问题描述】:

上一篇:

客户端打开socket向服务器发送数据:

private void Form1_Load(object sender, EventArgs e)
{
    client = new TcpClient();
    client.BeginConnect("127.0.0.1", 995, new AsyncCallback(ConnectCallback), client);
}

private void ConnectCallback(IAsyncResult _result) // it will send hello message from client
{
    string data;
    byte[] remdata = { };
    IAsyncResult inResult = _result;

    currentProcess = Process.GetCurrentProcess();
    string currentProcessAsText = currentProcess.Id.ToString();

    SetControlPropertyThreadSafe(proccessIdLabel, "Text", currentProcessAsText);

    try {
        sock = client.Client;

        // send hello message
        data = "Client with proccess id " + currentProcessAsText + " is connecting";
        sock.Send(Encoding.ASCII.GetBytes(data));
        SetControlPropertyThreadSafe(answersTextBox, "Text", answersTextBox.Text + "\n"+ GetCurrentTime() + " Connection established");
    }
    catch
    {
        SetControlPropertyThreadSafe(answersTextBox, "Text", answersTextBox.Text + "\n" + GetCurrentTime() + " Can't connect");
    }
}

之后我有点击某个按钮(发送消息)的处理程序:

private void SendButton_Click(object sender, EventArgs e)
{
    try
    {
        string data;
        sock = client.Client;
        data = "Some text";
        sock.Send(Encoding.ASCII.GetBytes(data));
    }
    catch
    {
        SetControlPropertyThreadSafe(answersTextBox, "Text", "Can't connect");
    }
}

同时处理表单关闭发送服务器exit命令,所以他会停止这个客户端的线程:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    try
    {
        sock.Send(Encoding.ASCII.GetBytes("exit"));
        sock.Close();
    }            
    catch
    {
    }
}

服务器监听端口并处理消息:

private void Listeners()
{
    Socket socketForClient = Listener.AcceptSocket();
    string data;

    int i = 0;

    if (socketForClient.Connected)
    {
        string remoteHost = socketForClient.RemoteEndPoint.ToString();
        Console.WriteLine(Message("Client:" + remoteHost + " now connected to server."));

        while (true)
        {
                // буфер данных
            byte[] buf = new byte[1024];
            try
            {
                int messageLength = socketForClient.Receive(buf);
                if (messageLength > 0)
                {
                    byte[] cldata = new byte[messageLength];
                    socketForClient.Receive(cldata);
                    data = "";
                    data = Encoding.ASCII.GetString(cldata).Trim();
                    if (data.Contains("exit"))
                    {
                        socketForClient.Close();
                        string message = Message("Client:" + remoteHost + " is disconnected from the server (client wish).");
                        Console.WriteLine(message);
                        return;
                    }
                    else
                    {
                        Console.WriteLine(Message("Recevied message from client " + remoteHost + ":\n"));
                        Console.WriteLine(data);
                        Console.WriteLine("\nEOF\n");
                    }
                }
            }
            catch
            {
                string message = Message("Client:" + remoteHost + " is disconnected from the server (forced close).");
                Console.WriteLine(message);
                socketForClient.Close();
                return;
            }
        }
    }
}
private void ServStart()
{
    Listener = new TcpListener(LocalPort);
    Listener.Start(); // начали слушать
    Console.WriteLine("Waiting connections [" + Convert.ToString(LocalPort) + "]...");

    for (int i = 0; i < 1000; i++)
    {
        Thread newThread = new Thread(new ThreadStart(Listeners));
        newThread.Start();
    }
}

所以在服务器启动时,它会创建 1000 个线程,用于监听客户端消息。

问题:

我将描述一些情况:

  1. 启动服务器

服务器启动线程并准备接受客户端连接

  1. 启动客户端

正在建立连接。服务器说客户端连接到某个端口。客户端发送“你好”消息。服务器不处理这条问候消息。

  1. 按下按钮,客户端将向服务器发送一些文本。服务器处理这条消息。

  2. 按下按钮。客户端再次发送“一些文本”。服务器不处理该消息。

  3. 按下按钮。客户端再次发送“一些文本”。服务器处理该消息。

  4. 如果我再推,它显然不会处理它......

服务器日志:

为什么服务器接收/客户端只发送 2 条消息中的 1 条?是什么原因造成的?

当客户端表单关闭时,我在向服务器发送exit 消息时也遇到问题。我在此操作上发送 exit 消息。

所以情况:

  1. 我只是按下了按钮,服务器处理了它(所以服务器不会处理下一条消息)。

  2. 我关闭表单,发送消息,但客户端发送错误消息或服务器接收错误消息。

控制台中的情况:

您可以看到,当表单关闭并且客户端发送exit 时,服务器处理了empty 消息。为什么?

客户端exit命令正常通过服务器时的情况:

  1. .....
  2. 客户端发送数据,服务器不处理
  3. 现在,服务器将处理客户端,所以我们尝试关闭表单:

控制台:

所以在第二个项目中,客户端已经发送了 hello 消息,而服务器未能处理它。在第 3 项中,客户端发送退出命令,服务器正确传递。

主要问题:为什么服务器只处理来自客户端的 2 条消息中的 1 条?

另一点:我还发现,当客户端发送exit 数据时,服务器会收到exit\0\0\0\0\0\0\0\0\(或更多,或更少\0 符号)。为什么?

我认为好消息是,该服务器不断接收或不接收消息。收到 1 条消息,未收到 1 条消息。这说明我缺乏知识,但不是随机错误。

【问题讨论】:

  • 你有没有发现为什么你会收到“\0\0\0\0\0\0\0\0\”?我现在遇到这个问题,但只有一个客户端(这让我相信硬件设置很奇怪)。
  • 嗯。第一种方法是在服务器端修剪\0。第二种方式 - 我改变了发送和接收消息的方法,我认为......但我可能会错过一些东西。我不太记得了
  • 没问题,谢谢。现在我无论如何都在做同样的事情(告诉服务器忽略'\0')

标签: c# multithreading sockets


【解决方案1】:

这么多错误。 :(

也就是说,我注意到的最大的是这个:

int messageLength = socketForClient.Receive(buf);
if (messageLength > 0)
{
    byte[] cldata = new byte[messageLength];
    socketForClient.Receive(cldata);
    data = "";
    data = Encoding.ASCII.GetString(cldata).Trim();

首先,了解在 TCP 中,您无法保证任何给定接收操作将接收的字节数。无论远程端点如何发送数据,您都可以在单独的接收操作中一次接收所有数据,或仅接收部分数据。 TCP 保证字节将按照发送它们的相同顺序被接收,仅此而已。

但上面的代码不仅没有考虑到这一点,而且完全是错误的。在第一个操作中接收到的字节数是在该操作中接收到的字节数。但是您使用该数字就好像它会告诉您在 next 调用Receive() 中收到的字节数。它没有做任何事情。同时,您忽略了您在第一次操作中收到的数据。

相反,您的代码应该看起来更像这样:

int messageLength = socketForClient.Receive(buf);
if (messageLength > 0)
{
    data = Encoding.ASCII.GetString(buf, 0, messageLength).Trim();

这仍然不太正确,因为您当然可以在对Receive() 的调用中收到部分消息,或者甚至多条消息串联在一起。但至少你可能会看到所有的文字。

该更改将解决您提出的具体问题。如果您无法弄清楚如何解决其他错误,请随时发布简洁、具体问题和代码示例以寻求帮助。请参阅 https://stackoverflow.com/help/mcvehttps://stackoverflow.com/help/how-to-ask 以获取有关更好地提出问题的建议。

【讨论】:

  • 这么多人这么多人......有些人想要简洁的帖子,有些人不想在没有代码示例的情况下说话。如果我没有在我的帖子中输入代码,你能指出我的错误吗?不,因为不会有代码示例。我是新来的(套接字,c#),所以我不能轻易犯错。我什至不知道它在哪里。正确的?这就是我在那里问的原因。
  • 感谢您的回答。它对我帮助很大。
猜你喜欢
  • 1970-01-01
  • 2014-02-28
  • 2015-12-09
  • 1970-01-01
  • 2023-04-09
  • 2012-04-07
  • 1970-01-01
  • 2015-11-28
  • 1970-01-01
相关资源
最近更新 更多