为什么我的TCP连接似乎丢失了服务器端的数据包?

时间:2013-04-14 05:34:23

标签: tcp package socketasynceventargs

我现在正在编写一个基于SocketAsyncEventArgs的小框架,这个类是基于IOCP创建的,它比APM模式更有效。 但是在这里,我在运行测试时遇到了一些问题。 这是服务器代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Windows.Forms;

namespace SocketServer
{
public class Server
{
    Socket serverSocket;
    SocketAsyncEventArgs socketAsyncEventArgs;
    SocketAsyncEventArgsPool readWritePool;
    HandleMessage handleMessage;
    BufferManager buffeManager;

    const int PrefixSize = 11;

    public void Init(int port,int connections,int receiveBufferSize)
    {
        buffeManager = new BufferManager(receiveBufferSize * connections * 2, receiveBufferSize);

        buffeManager.InitBuffer();

        readWritePool = new SocketAsyncEventArgsPool(connections);

        SocketAsyncEventArgs socketAsyncEventArgsPooling;
        for (int i = 0; i < connections; i++)
        {
            socketAsyncEventArgsPooling = new SocketAsyncEventArgs();
            socketAsyncEventArgsPooling.Completed += readEventArgsIO_Completed;

            buffeManager.SetBuffer(socketAsyncEventArgsPooling);
            readWritePool.Push(socketAsyncEventArgsPooling);
        }

        handleMessage = new HandleMessage();

        IPAddress[] addressList = Dns.GetHostEntry(Environment.MachineName).AddressList;
        IPEndPoint localEndPoint = new IPEndPoint(addressList[addressList.Length - 1], port);

        this.serverSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);


        if (localEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
        {
            this.serverSocket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false);
            this.serverSocket.Bind(new IPEndPoint(IPAddress.IPv6Any, localEndPoint.Port));
        }
        else
        {
            this.serverSocket.Bind(localEndPoint);
        }

        this.serverSocket.Listen(100);

        StartAccept(null);
    }

    private void StartAccept(SocketAsyncEventArgs acceptSocketAsyncEventArgs)
    {
        if (acceptSocketAsyncEventArgs == null)
        {
            acceptSocketAsyncEventArgs = new SocketAsyncEventArgs();
            acceptSocketAsyncEventArgs.Completed += socketAsyncEventArgs_Completed;
        }
        else
        {
            acceptSocketAsyncEventArgs.AcceptSocket = null;
        }

        Boolean willRaiseEvent = this.serverSocket.AcceptAsync(acceptSocketAsyncEventArgs);
        if (!willRaiseEvent)
        {
            this.ProcessAccept(acceptSocketAsyncEventArgs);
        }
    }

    private void socketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e)
    {
        ProcessAccept(e);
    }

    private void readEventArgsIO_Completed(object sender, SocketAsyncEventArgs e)
    {
        switch (e.LastOperation)
        {
            case SocketAsyncOperation.Receive:
                this.ProcessReceive(e);
                break;
            case SocketAsyncOperation.Send:
                //this.ProcessSend(e);
                break;
            default:
                throw new ArgumentException("The last operation completed on the socket was not a receive or send");
        }
    }


    private void ProcessAccept(SocketAsyncEventArgs e)
    {

        SocketAsyncEventArgs readEventArgs = this.readWritePool.Pop();
        //SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs();
        readEventArgs.UserToken = e.AcceptSocket;

        Console.WriteLine("---------------------------------------------------");
        Console.WriteLine("Client Connected {0}",e.AcceptSocket.RemoteEndPoint);

        Boolean willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs);

        if (!willRaiseEvent)
        {
            this.ProcessReceive(readEventArgs);
        }

        this.StartAccept(e);
    }

    private void ProcessReceive(SocketAsyncEventArgs e)
    {
        if (e.BytesTransferred > 0)
        {
            if (e.SocketError == SocketError.Success)
            {
                Console.WriteLine("receiving data, {0} bytes", e.BytesTransferred);
                Socket socket = e.UserToken as Socket;

                int bytesTransferred = e.BytesTransferred;

                string received = Encoding.ASCII.GetString(e.Buffer, e.Offset, bytesTransferred);


                Console.WriteLine("Received:{0}", received);

                string[] msgArray = handleMessage.GetActualString(received);

                foreach (var msg in msgArray)
                {
                    Console.WriteLine("After Split:{0}", msg);
                }

               // Array.Clear(e.Buffer, e.Offset, bytesTransferred);

                Boolean willRaiseEvent = socket.SendAsync(e);
                if (!willRaiseEvent)
                {
                    this.ProcessSend(e);
                }

                readWritePool.Push(e);
            }
        }

    }

    private void ProcessSend(SocketAsyncEventArgs e)
    {

    }
}
}

这是我的客户代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;

namespace SocketClient
{
class Program
{
    static void Main(string[] args)
    {
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("192.168.2.129"), 1234);

        SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs();

        connectArgs.RemoteEndPoint = ipEndPoint;
        connectArgs.Completed += OnConnected;

        socket.ConnectAsync(connectArgs);

        socket.SendBufferSize = Int16.MaxValue;

        //NetworkStream streamToServer = new NetworkStream(socket);
        string text = "[length=12]Hello server";
        byte[] sendBuffer = Encoding.ASCII.GetBytes(text);


        for (int i = 0; i < 5; i++)
        {
            SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs();

            sendArgs.UserToken = socket;
            sendArgs.SetBuffer(sendBuffer,0,sendBuffer.Length);
            sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSend);


            socket.SendAsync(sendArgs);
        }

        Console.ReadLine();
    }

    private static void OnSend(object sender, SocketAsyncEventArgs e)
    {
        Console.WriteLine("SendOk: {0}", e.UserToken.ToString());
    }

    private static void OnConnected(object sender, SocketAsyncEventArgs e)
    {
        Console.WriteLine("Conectioned");
    }
}
}

但是,当我启动多个客户端时,我发现有时服务器可以正确接收消息;但有时候,服务器只能接收第一条消息,剩下的消息似乎全部丢失了#34;任何人都可以建议? THX。

我从某人那里听说,我应该实现自己的传输数据协议,但是任何人都可以告诉我如何定义? THX

下面是捕获服务器端的屏幕截图: enter image description here

1 个答案:

答案 0 :(得分:1)

所描述的问题发生是因为在服务器代码中,无论是否已接收到整个消息,都只调用一次ReceiveAsync方法。其余部分根本就没有读过。

正如ReceiveAsync documentation on MSDN中提到的那样,“对于字节流样式的套接字,传入的数据被放入缓冲区,直到填充缓冲区,连接关闭或内部缓冲的数据耗尽。” 。在您的情况下,如果客户端发送的消息被分成几个块,那么当数据的第一部分到达服务器时,它将由系统放置在套接字的内部缓冲区中。如果你有一个等待数据的ReceiveAsync方法,它会读取内部缓冲的数据,直到它耗尽然后它返回,即使这只是第一个块,仍然有数据要来。您将需要另一个ReceiveAsync操作来获得它。如果要检查这是否属实,可以尝试在for循环中放置Thread.Sleep(200),从客户端发送5条消息。在这种情况下,服务器仅接收消息的第一部分的机会将变得非常高,因为TCP使用一些算法来有效地发送数据,并且该超时将确定它分别发送5个消息。但是,您无法控制邮件在客户端和服务器之间的网络上的碎片方式。即使整个消息仅使用一个SendAsync操作发送,也可能需要多个ReceiveAsync操作。

要解决在服务器上读取部分消息的问题,您必须知道您期望的字节数。这可以通过使用恒定的消息长度或通过使用某种协议来确定长度来完成,例如为从客户端发送的每个消息添加前缀以及将要发送的消息的字节数。服务器必须进行多次ReceiveAsync调用,直到收到整个长度。为此,服务器需要保留剩余的字节数。 您可以找到SocketAsyncEventArgs client-server application on CodeProject的完整且经过解释的示例,了解它可以帮助您解决问题。