.NET Socket開發之同步Socket實現兩例今天,我們來講一下在.NET 網路應用程式開發中同步Socket的應用,很多人認為在網路應用的服務端Socket不應該使用同步Socket。是的,在大多數情況下是這樣的,但是也有一些情境下我們使用同步Socket可能會得到更的結果。如在下面的兩種情境下我們便可以考慮使用同步的Socket。一、用戶端數量比較少:數量比較少是指會同時串連到伺服器的用戶端數量一般在50人以下。這種情況下我們可以考慮使用同步Socket+Thread來實現我們的服務端。這樣會讓我們編寫邏輯更清晰的代碼而效能不會下降太多。二、用戶端數量較多但都是短串連:短串連是指用戶端的串連在處理完一次收發之後就產即斷開的情境,比如說HTTP協議就是一種短串連。HTTP在用戶端發出請求時建立一個Socket串連,並通過Socket發出一個URL請求,服務端在處理完這個請求並回傳相應的頁面後便會斷開這個串連。那麼在這種情境下我們也可以使用同步Socket來實現我們的需求。
那麼應該如果實現我上面提到的兩種需求呢。對於這兩種需求,我將採用不同的方案來實現它們。首先我們來看看第一種需求,這裡我採用Socket+Thread來實現,基本的流程如下:首先建立一個Socket,並且給它綁定一個EndPoint後開始監聽。接下來我們建立一個線程,在這個線程中我們用一個無限迴圈來接收來自用戶端的串連請求。在接收到一個請求後,為這個用戶端建立一個新的線程,並且在這個線程中也使用一個無限迴圈接收來自這個用戶端的資料。下面讓我們來看看代碼:
代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace SocketServer
{
/// <summary>
/// Socket 同步通訊 Socket+Thread
/// </summary>
public class SocketA
{
Socket server;
public SocketA()
{
//建立一個Socket用來偵聽用戶端的串連
server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(IPAddress.Any,9999));
server.Listen(100);
Console.WriteLine("伺服器啟動......");
//然後建立一個線程來處理用戶端的串連請求
Thread th = new Thread(new ThreadStart(WriteConnction));
th.Start();
Console.ReadLine();
}
public void WriteConnction()
{
Thread.CurrentThread.IsBackground = true;
while(true)
{
Socket s = server.Accept();
IPEndPoint ippoint =(IPEndPoint)s.RemoteEndPoint;
Console.WriteLine("收到串連,來自:" + ippoint.Address.ToString());
//然後建立一個線程來接受資訊
Thread th = new Thread(new ParameterizedThreadStart(WriteMsg));
th.Start(s);
}
}
public void WriteMsg(object obj)
{
Thread.CurrentThread.IsBackground = true;
if(obj !=null)
{
Socket socket = (Socket)obj;
byte[] byt=new byte[1024];
while(true)
{
int getlength = socket.Receive(byt);
if(getlength>0)
{
IPEndPoint ippoint = (IPEndPoint)socket.RemoteEndPoint;
string address=ippoint.Address.ToString();
string msg = System.Text.Encoding.UTF8.GetString(byt, 0, getlength);
Console.WriteLine("收到來自" + address+"的訊息:"+msg);
socket.Send(byt, getlength, SocketFlags.None);
}
}
}
}
}
}
現在讓我們來看看第二個需求:這個方案我們將採用另外一個方法來實現,為什麼不採用上一個方法來實現呢?讓我們來分析一下。我們知道,在上一個實現中,每接入一個用戶端就要建立一個線程,如果有大量的用戶端接入的話,就會建立過多的線程。但是如果線程過多的話,Windows就需要更多的CPU時間來切換線程的上下文(這也是上一個實現不能接入很多用戶端的原因)。我們知道,在這個方案中每一個串連都是短串連。而且順序都是固定的。都是:接入->接收->發送這樣的順序,那麼我們就可以在一個方法中完成整個處理。這樣,我們就可以利用線程池來實現我們所需要的。好了,讓我們用代碼來說話吧:
代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace SocketServer
{
/// <summary>
/// Socket 同步通訊 Socket+Thread
/// 用戶端數量比較少
/// </summary>
public class SocketB
{
Socket server;
public SocketB()
{
//建立一個Socket用來偵聽用戶端的串連
server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(IPAddress.Any, 8888));
server.Listen(100);
Console.WriteLine("伺服器啟動......");
//建立一個線程池
Thread[] ths=new Thread[30];
for (int i = 0; i < ths.Length;i++ )
{
ths[i] = new Thread(new ThreadStart(WaiteConnetion));
}
Console.ReadLine();
}
public void WaiteConnetion()
{
byte[] byt=new byte[1024];
Socket s = server.Accept();
IPEndPoint ipoint=(IPEndPoint)s.RemoteEndPoint;
string ip=ipoint.Address.ToString();
Console.WriteLine("收到串連,來自:" + ip);
int getlength = s.Receive(byt);
if(getlength>0)
{
string msg = System.Text.Encoding.UTF8.GetString(byt);
Console.WriteLine("收到資訊"+ip+"來自"+msg);
s.Send(byt,getlength,SocketFlags.None);
}
s.Shutdown(SocketShutdown.Both);
s.Close();
}
}
}
為什麼我們要這樣做呢? 首先我們建立了一個Socket用於偵聽用戶端的串連請求,接下我們建立了一個擁有30個線程的線程池。並在每個線程中實現了Accept、Receive、Send和Close(),以完成串連、接收、發送、關閉的操作。現在我們假設有一個客戶串連到伺服器了,這時會有一個線程Accept到這個請求,並開始接收用戶端發送過來的資料,接收到資料之後處理完髮送給用戶端,然後關閉這個串連,再次進入等待串連狀態。而其它29個線程由於沒有Accept到這個請求,仍然處理等待接入狀態。