【问题标题】:Socket OnData event套接字 OnData 事件
【发布时间】:2014-07-15 01:51:40
【问题描述】:

我现在有点苦恼。我有一些代码可以在数据可用时从套接字读取数据,但目前它处于单独线程上的 while 循环中,并且由于没有可用数据而在函数返回时占用了 50% 的 CPU。我真正想要的是 Sockets 的阻塞函数,它会阻塞直到数据可用,或者至少是可以监听的 OnData 事件。我最初从 AS3 (Flash) 转换了这段代码,但他们的套接字类有我需要的 OnData 事件......只是语言错误。

我目前在处理客户端连接的代码中有这段代码:

ServerThread = new Thread(() =>
{
    while (server.Connected && ServerContinue)
    {
        ReceiveFromServer(server, client);
    }

    Disconnect(server, client, false);
});

ServerThread.Start();

这是 ReceiveFromServer 中的代码:

bool isReady = false;
int messageLength = 0;
int dataAvailable = 0;
UInt16 packetSize = 0;
byte[] temp = new byte[2];
do
{
    dataAvailable = server.Available;
    if (isReady)
    {
        if (dataAvailable >= messageLength)
        {
            byte[] temp1 = new byte[2000];
            int bytesRead = server.Receive(temp1, 0, messageLength, SocketFlags.None);

            byte[] data = new byte[bytesRead + 2];
            Buffer.BlockCopy(temp1, 0, data, 2, messageLength);

            Helpers.ByteArray tempo = data;
            tempo.writeByte(temp[1]);
            tempo.writeByte(temp[0]);
            if (!VersionCheckPass)
            {
                Send(tempo, client);
                return;
            }

            ServerPacketHandler(tempo, client);
            messageLength = 0;
            isReady = false;
            temp = new byte[2];
        }
    }
    else if(dataAvailable > 2)
    {
        server.Receive(temp, 0, 2, SocketFlags.None);
        temp = temp.Reverse().ToArray();
        packetSize = BitConverter.ToUInt16(temp, 0);
        if (packetSize > 0)
        {
            messageLength = packetSize;
            isReady = true;
        }
    }
}
while (dataAvailable > 2 && dataAvailable >= messageLength && ServerContinue);

但这里的问题是,当 dataAvailable 为 0 时,函数会简单地返回,然后在线程中再次调用 RecevieFromServer。这意味着只需调用 ReceiveFromServer 然后再次返回即可使用大量 CPU。

我目前在 ServerThread 中的 ReceiveFromServer 之后有Thread.Sleep(10),但这效率低下。所以我的问题是,有没有办法阻止直到数据可用或者有我可以处理的事件?或者有没有其他人对如何做我目前正在做的事情有任何建议,但它不会在没有可用数据的情况下无休止地循环。

【问题讨论】:

  • 我已经成功使用了Nito Async Sockets 库。它为套接字编程提供了一种基于事件的方法。
  • 谢谢,我去看看,但我真的很不想重新写一遍。

标签: c# sockets events


【解决方案1】:

找到了一个非常简单(且显而易见)的解决方案来阻止直到数据可用。使用接收大小为 0 调用 Socket.Receive。套接字阻塞,直到有数据要接收,然后从套接字读取 0 个字节,然后解除阻塞。它真的很了不起 :) 下面是我如何实现它:

ServerThread = new Thread(() =>
{
    byte[] zero = new byte[0];
    while (Server.Connected && ServerContinue)
    {
        server.Receive(zero, 0, SocketFlags.None);
        ReceiveFromServer(server, client);
    }

    Disconnect(server, client, false);
});

感谢大家的帮助。

乔什

【讨论】:

    【解决方案2】:

    不需要大量的重写。您的代码看起来只是在接收消息,然后传递给另一个例程来处理它。

    我对该主题的回复几乎涵盖了您想要做的事情:

    C# Sockets and Multithreading

    我的socketReadCallBack函数是:

        private void OnDataReceived(IAsyncResult asyn)
        {
            ReceiveState rState = (ReceiveState)asyn.AsyncState;
            Socket client = rState.Client;
            SocketError socketError = SocketError.TypeNotFound;
    
            if (!client.Connected)
            {
                // Not Connected anymore ?
                return;
            }
    
            _LastComms = DateTime.Now;
            _LastIn = _LastComms;
    
            int dataOffset = 0; 
            int restOfData = 0;
            int dataRead = 0;
            Boolean StreamClosed = false;
            long rStateDataLength = 0;
            long LastrStateDataLength = 0;
    
            try
            {
    
                dataRead = client.EndReceive(asyn, out socketError);
            }
            catch (Exception excpt)
            {
                // Handle error - use your own code..
    
            }
    
            if (socketError != SocketError.Success)
            {
                // Has Connection been lost ?
                OnConnectionDropped(client);
                return;
            }
    
            if (dataRead <= 0)
            {
                // Has connection been lost ?
                OnConnectionDropped(client);
                return;
            }
    
            while (dataRead > 0)
            {
                //check to determine what income data contain: size prefix or message
                if (!rState.DataSizeReceived)
                {
                    //there is already some data in the buffer
                    if (rState.Data.Length > 0)
                    {
                        restOfData = PrefixSize - (int)rState.Data.Length;
                        rState.Data.Write(rState.Buffer, dataOffset, restOfData);
                        dataRead -= restOfData;
                        dataOffset += restOfData;
                    }
                    else if (dataRead >= PrefixSize)
                    {  //store whole data size prefix
                        rState.Data.Write(rState.Buffer, dataOffset, PrefixSize);
                        dataRead -= PrefixSize;
                        dataOffset += PrefixSize;
                    }
                    else
                    {  // store only part of the size prefix
                        rState.Data.Write(rState.Buffer, dataOffset, dataRead);
                        dataOffset += dataRead;
                        dataRead = 0;
                    }
    
                    if (rState.Data.Length == PrefixSize)
                    {  //we received data size prefix 
                        rState.DataSize = BitConverter.ToInt32(rState.Data.GetBuffer(), 0);
                        rState.DataSizeReceived = true;
                        //reset internal data stream             
                        rState.Data.Position = 0;
                        rState.Data.SetLength(0);
                    }
                    else
                    {  //we received just part of the prefix information 
                        //issue another read
                        client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
                           SocketFlags.None, new AsyncCallback(socketReadCallBack),
                              rState);
                        return;
                    }
                }
    
    
    
                //at this point we know the size of the pending data
    
                // Object disposed exception may raise here
                try
                {
                    rStateDataLength = rState.Data.Length;
                    LastrStateDataLength = rStateDataLength;
                }
                catch (ObjectDisposedException Ode)
                {
                    StreamClosed = true;
                }
                if (!StreamClosed)
                {
    
                    if ((rStateDataLength + dataRead) >= rState.DataSize)
                    {   //we have all the data for this message
    
                        restOfData = rState.DataSize - (int)rState.Data.Length;
    
                        rState.Data.Write(rState.Buffer, dataOffset, restOfData);
                        //Console.WriteLine("Data message received. Size: {0}",
                        //   rState.DataSize);
    
                        // Is this a heartbeat message ?
                        if (rState.Data.Length == 2)
                        {
                            // Yes
                            HeartBeatReceived();
                        }
                        else
                        {
                            //charArray = new char[uniEncoding.GetCharCount(
                            //byteArray, 0, count)];
                            //uniEncoding.GetDecoder().GetChars(
                            //    byteArray, 0, count, charArray, 0);
                            //Console.WriteLine(charArray);
    
                            //rState.Data.Position = 0;
    
                            DecodeMessageReceived(GetStringFromStream(rState.Data));
                        }
    
                        dataOffset += restOfData;
                        dataRead -= restOfData;
    
                        //message received - cleanup internal memory stream
                        rState.Data = new MemoryStream();
                        rState.Data.Position = 0;
                        rState.DataSizeReceived = false;
                        rState.DataSize = 0;
    
                        if (dataRead == 0)
                        {  
                            //no more data remaining to process - issue another receive
                            if (_IsConnected)
                            {
                                client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
                                   SocketFlags.None, new AsyncCallback(socketReadCallBack),
                                      rState);
                                return;
                            }
                        }
                        else
                            continue; //there's still some data to process in the buffers
                    }
                    else
                    {  //there is still data pending, store what we've 
                        //received and issue another BeginReceive
                        if (_IsConnected)
                        {
                            rState.Data.Write(rState.Buffer, dataOffset, dataRead);
    
                            client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
                               SocketFlags.None, new AsyncCallback(socketReadCallBack), rState);
    
                            dataRead = 0;
                        }
                    }
                }
                else
                {
                    // Stream closed, but have we read everything ?
                    if (LastrStateDataLength + dataRead == rState.DataSize)
                    {
                        // We're equal, get ready for more
                        //no more data remaining to process - issue another receive
                        if (_IsConnected)
                        {
                            client.BeginReceive(rState.Buffer, 0, rState.Buffer.Length,
                               SocketFlags.None, new AsyncCallback(socketReadCallBack),
                                  rState);
                        }
                        return;
                    }
                    else
                    {
                        // We should have more..
                        // Report Error
                    }
                }
    
                // If we've been disconnected, provide a graceful exit
                if (!_IsConnected)
                    dataRead = -1;
    
            }
         }
    

    我这里还有一些你需要的东西,比如提供心跳消息和在连接断开时引发事件等。

    【讨论】:

      猜你喜欢
      • 2016-06-08
      • 2020-04-15
      • 2021-11-22
      • 1970-01-01
      • 2015-02-10
      • 2019-06-12
      • 1970-01-01
      • 1970-01-01
      • 2020-07-03
      相关资源
      最近更新 更多