[英]C# Sockets and Multithreading
我正在尝试了解有关C#中的套接字和线程的更多信息。 我在网上遇到了很多很好的资源,可以帮助我入门。 到目前为止,我编写的程序是一个简单的“中间人”应用程序。 它的设计如下:客户端<-> [应用程序] <->服务器
给定以下代码,如何防止该线程在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告诉它时才做有数据要处理。
为真正友好起见,您还可以异步进行写操作,以使一个连接不能耗尽另一个连接的处理时间(在当前实现中,如果一个写阻塞,则另一个流永远不会得到服务)。
最后,要获得超好效果,您可以将此行为封装在一个对象中,该对象将要使用的流作为参数,然后简单地实例化它们中的两个,而不是拥有两个流并在主代码中进行两次操作。
在接受中间人的套接字后,我执行以下操作:
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问题。
客户端和中间人使用相同的代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.