日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

[C#]手把手教你打造Socket的TCP通讯连接(三)

發(fā)布時間:2025/6/15 C# 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [C#]手把手教你打造Socket的TCP通讯连接(三) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

上一篇中,我們編寫了SocketHandler處理Socket的IO。

現(xiàn)在我們只剩下服務器端了。

服務器端包含兩個類,一個TCPListener,一個TCPListenerClient。

TCPListener只管Start與Stop還有Accept。

TCPListenerClient是連接到服務器的客戶端,相當于TCPClient在TCPListener上的體現(xiàn)。

現(xiàn)在我們開始編寫TCPListener。

/// <summary> /// TCP監(jiān)聽端 /// </summary> public class TCPListener : IEnumerable<TCPListenerClient> {private Socket socket;private HashSet<TCPListenerClient> clients;/// <summary>/// 實例化TCP監(jiān)聽者。/// </summary>public TCPListener(){clients = new HashSet<TCPListenerClient>();IsStarted = false;Handler = new SocketHandler();}public ISocketHandler Handler { get; set; }private int port;/// <summary>/// 監(jiān)聽端口。/// </summary>public int Port{get { return port; }set{if (value < 0 || value > 65535)throw new ArgumentOutOfRangeException(value + "不是有效端口。");port = value;}}/// <summary>/// 服務啟動中/// </summary>public bool IsStarted { get; private set; }/// <summary>/// 開始服務。/// </summary>public void Start(){}/// <summary>/// 停止服務。/// </summary>public void Stop(){}/// <summary>/// 接收完成時引發(fā)事件。/// </summary>public event EventHandler<SocketEventArgs> ReceiveCompleted;/// <summary>/// 接受客戶完成時引發(fā)事件。/// </summary>public event EventHandler<SocketEventArgs> AcceptCompleted;/// <summary>/// 客戶斷開完成時引發(fā)事件。/// </summary>public event EventHandler<SocketEventArgs> DisconnectCompleted;/// <summary>/// 發(fā)送完成時引發(fā)事件。/// </summary>public event EventHandler<SocketEventArgs> SendCompleted;/// <summary>/// 獲取客戶端泛型。/// </summary>/// <returns></returns>public IEnumerator<TCPListenerClient> GetEnumerator(){return clients.GetEnumerator();}/// <summary>/// 獲取客戶端泛型。/// </summary>/// <returns></returns> System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator(){return clients.GetEnumerator();} /// <summary>/// 釋放資源。/// </summary>/// <returns></returns> public void Dispose(){} }

TCPListener繼承IEnumerable<TCPListenerClient>與IDisposable

clients保存所有已連接的客戶端。

編寫Start方法。

/// <summary>/// 開始服務。/// </summary>public void Start(){lock (this){if (IsStarted)throw new InvalidOperationException("已經(jīng)開始服務。");socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//綁定端口//可以引發(fā)端口被占用異常socket.Bind(new IPEndPoint(IPAddress.Any, port));//監(jiān)聽隊列socket.Listen(512);//如果端口是0,則是隨機端口,把這個端口賦值給portport = ((IPEndPoint)socket.LocalEndPoint).Port;//服務啟動中設置為trueIsStarted = true;//開始異步監(jiān)聽socket.BeginAccept(EndAccept, null);}}//異步監(jiān)聽結(jié)束private void EndAccept(IAsyncResult result){//獲得客戶端SocketSocket clientSocket = socket.EndAccept(result);//實例化客戶端類TCPListenerClient client = new TCPListenerClient(this, clientSocket);//增加事件鉤子client.SendCompleted += client_SendCompleted;client.ReceiveCompleted += client_ReceiveCompleted;client.DisconnectCompleted += client_DisconnectCompleted;socket.BeginAccept(EndAccept, null);//增加客戶端lock (clients)clients.Add(client);//客戶端連接事件if (AcceptCompleted != null)AcceptCompleted(this, new SocketEventArgs(client, SocketAsyncOperation.Accept));}//客戶端斷開連接private void client_DisconnectCompleted(object sender, SocketEventArgs e){//移除客戶端lock (clients)clients.Remove((TCPListenerClient)e.Socket);e.Socket.DisconnectCompleted -= client_DisconnectCompleted;e.Socket.ReceiveCompleted -= client_ReceiveCompleted;e.Socket.SendCompleted -= client_SendCompleted;if (DisconnectCompleted != null)DisconnectCompleted(this, e);}//收到客戶端發(fā)送的數(shù)據(jù)private void client_ReceiveCompleted(object sender, SocketEventArgs e){if (ReceiveCompleted != null)ReceiveCompleted(this, e);}//向客戶端發(fā)送數(shù)據(jù)完成private void client_SendCompleted(object sender, SocketEventArgs e){if (SendCompleted != null)SendCompleted(this, e);}

編寫Stop與Dispose方法。

/// <summary>/// 停止服務。/// </summary>public void Stop(){lock (this){if (!IsStarted)throw new InvalidOperationException("沒有開始服務。");foreach (TCPListenerClient client in clients){client.Disconnect();client.DisconnectCompleted -= client_DisconnectCompleted;client.ReceiveCompleted -= client_ReceiveCompleted;client.SendCompleted -= client_SendCompleted;}socket.Close();socket = null;IsStarted = false;}}/// <summary>/// 釋放資源/// </summary>public void Dispose(){if (socket == null)return;Stop();}

輪到TCPListenerClient了,TCPListenerClient其實和TCPClient差不多,也是要繼承ISocket和IDisposable。

既然重復代碼做么多,要不要合并起來呢?答案是肯定的。

做一個SocketBase類,繼承ISocket和IDisposable。

大部分代碼直接從TCPClient復制過來。

View Code public class SocketBase : ISocket, IDisposable {protected Socket Socket { get; private set; }protected Stream Stream { get; set; }/// <summary>/// 實例化TCP客戶端。/// </summary>public SocketBase(Socket socket, ISocketHandler socketHandler){if (socket == null)throw new ArgumentNullException("socket");if (socketHandler == null)throw new ArgumentNullException("socketHandler");Socket = socket;Handler = socketHandler;}/// <summary>/// Socket處理程序/// </summary>public ISocketHandler Handler { get; set; }/// <summary>/// 獲取是否已連接。/// </summary>public bool IsConnected { get { return Socket.Connected; } }#region 斷開連接/// <summary>/// 斷開與服務器的連接。/// </summary>public void Disconnect(){//判斷是否已連接if (!IsConnected)throw new SocketException(10057);lock (this){//Socket異步斷開并等待完成Socket.BeginDisconnect(true, EndDisconnect, true).AsyncWaitHandle.WaitOne();}}/// <summary>/// 異步斷開與服務器的連接。/// </summary>public void DisconnectAsync(){//判斷是否已連接if (!IsConnected)throw new SocketException(10057);lock (this){//Socket異步斷開Socket.BeginDisconnect(true, EndDisconnect, false);}}private void EndDisconnect(IAsyncResult result){try{Socket.EndDisconnect(result);}catch{}//是否同步bool sync = (bool)result.AsyncState;if (!sync && DisconnectCompleted != null){DisconnectCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Disconnect));}}//這是一個給收發(fā)異常準備的斷開引發(fā)事件方法private void Disconnected(bool raiseEvent){if (raiseEvent && DisconnectCompleted != null)DisconnectCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Disconnect));}#endregion#region 發(fā)送數(shù)據(jù)/// <summary>/// 發(fā)送數(shù)據(jù)。/// </summary>/// <param name="data">要發(fā)送的數(shù)據(jù)。</param>public void Send(byte[] data){//是否已連接if (!IsConnected)throw new SocketException(10057);//發(fā)送的數(shù)據(jù)不能為nullif (data == null)throw new ArgumentNullException("data");//發(fā)送的數(shù)據(jù)長度不能為0if (data.Length == 0)throw new ArgumentException("data的長度不能為0");//設置異步狀態(tài)SocketAsyncState state = new SocketAsyncState();state.IsAsync = false;state.Data = data;try{//開始發(fā)送數(shù)據(jù)Handler.BeginSend(data, 0, data.Length, Stream, EndSend, state).AsyncWaitHandle.WaitOne();}catch{//出現(xiàn)異常則斷開Socket連接Disconnected(true);}}/// <summary>/// 異步發(fā)送數(shù)據(jù)。/// </summary>/// <param name="data">要發(fā)送的數(shù)據(jù)。</param>public void SendAsync(byte[] data){//是否已連接if (!IsConnected)throw new SocketException(10057);//發(fā)送的數(shù)據(jù)不能為nullif (data == null)throw new ArgumentNullException("data");//發(fā)送的數(shù)據(jù)長度不能為0if (data.Length == 0)throw new ArgumentException("data的長度不能為0");//設置異步狀態(tài)SocketAsyncState state = new SocketAsyncState();state.IsAsync = true;state.Data = data;try{//開始發(fā)送數(shù)據(jù)并等待完成Handler.BeginSend(data, 0, data.Length, Stream, EndSend, state);}catch{//出現(xiàn)異常則斷開Socket連接Disconnected(true);}}private void EndSend(IAsyncResult result){SocketAsyncState state = (SocketAsyncState)result.AsyncState;//是否完成state.Completed = Handler.EndSend(result);//沒有完成則斷開Socket連接if (!state.Completed)Disconnected(true);//引發(fā)發(fā)送結(jié)束事件if (state.IsAsync && SendCompleted != null){SendCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Send) { Data = state.Data });}}#endregion#region 接收數(shù)據(jù)protected void EndReceive(IAsyncResult result){SocketAsyncState state = (SocketAsyncState)result.AsyncState;//接收到的數(shù)據(jù)byte[] data = Handler.EndReceive(result);//如果數(shù)據(jù)長度為0,則斷開Socket連接if (data.Length == 0){Disconnected(true);return;}//再次開始接收數(shù)據(jù) Handler.BeginReceive(Stream, EndReceive, state);//引發(fā)接收完成事件if (ReceiveCompleted != null)ReceiveCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Receive) { Data = data });}#endregion#region 事件///// <summary>///// 斷開完成時引發(fā)事件。///// </summary>public event EventHandler<SocketEventArgs> DisconnectCompleted;///// <summary>///// 接收完成時引發(fā)事件。///// </summary>public event EventHandler<SocketEventArgs> ReceiveCompleted;///// <summary>///// 發(fā)送完成時引發(fā)事件。///// </summary>public event EventHandler<SocketEventArgs> SendCompleted;#endregion/// <summary>/// 釋放資源/// </summary>public void Dispose(){lock (this){if (IsConnected)Socket.Disconnect(false);Socket.Close();}} }

然后我們再寫TCPListenerClient,繼承SocketBase。

public class TCPListenerClient : SocketBase {internal TCPListenerClient(TCPListener listener, Socket socket):base(socket,listener.Handler){
??????? data = new Dictionary<string, object>();
this["RemoteEndPoint"] = socket.RemoteEndPoint;//創(chuàng)建Socket網(wǎng)絡流Stream = new NetworkStream(socket); //設置服務器Listener = listener; //開始異步接收數(shù)據(jù)SocketAsyncState state = new SocketAsyncState();Handler.BeginReceive(Stream, EndReceive, state);}public TCPListener Listener { get; private set; } }

我們還可以給TCPListenerClient加上點東西,比如類似Session的東西。

private Dictionary<string, object> data;public object this[string key]{get{key = key.ToLower();if (data.ContainsKey(key))return data[key];return null;}set{key = key.ToLower();if (value == null){if (data.ContainsKey(key))data.Remove(key);return;}if (data.ContainsKey(key))data[key] = value;elsedata.Add(key, value);}}

為構(gòu)造函數(shù)添加以下代碼。

data = new Dictionary<string, object>();//保存IP地址到字典this["RemoteEndPoint"] = socket.RemoteEndPoint;

這樣,我們的TCPListenerClient就完成了。

接下來我們再把TCPClient修改以下,繼承SocketBase。

View Code /// <summary> /// TCP客戶端 /// </summary> public class TCPClient : SocketBase {/// <summary>/// 實例化TCP客戶端。/// </summary>public TCPClient(): base(new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp), new SocketHandler()){}public bool IsUseAuthenticate { get; set; }/// <summary>/// 連接至服務器。/// </summary>/// <param name="endpoint">服務器終結(jié)點。</param>public void Connect(IPEndPoint endpoint){//判斷是否已連接if (IsConnected)throw new InvalidOperationException("已連接至服務器。");if (endpoint == null)throw new ArgumentNullException("endpoint");//鎖定自己,避免多線程同時操作lock (this){SocketAsyncState state = new SocketAsyncState();//Socket異步連接 Socket.BeginConnect(endpoint, EndConnect, state).AsyncWaitHandle.WaitOne();//等待異步全部處理完成while (!state.Completed) { }}}/// <summary>/// 異步連接至服務器。/// </summary>/// <param name="endpoint"></param>public void ConnectAsync(IPEndPoint endpoint){//判斷是否已連接if (IsConnected)throw new InvalidOperationException("已連接至服務器。");if (endpoint == null)throw new ArgumentNullException("endpoint");//鎖定自己,避免多線程同時操作lock (this){SocketAsyncState state = new SocketAsyncState();//設置狀態(tài)為異步state.IsAsync = true;//Socket異步連接 Socket.BeginConnect(endpoint, EndConnect, state);}}private void EndConnect(IAsyncResult result){SocketAsyncState state = (SocketAsyncState)result.AsyncState;try{Socket.EndConnect(result);}catch{//出現(xiàn)異常,連接失敗。state.Completed = true;//判斷是否為異步,異步則引發(fā)事件if (state.IsAsync && ConnectCompleted != null)ConnectCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Connect));return;}//連接成功。//創(chuàng)建Socket網(wǎng)絡流Stream = new NetworkStream(Socket);if (IsUseAuthenticate){NegotiateStream negotiate = new NegotiateStream(Stream);negotiate.AuthenticateAsClient();while (!negotiate.IsMutuallyAuthenticated){Thread.Sleep(10);}}//連接完成state.Completed = true;if (state.IsAsync && ConnectCompleted != null){ConnectCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Connect));}//開始接收數(shù)據(jù) Handler.BeginReceive(Stream, EndReceive, state);}/// <summary>/// 連接完成時引發(fā)事件。/// </summary>public event EventHandler<SocketEventArgs> ConnectCompleted; }

所有工作,全部完成。

這個Socket還有很多功能可以增加、改造。

比如你自己寫一個Handler內(nèi)置加密解密,或者壓縮與解壓縮。

還可以再改寫一下Stream,可以弄成NegotiateStream驗證等等。

下一篇我們總結(jié)一下所有工作。

?

原文地址:http://www.cnblogs.com/Kation/archive/2013/03/07/2947278.html

轉(zhuǎn)載于:https://www.cnblogs.com/Kation/archive/2013/03/07/2947278.html

總結(jié)

以上是生活随笔為你收集整理的[C#]手把手教你打造Socket的TCP通讯连接(三)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。