C#套接字和multithreading

我想了解更多有关c#中的套接字和线程的信息。 我在网上遇到了很多很好的资源,以帮助我入门。 我到目前为止制作的程序是一个简单的“中间人”应用程序。 它的设计如下:client [application] server

鉴于以下代码,如何防止此线程以100%CPU运行? 如何让线程等待和阻塞数据,而不是在客户端/服务器空闲时退出?

while (true) { lock (ClientState) { checkConnectionStatus(client, server); } if (clientStream.CanRead && clientStream.DataAvailable) { Byte[] bytes = new Byte[(client.ReceiveBufferSize)]; IAsyncResult result = clientStream.BeginRead(bytes, 0, client.ReceiveBufferSize, null, null); int size = clientStream.EndRead(result); sendData(bytes, serverStream, size); } if (serverStream.CanRead && serverStream.DataAvailable) { Byte[] bytes = new byte[(server.ReceiveBufferSize)]; IAsyncResult result = serverStream.BeginRead(bytes, 0, server.ReceiveBufferSize, null, null); int size = serverStream.EndRead(result); sendData(bytes, clientStream, size); } } 

编辑:决定为感兴趣的人发布整个“Connection.cs”课程。 我是一个初学者编程,所以我知道这里有一些不好的编码实践。 基本上这个整个类在另一个线程中运行,并且在连接(到客户端套接字或服务器套接字)丢失时应该死掉。

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Threading; using System.Net; namespace TCPRelay { public class Connection { public delegate void delThreadSafeHandleException(System.Exception ex); public delegate void ConnectionDelegate(Connection conn); public int DataGridIndex; Main pMain; public TcpClient client { get; set; } public TcpClient server { get; set; } public String ClientState { get; set; } public string ListenPort { get; set; } public string remotePort { get; set; } public string listenAddress { get; set; } public string remoteAddress { get; set; } private TcpListener service { get; set; } private Main Form { get { return pMain; } } private NetworkStream clientStream { get; set; } private NetworkStream serverStream { get; set; } public Connection(TcpClient client, TcpClient server) { clientStream = client.GetStream(); serverStream = server.GetStream(); } public Connection(String srcAddress, int srcPort, String dstAddress, int dstPort, Main caller) { try { pMain = caller; TcpListener _service = new TcpListener((IPAddress.Parse(srcAddress)), srcPort); //Start the client service and add to connection property _service.Start(); service = _service; //Set other useful parameters listenAddress = srcAddress; ListenPort = srcPort.ToString(); remoteAddress = dstAddress; remotePort = dstPort.ToString(); this.ClientState = "Listening"; } catch (Exception ex) { pMain.HandleException(ex); Thread.CurrentThread.Abort(); } } private TcpClient getServerConnection(String address, int port) { TcpClient client = new TcpClient(address, port); if (client.Connected) { return client; } else { throw new Exception( String.Format("Unable to connect to {0} on port {0}", address, port) ); } } private void sendData(Byte[] databuf, NetworkStream stream, int size) { bool waiting = true; while (waiting) { if (stream.CanWrite) { waiting = false; stream.Write(databuf, 0, size); } else { throw new Exception("Unable to write to network stream"); } } } //Main Looping and data processing goes here public void ProcessClientRequest() { try { //Wait for a connection to the client TcpClient client = service.AcceptTcpClient(); //Get the streams and set the peer endpoints this.clientStream = client.GetStream(); this.client = client; //Now that we have a client, lets connect to our server endpoint TcpClient server = getServerConnection(remoteAddress, int.Parse(remotePort)); //Set some useful parameters this.server = server; this.serverStream = server.GetStream(); } catch (Exception ex) { lock (ClientState) { this.ClientState = ex.Message; } CloseConnection(); Thread.CurrentThread.Abort(); } while (true) { lock (ClientState) { checkConnectionStatus(client, server); } if (clientStream.CanRead && clientStream.DataAvailable) { Byte[] bytes = new Byte[(client.ReceiveBufferSize)]; IAsyncResult result = clientStream.BeginRead(bytes, 0, client.ReceiveBufferSize, null, null); int size = clientStream.EndRead(result); sendData(bytes, serverStream, size); } if (serverStream.CanRead && serverStream.DataAvailable) { Byte[] bytes = new byte[(server.ReceiveBufferSize)]; IAsyncResult result = serverStream.BeginRead(bytes, 0, server.ReceiveBufferSize, null, null); int size = serverStream.EndRead(result); sendData(bytes, clientStream, size); } } } private void checkConnectionStatus(TcpClient _client, TcpClient _server) { try { if (_client.Client.Poll(0, SelectMode.SelectRead)) { byte[] buff = new byte[1]; if (_client.Client.Receive(buff, SocketFlags.Peek) == 0) { this.ClientState = "Closed"; CloseConnection(); Thread.CurrentThread.Abort(); } } else if (_server.Client.Poll(0, SelectMode.SelectRead)) { byte[] buff = new byte[1]; if (_server.Client.Receive(buff, SocketFlags.Peek) == 0) { this.ClientState = "Closed"; CloseConnection(); Thread.CurrentThread.Abort(); } } else { this.ClientState = "Connected"; } } catch (System.Net.Sockets.SocketException ex) { this.ClientState = ex.SocketErrorCode.ToString(); CloseConnection(); Thread.CurrentThread.Abort(); } } public void CloseConnection() { if (clientStream != null) { clientStream.Close(); clientStream.Dispose(); } if (client != null) { client.Close(); } if (serverStream != null) { serverStream.Close(); serverStream.Dispose(); } if (server != null) { server.Close(); } if (service != null) { service.Stop(); } } } 

}

我还有一个“Main”表单和一个我正在玩的“ConnectionManager”类。

处理此问题的最有效方法是在每个流上发出带回调的读取。

在发出两个读取之后,等待永远等待用于表示线程应该停止其工作的对象(ManualResetEvent是传统的使用 – 可以用于一次发出多个线程的信号)。

收到数据后,操作系统将调用您的回调函数,然后您将在那里进行处理,然后(重要的是)排队另一个读取。

这意味着你的线程永远处于空闲状态,等待一个信号对象告诉它它是时候离开(在“唤醒 – 死亡时间”的方式),并且只在OS告诉它时才工作有数据要处理。

为了真正友好,你也可以异步进行写操作,这样一个连接就不会饿掉另一个处理时间(在当前实现中,如果一个写入块,则另一个流永远不会得到服务)。

最后,为了超级好,您可以将此行为封装在一个对象中,该对象将要使用的流作为参数,然后简单地实例化其中两个,而不是在主代码中执行两次流并执行两次操作。

在接受中间人的sockets后,我执行以下操作:

  private void WaitForData() { try { if (socketReadCallBack == null) { socketReadCallBack = new AsyncCallback(OnDataReceived); } ReceiveState rState = new ReceiveState(); rState.Client = mySocket; mySocket.BeginReceive(rState.Buffer, 0, rState.Buffer.Length, SocketFlags.None, new AsyncCallback(socketReadCallBack), rState); } catch (SocketException excpt) { // Process Exception } } 

接收国是:

 public class ReceiveState { public byte[] Buffer = new byte[1024]; //buffer for network i/o public int DataSize = 0; //data size to be received by the server public bool DataSizeReceived = false; //whether prefix was received public MemoryStream Data = new MemoryStream(); //place where data is stored public Socket Client; //client socket } 

收到数据后,我的例程“OnDataReceived”会对其进行处理。 我没有遇到任何CPU问题。

用于客户端和中间人的代码相同。