摘要
之所以要進行Socket套接字通信庫封裝,主要是直接使用套接字進行網絡通信編程相對複雜,特別對於初學者而言。實際上微軟從.net 2.0開始已經提供了TCP、UDP通信高級封裝類如下:
TcpListener
TcpClient
UdpClient
微軟從.net 4.0開始提供基於Task任務的異步通信接口。而直接使用socket封裝庫,很多socket本身的細節沒辦法自行控制,本文目就是提供一種socket的封裝供參考。文中展示部分封裝了TCP通信庫,UDP封裝也可觸類旁通:
CusTcpListener
CusTcpClient
TCP服務端
TCP服務端封裝了服務端本地綁定、監聽、接受客戶端連接,並提供了網絡數據流的接口。完整代碼:
public class CusTcpListener
{
private IPEndPoint mServerSocketEndPoint;
private Socket mServerSocket;
private bool isActive;
public Socket Server
{
get { return this.mServerSocket; }
}
protected bool Active
{
get { return this.isActive; }
}
public EndPoint LocalEndpoint
{
get
{
if (!this.isActive)
{
return this.mServerSocketEndPoint;
}
return this.mServerSocket.LocalEndPoint;
}
}
public NetworkStream DataStream
{
get
{
NetworkStream networkStream = null;
if (this.Server.Connected)
{
networkStream = new NetworkStream(this.Server, true);
}
return networkStream;
}
}
public CusTcpListener(IPEndPoint localEP)
{
this.mServerSocketEndPoint = localEP;
this.mServerSocket = new Socket(this.mServerSocketEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
}
public CusTcpListener(string localaddr, int port)
{
if (localaddr == null)
{
throw new ArgumentNullException("localaddr");
}
this.mServerSocketEndPoint = new IPEndPoint(IPAddress.Parse(localaddr), port);
this.mServerSocket = new Socket(this.mServerSocketEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
}
public CusTcpListener(int port)
{
this.mServerSocketEndPoint = new IPEndPoint(IPAddress.Any, port);
this.mServerSocket = new Socket(this.mServerSocketEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
}
public void Start()
{
this.Start(int.MaxValue);
}
/// <summary>
/// 開始服務器監聽
/// </summary>
/// <param name="backlog">同時等待連接的最大個數(半連接隊列個數限制)</param>
public void Start(int backlog)
{
if (backlog > int.MaxValue || backlog < 0)
{
throw new ArgumentOutOfRangeException("backlog");
}
if (this.mServerSocket == null)
{
throw new NullReferenceException("套接字爲空");
}
this.mServerSocket.Bind(this.mServerSocketEndPoint);
this.mServerSocket.Listen(backlog);
this.isActive = true;
}
public void Stop()
{
if (this.mServerSocket != null)
{
this.mServerSocket.Close();
this.mServerSocket = null;
}
this.isActive = false;
this.mServerSocket = new Socket(this.mServerSocketEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
}
public Socket AcceptSocket()
{
Socket socket = this.mServerSocket.Accept();
return socket;
}
public CusTcpClient AcceptTcpClient()
{
CusTcpClient tcpClient = new CusTcpClient(this.mServerSocket.Accept());
return tcpClient;
}
}
TCP客戶端
TCP客戶端封裝了客戶端本地綁定、連接服務器,並提供了網絡數據流的接口。完整代碼:
public class CusTcpClient : IDisposable
{
public Socket Client { get; set; }
protected bool Active { get; set; }
public IPEndPoint ClientSocketEndPoint { get; set; }
public bool IsConnected { get { return this.Client.Connected; } }
public NetworkStream DataStream
{
get
{
NetworkStream networkStream = null;
if (this.Client.Connected)
{
networkStream = new NetworkStream(this.Client, true);
}
return networkStream;
}
}
public CusTcpClient(IPEndPoint localEP)
{
if (localEP == null)
{
throw new ArgumentNullException("localEP");
}
this.Client = new Socket(localEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
this.Active = false;
this.Client.Bind(localEP);
this.ClientSocketEndPoint = localEP;
}
public CusTcpClient(string localaddr, int port)
{
if (localaddr == null)
{
throw new ArgumentNullException("localaddr");
}
IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(localaddr), port);
this.Client = new Socket(localEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
this.Active = false;
this.Client.Bind(localEP);
this.ClientSocketEndPoint = localEP;
}
internal CusTcpClient(Socket acceptedSocket)
{
this.Client = acceptedSocket;
this.Active = true;
this.ClientSocketEndPoint = (IPEndPoint)this.Client.LocalEndPoint;
}
public void Connect(string address, int port)
{
if (address == null)
{
throw new ArgumentNullException("address");
}
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(address), port);
this.Connect(remoteEP);
}
public void Connect(IPEndPoint remoteEP)
{
if (remoteEP == null)
{
throw new ArgumentNullException("remoteEP");
}
this.Client.Connect(remoteEP);
this.Active = true;
}
public void Close()
{
this.Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
IDisposable dataStream = this.DataStream;
if (dataStream != null)
{
dataStream.Dispose();
}
else
{
Socket client = this.Client;
if (client != null)
{
client.Close();
this.Client = null;
}
}
GC.SuppressFinalize(this);
}
}
public void Dispose()
{
this.Dispose(true);
}
}
通信實驗
控制檯程序試驗,服務端程序:
class Program
{
static void Main(string[] args)
{
Thread listenerThread = new Thread(ListenerClientConnection);
listenerThread.IsBackground = true;
listenerThread.Start();
Console.ReadKey();
}
private static void ListenerClientConnection()
{
CusTcpListener tcpListener = new CusTcpListener("127.0.0.1", 5100);
tcpListener.Start();
Console.WriteLine("等待客戶端連接……");
while (true)
{
CusTcpClient tcpClient = tcpListener.AcceptTcpClient();
Console.WriteLine("客戶端接入,ip={0} port={1}",
tcpClient.ClientSocketEndPoint.Address, tcpClient.ClientSocketEndPoint.Port);
Thread thread = new Thread(DataHandleProcess);
thread.IsBackground = true;
thread.Start(tcpClient);
}
}
private static void DataHandleProcess(object obj)
{
CusTcpClient tcpClient = (CusTcpClient)obj;
StreamReader streamReader = new StreamReader(tcpClient.DataStream, Encoding.Default);
Console.WriteLine("等待客戶端輸入:");
while (true)
{
try
{
string receStr = streamReader.ReadLine();
Console.WriteLine(receStr);
}
catch (Exception)
{
Console.WriteLine("斷開連接");
break;
}
Thread.Sleep(5);
}
}
}
客戶端程序:
class Program
{
static void Main(string[] args)
{
Thread listenerThread = new Thread(UserProcess);
listenerThread.IsBackground = true;
listenerThread.Start();
Console.ReadKey();
}
private static void UserProcess()
{
Console.WriteLine("連接服務器");
CusTcpClient tcpClient = new CusTcpClient("127.0.0.1", 5080);
tcpClient.Connect("127.0.0.1", 5100);
Console.WriteLine("開始和服務器通信");
StreamWriter sw = new StreamWriter(tcpClient.DataStream, Encoding.Default);
sw.AutoFlush = true;
while (true)
{
for (int i = 0; i < 10; i++)
{
string str = string.Format("第{0}次,內容:{1}", i, "測試通信");
Console.WriteLine("發送數據:{0}", str);
sw.WriteLine(str);
}
break;
}
}
}
通信成功:
通過本次封裝演示可實現基於Socket的通信庫封裝,目的就是使用Socket通信庫讓應用開發人員在進行網絡通訊編程時無需關心底層通訊機制,而只關心應用層的開發,讓開發變得更簡潔。當然UDP封裝類似,可自行設計。當然本文只是一種示例,實際使用可使用.net自帶封裝庫或自定義封裝。
補充:目前有很多優秀的開源Socket框架,比如SuperSocket、FastSocket等。