2012-12-19 163 views
1

这是用于MoSync应用程序和外部DLL之间的本地通信,MoSync不允许使用第三部分DLL,这就是为什么我必须实现此桥接软件而不是使用简单调用的原因到一个DLL,我必须从XML转换到DLL消息格式,并再次转换为XML。我知道这是一件愚蠢的事情,不幸的是没有灵活性来改变架构。起初我以为只有一个请求,所以我有Sync com,但现在我发现可以有多个请求,所以我需要再次实现Async。EndRead引发IO异常

我有一个是从时间抛出来时的异常,因为我是新来的C#我无法找到内存泄漏......也许一对更训练有素的眼睛可以发现问题

SOURCE代码:

我写了下面的代码,我对C#和套接字很陌生,所以也许我犯了一些只有经验丰富的眼睛才能察觉的大错误。这是用于Windows Mobile 6.1设备,所以我试图避免使用很多线程。

using System; 

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

namespace SmartDevice_Server 
{ 
    //ClientConnection saves connection information is used to keep context in Async and Event calls 
    public class ClientConnection : EventArgs 
    { 
     public NetworkStream NetworkStream { get; private set; } 
     public byte[] Data { get; private set; } 
     public int byteReadCount { get; set; } 

     public ClientConnection(NetworkStream networkStream, byte[] data) 
     { 
      NetworkStream = networkStream; 
      Data = data; 
     } 
    } 

    //MySocket - Is a server that listens for events and triggers Events upon Request Completion 
    public class MySocketTCP 
    { 
     #region Class Members 
     TcpListener myTcpListener; 
     TcpClient myTcpClient; 
     NetworkStream myNetworkStream; 

     const string localHost = "127.0.0.1"; 
     IPAddress myAddress = IPAddress.Parse(localHost); 
     int myPortNumber = 58889; 
     byte[] myData; 

     int bytesReadCount; 
     const int MIN_REQUEST_STRING_SIZE = 10; 

     int TimeStart; 

     //Event 
     public event socketReadCompleteHandler socketReadCompleteEvent; 
     public EventArgs eventArguments = null; 
     public delegate void socketReadCompleteHandler(MySocketTCP myTcpSocket, ClientConnection eventArguments); 

     #endregion 

     //Constructor 
     public MySocketTCP() 
     { 
      Init(); 
     } 

     //Constructor overloaded to receive IPAdress Host, and Port number 
     public MySocketTCP(IPAddress hostAddress, int portNumber) 
     { 
      myAddress = hostAddress; 
      myPortNumber = portNumber; 

      Init(); 
     } 

     //Initializes the TCPListner 
     public void Init() 
     { 
      try 
      { 
       myTcpListener = new TcpListener(myAddress, myPortNumber); 

       //myNetworkStream = myTcpClient.GetStream(); 
      } 
      catch (Exception ex) 
      { 
       throw ex; 
      } 
     } 

     /*TODO_Listener_Timer: After you accept a connection you wait for data to be Read indefinitely 
     *Possible solution: Use a timeout to close the socket connection. 
     *Check WIKI, TODOS 
     * */ 
     //Listens Asynchronously to Clients, class a recieveMessageHandler to process the read 
     public void ListenAsync() 
     { 
      myTcpListener.Start(); 

      while (true) 
      { 
       //blocks until a client has connected to the server 
       myTcpClient = myTcpListener.AcceptTcpClient(); 

       var client = new ClientConnection(myTcpClient.GetStream(), new byte[myTcpClient.ReceiveBufferSize]); 

       // Capture the specific client and pass it to the receive handler 
       client.NetworkStream.BeginRead(client.Data, 0, client.Data.Length, r => receiveMessageHandler(r, client), null); 
      } 
     } 

     //Callback is used to Process the request Asynchronously, triggers socketReadCompleteEvent 
     public void receiveMessageHandler(IAsyncResult asyncResult, ClientConnection clientInstance) 
     { 
      bytesReadCount = 0; 

      lock (clientInstance.NetworkStream) 
      { 
       try 
       { 
        bytesReadCount = clientInstance.NetworkStream.EndRead(asyncResult); 
        clientInstance.byteReadCount = bytesReadCount; 
       } 
       catch (Exception exc) 
       { 
        throw exc; 
       } 
      } 

      if (bytesReadCount < MIN_REQUEST_STRING_SIZE) 
      { 
       //Could not read form client. 
       Debug.WriteLine("NO DATA READ"); 
      } 
      else 
      { 
       if (socketReadCompleteEvent != null) 
       { 
        socketReadCompleteEvent(this, clientInstance); 
       } 
      } 
     } 

     //Reads the request, uses the ClientConnection for context 
     public string ReadAsync(ClientConnection connObj) 
     { 
      int bytesReadCount = connObj.byteReadCount; 
      byte[] myData = connObj.Data; 

      string xmlMessage; 

      try 
      { 
       xmlMessage = Encoding.ASCII.GetString(myData, 0, bytesReadCount); 
      } 
      catch (Exception ex) 
      { 
       throw ex; 
      } 

      return xmlMessage; 
     } 

     //Deprecated 
     public string Read() 
     { 
      string xmlMessage; 

      try 
      { 
       xmlMessage = Encoding.ASCII.GetString(myData, 0, bytesReadCount); 
      } 
      catch (Exception ex) 
      { 
       throw ex; 
      } 

      return xmlMessage; 
     } 

     //Deprecated 
     public void Write(byte[] outBytes) 
     { 
      try 
      { 
       myNetworkStream.Write(outBytes, 0, outBytes.Length); 
      } 
      catch (Exception ex) 
      { 
       throw ex; 
      } 
     } 

     //Deprecated 
     public void Write(string outMessage) 
     { 
      byte[] outBytes = Encoding.ASCII.GetBytes(outMessage); 

      try 
      { 
       myNetworkStream.Write(outBytes, 0, outBytes.Length); 
      } 
      catch (Exception ex) 
      { 
       throw ex; 
      } 

      int TimeEnd = Environment.TickCount; 
      int TimeResult = TimeEnd - TimeStart; 
     } 

     //Is used to send the message to the correct socket 
     public void WriteAsync(ClientConnection connObj, string outMessage) 
     { 
      byte[] outBytes = Encoding.ASCII.GetBytes(outMessage); 

      try 
      { 
       connObj.NetworkStream.Write(outBytes, 0, outBytes.Length); 
      } 
      catch (Exception ex) 
      { 
       throw ex; 
      } 

      int TimeEnd = Environment.TickCount; 
      int TimeResult = TimeEnd - TimeStart; 
     } 

     //Closes the client 
     public void Close() 
     { 
      //myNetworkStream.Close(); 
      try 
      { 
       myTcpClient.Close(); 
      } 
      catch (Exception ex) 
      { 

       throw ex; 
      } 
     } 
    } 
} 
+0

你是什么意思“3个请求” ?你期望读的三条消息? –

+0

正确,三条消息一个接一个地发送到服务器。 – Astronaut

+0

您每次通过while循环创建一个新的TCP客户端。读完数据后需要处置或关闭它。此外,为什么每次他们想要发送数据时都要求客户端进程重新连接?这需要TCP层的大量开销。 – user957902

回答

6

最可能的问题是,您希望为客户端做的三个“写入”执行正好三个“读取”。

这是一个错误的假设,因为TCP套接字是字节流并且不保留您的应用程序消息边界。服务器可能会使用客户端发送的三条“消息”中的一条,两条或十七条读取消息。

您需要以某种方式告诉服务器消息在字节流中的结束位置。通常的选择是固定长度的消息,分隔符,指示有效载荷长度的消息头,XML等自描述形式。

因此,您继续从流中读取数据,直到您有完整的处理消息,但在同时您可能会将下一条消息的一部分读入缓冲区。

+0

我正在关闭请求之间的NetworkStream,这是否不清除缓冲区? – Astronaut

+1

我认为关闭流也会关闭底层套接字,所以这是另一个问题。 –

+0

我已经更新了代码。收到和发送的消息都是简短且预定义的XML。我用它来让两个应用程序相互通信。客户端使用XML操作和服务器进程发出请求,然后用XML结果进行响应......在某个时间点只能执行一个操作。 – Astronaut

3

我觉得这里的问题是,你只持有单一NetworkStreammyNetworkStream)正因为如此,如果第二个客户端连接的第一个已发送的数据之前,你接受环将覆盖myNetworkStream与流的第二个连接。当第一个客户端然后发送一些数据的receiveMessageHandler将拜会EndRead第二连接的NetworkStream(这是存储在myNetworkStream第二个客户端连接时),但是从客户端读取的asyncResult传递。这会导致您指出的异常。具体来说,当我测试它时,我收到以下消息:

无法从传输连接读取数据:IAsyncResult对象未从该类的相应异步方法返回。 参数名称:asyncResult。

尝试做如下改变:

// Create a class to hold details related to a client connection 
public class ClientConnection 
{ 
    public ClientConnection(NetworkStream networkStream, byte[] data) 
    { 
     NetworkStream = networkStream; 
     Data = data; 
    } 

    public NetworkStream NetworkStream { get; private set; } 
    public byte[] Data { get; private set; } 
} 

public void Listen() 
{ 
    myTcpListener.Start(); 

    while (true) 
    { 
     //blocks until a client has connected to the server 
     myTcpClient = myTcpListener.AcceptTcpClient(); 

     var client = new ClientConnection(myTcpClient.GetStream(), new byte[myTcpClient.ReceiveBufferSize]); 

     // Capture the specific client and pass it to the receive handler 
     client.NetworkStream.BeginRead(client.Data, 0, client.Data.Length, r => receiveMessageHandler(r, client), null); 
    } 
} 

public void receiveMessageHandler(IAsyncResult asyncResult, ClientConnection client) 
{ 
    var byteReadCount = client.NetworkStream.EndRead(asyncResult); 

    if (byteReadCount < MIN_REQUEST_STRING_SIZE) 
    { 
     //Could not read form client. 
     //Erro - Como tratar? Close() 
    } 
    else 
    { 
     if (socketReadCompleteEvent != null) 
     { 
      socketReadCompleteEvent(this, eventArguments); 
     } 
    } 
} 

正如其他人所提到的,有与你匹配的读取发送者和接收者之间/写入期望更多的问题,但是这似乎是事业你看到的实际问题。

编辑:

这里收到一个完整的消息时,将接收数据,并调用callback方法的服务器。 callback返回一个字符串,然后将其发送回客户端,该客户端使用响应数据调用其自己的replyCallback。每个连接只发送一个请求 - 响应(效率相当低,但应该是一个很好的起点)。

public static class Server 
{ 
    public static void Run(int port, Action<string> callback) 
    { 
     var listener = new TcpListener(IPAddress.Loopback, port); 
     listener.Start(); 

     while (true) 
     { 
      using (var client = listener.AcceptTcpClient()) 
      { 
       try 
       { 
        var buffer = new byte[2048]; 
        using (var memoryStream = new MemoryStream()) 
        { 
         using (var stream = client.GetStream()) 
         { 
          stream.ReadTimeout = 1000; // 1 second timeout 
          int bytesRead; 
          // Loop until Read returns 0, signalling the socket has been closed 
          while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) 
          { 
           memoryStream.Write(buffer, 0, bytesRead); 
          } 
         } 

         // Pass the client's message to the callback and use the response as the reply message to the client. 
         var reply = Encoding.UTF8.GetBytes(callback(Encoding.UTF8.GetString(memoryStream.GetBuffer(), 0, (int)memoryStream.Length))); 
         stream.Write(reply, 0, reply.Length); 
        } 
       } 
       catch (Exception e) 
       { 
        Console.WriteLine("Error: {0}", e.Message); 
       } 
      } 
     } 
    } 
} 

这是一个小型客户端程序,它将连接,发送数据并等待响应。一旦接收到响应,它会通过调用replyCallback与服务器的响应:

public static class Client 
{ 
    public static void Run(string hostname, int port, string dataToSend, Action<string> replyCallback) 
    { 
     using (var client = new TcpClient(hostname, port)) 
     { 
      using (var stream = client.GetStream()) 
      { 
       var buffer = Encoding.UTF8.GetBytes(dataToSend); 
       stream.Write(buffer, 0, buffer.Length); 
       // Shutdown the send side of the socket, indicating to the server we're done sending our message 
       client.Client.Shutdown(SocketShutdown.Send); 
       using (var memoryStream = new MemoryStream()) 
       { 
        stream.ReadTimeout = 1000; // 1 second timeout 
        int bytesRead; 
        // Loop until Read returns 0, signalling the socket has been closed 
        while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) 
        { 
         memoryStream.Write(buffer, 0, bytesRead); 
        } 
        replyCallback(Encoding.UTF8.GetString(memoryStream.GetBuffer(), 0, (int)memoryStream.Length)); 
       } 
      } 
     } 
    } 
} 

和一个小测试,线束,以将其结合在一起:

static class Program 
{ 
    static void Main(string[] args) 
    { 
     var port = 12345; 
     ThreadPool.QueueUserWorkItem(o => Server.Run(port, ProcessClientMessage)); 
     while (true) 
     { 
      Console.WriteLine("Enter a message to send and hit enter (or a blank line to exit)"); 
      var data = Console.ReadLine(); 
      if (string.IsNullOrEmpty(data)) break; 
      Client.Run("localhost", port, data, m => Console.WriteLine("Client received server reply: {0}", m)); 
     } 
    } 

    private static string ProcessClientMessage(string clientMessage) 
    { 
     Console.WriteLine("Server received client message: {0}", clientMessage); 
     // This callback would ordinarily process the client message, then return a string that will be sent back to the client in response. 
     // For now, we'll just return a fixed string value... 
     return "This is the server reply..."; 
    } 
} 
+0

我实际上想阻止它一次只处理一个请求,但没有找到使其同步的方法。发布完整的课程来源。 – Astronaut

+0

@AdamSurfari然后不要使用显式*异步*的'BeginRead'。相反,只需使用“Read”。 – Iridium

+0

我用过了,但现在我陷入了阅读功能......你知道为什么会出现这种情况吗? – Astronaut