为什么服务器应用会收到50%的客户端消息?

时间:2014-12-06 12:16:56

标签: c# multithreading sockets

预:

客户端打开套接字以将数据发送到服务器:

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. 启动服务器
  2. 服务器启动线程并准备接受客户端连接

    1. 启动客户端
    2. 连接正在建立。服务器说客户端连接在某个端口上。客户发送&#34;你好&#34;信息。服务器无法处理此问候消息。

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

      2. 按下按钮。客户发送&#34;一些文字&#34;再次。服务器无法处理该消息。

      3. 按下按钮。客户发送&#34;一些文字&#34;再次。服务器处理该消息。

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

      5. 服务器日志:

        enter image description here

        为什么服务器接收/客户端只发送2条消息中的1条?是什么导致它?

        当客户端表单关闭时,我也会向服务器发送exit消息。我就此操作发送了exit条消息。

        情况如此:

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

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

        3. 控制台中的情况:

          enter image description here

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

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

          1. .....
          2. 客户端发送数据,服务器没有处理它
          3. 现在,服务器将处理客户端,因此我们尝试关闭表单:
          4. 控制台:

            enter image description here

            所以在第二项客户端发出了hello消息,服务器无法处理它。在第3项客户端发送退出命令,服务器正确传递它。

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

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

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

1 个答案:

答案 0 :(得分:2)

这么多错误。 :(

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

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保证将在发送它们的相同顺序中接收字节,仅此而已。

但上述代码不仅没有考虑到这一点,而且完全错误。第一个操作中接收的字节数是在该操作中接收的字节数。但是您正在使用该数字,就好像它会告诉您有关下一次调用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