以異步的方式操作TCP/IP套接字——以異步方式實現(xiàn)簡單的聊天室
普通的TCP/IP開發(fā)方式大家都應(yīng)該非常熟練,但在系統(tǒng)開發(fā)的時候往往會遇到問題。
比如:在開發(fā)一個簡單的聊天室的時候,一般情況下,Windows應(yīng)用程序會處于同步方式運行,當(dāng)監(jiān)聽的客戶端越多,服務(wù)器的負(fù)荷將會越重,信息發(fā)送與接收都會受到影響。這時候,我們就應(yīng)該嘗試使用異步的TCP/IP通訊來緩解服務(wù)器的壓力。
下面以一個最簡單的聊天室服務(wù)器端的例子來說明異步TCP/IP的威力,先開發(fā)一個ChatClient類作為客戶管理的代理類,每當(dāng)服務(wù)器接收到信息時,就會把信息處理并發(fā)送給每一個在線客戶。
void Main()
{
IPAddress ipAddress = IPAddress.Parse("127.0.0.1"); //默認(rèn)地址
TcpListener
tcpListener = new TcpListener(ipAddress,500);
tcpListener.Start();
while (isListen)
//以一個死循環(huán)來實現(xiàn)監(jiān)聽
{
ChatClient chatClient =
new
ChatClient(tcpListener.AcceptTcpClient()); //調(diào)用一個ChatClient對象來實現(xiàn)監(jiān)聽
}
tcpListener.Stop();
}
ChatClient中存在著一個Hashtabel類的靜態(tài)變量clients,此變量用來存貯在線的客戶端信息,每當(dāng)對一個客戶端進(jìn)行監(jiān)聽時,系統(tǒng)就生成一個ChatClient對象,然后在變量clients中加入此客戶端的信息。在接收客戶端信息時,信息會調(diào)用Receive(IAsyncResult async)方法,把接收到的信息發(fā)送給每一個在線客戶。
值得注意的是,每當(dāng)接收到客戶信息時,系統(tǒng)都會利用Stream.BeginRead()的方法去接收信息,然后把信息發(fā)送到每一個在線客戶,這樣做就可以利用異步的方式把信息進(jìn)行接收,從而令主線程及早得到釋放,提高系統(tǒng)的性能。
public class ChatClient
{
private
TcpClient _tcpClient;
private byte[] byteMessage;
private string
_clientEndPoint;
public
volatile string
message;
public static Hashtable clients= new
Hashtable(); //以此靜態(tài)變量存處多個客戶端地址
public
ChatClient(TcpClient tcpClient)
{
_tcpClient
=
tcpClient;
_clientEndPoint =
_tcpClient.Client.RemoteEndPoint.ToString();
Console.WrtieLine("連接成功,客戶端EndPoint為"+_clientEndPoint);
ChatClient.clients.Add(_clientEndPoint, this);
//每創(chuàng)建一個對象,就會將客戶端的ChatClient對象存入clients;
byteMessage=new
byte[_tcpClient.ReceiveBufferSize];
lock (_tcpClient.GetStream()) //接收信息,使用lock避免數(shù)據(jù)沖突
{
_tcpClient.GetStream().BeginRead(byteMessage, 0, _tcpClient.ReceiveBufferSize, new AsyncCallback(Receive), null);
//就在此處使用異步的IO線程進(jìn)行數(shù)據(jù)讀取,這樣每個一客戶端的都處于一個IO線程中處理,使主線程及早得到釋放
//這樣做就緩解了服務(wù)器端壓力。
}
}
public void Receive(IAsyncResult iAsyncResult)
{
try
{
int length;
lock (_tcpClient.GetStream())
//信息接收,使用lock避免數(shù)據(jù)沖突
{
length=
_tcpClient.GetStream().EndRead(iAsyncResult);
}
if (length < 1)
{
MessageBox.Show(_tcpClient.Client.RemoteEndPoint + "已經(jīng)斷線");
clients.Remove(_tcpClient);
return ;
}
message=Encoding.Unicode.GetString(byteMessage,0,length);
SendToEveryone(message);
//在此時我們可以在此處調(diào)用SendToEveryone方法,利用clients變量以Stream.Write方法為每個客戶端發(fā)送信息。
lock
(_tcpClient.GetStream()) //再次監(jiān)聽,使用lock避免數(shù)據(jù)沖突
{
_tcpClient.GetStream().BeginRead(byteMessage, 0,
_tcpClient.ReceiveBufferSize, new AsyncCallback(Receive), null);
//再次調(diào)用Stream.BeginRead方法,以監(jiān)聽以下次客戶的信息
}
}
catch (Exception ex)
{
clients.Remove(_tcpClient);
_tcpClient.GetStream().Close();
_tcpClient.Close();
}
}
//通過Send方法把信息轉(zhuǎn)換成二進(jìn)制數(shù)據(jù),然后發(fā)送到客戶端
public void Send(string message)
{
try
{
NetworkStream ns;
lock (_tcpClient.GetStream())
{
ns = _tcpClient.GetStream();
}
byte[] byteMessage = Encoding.ASCII.GetBytes(message);
ns.Write(byteMessage, 0, byteMessage.Length);
ns.Flush();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
//由于客戶端信息記錄在HashTabel變量clients中,當(dāng)信息接收后,就會通過此變量把信息發(fā)送給每一個在線客戶。
public void SendToEveryone(string message)
{
foreach (DictionaryEntry client in clients)
{
ChatClient chatClient = (ChatClient)client.Value;
chatClient.Send(message);
}
}
}
測試結(jié)果:

至于窗口的設(shè)計和客戶端的設(shè)計在這里就省略不說,這里的目的只是要你了解服務(wù)器端多線程TCP/IP信息接收的原理。
這個例子里,ChatClient類使用異步的IO線程進(jìn)行數(shù)據(jù)讀取,這樣每個一客戶端的都處于一個IO線程中處理,使主線程及早得到釋放,這樣做就緩解了服務(wù)器端壓力。
這時候你可以做一個測試,此聊天室在默認(rèn)情況下可接受大約3000個客戶端連接,仍然能夠正常工作
對 JAVA 開發(fā)有興趣的朋友歡迎加入QQ群:174850571 共同探討!
對 .NET 開發(fā)有興趣的朋友歡迎加入QQ群:162338858 共同探討 !
以異步的方式操作TCP/IP套接字——以異步方式實現(xiàn)簡單的聊天室合理使用“.NET擴展方法”來簡化代碼(例子:空值判斷,利用擴展方法實現(xiàn)LINQ操作符ForEach)分部類和分部方法反射的奧妙利用泛型與反射更新實體(ADO.NET Entity Framework)
posted on 2011-01-27 10:52 風(fēng)塵浪子 閱讀(7028) 評論(14) 收藏 舉報
普通的TCP/IP開發(fā)方式大家都應(yīng)該非常熟練,但在系統(tǒng)開發(fā)的時候往往會遇到問題。
比如:在開發(fā)一個簡單的聊天室的時候,一般情況下,Windows應(yīng)用程序會處于同步方式運行,當(dāng)監(jiān)聽的客戶端越多,服務(wù)器的負(fù)荷將會越重,信息發(fā)送與接收都會受到影響。這時候,我們就應(yīng)該嘗試使用異步的TCP/IP通訊來緩解服務(wù)器的壓力。
浙公網(wǎng)安備 33010602011771號