[英]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.