C#中Socket通信編程的異步實(shí)現(xiàn)流程分析
什么是Socket編程的異步是實(shí)現(xiàn)
所謂Socket編程的異步實(shí)現(xiàn)是指按照異步過(guò)程來(lái)實(shí)現(xiàn)Socket編程,那么什么是異步過(guò)程呢,我們把在完成了一次調(diào)用后通過(guò)狀態(tài)、通知和回調(diào)來(lái)告知調(diào)用者的方式成為異步過(guò)程,換句話說(shuō),在異步過(guò)程中當(dāng)調(diào)用一個(gè)方法時(shí),調(diào)用者并不能夠立刻得到結(jié)果,只有當(dāng)這個(gè)方法調(diào)用完畢后調(diào)用者才能獲得調(diào)用結(jié)果。這樣做的好處是什么呢?答案是高效。相信大家還記得我們?cè)凇禖#中Socket通信編程的同步實(shí)現(xiàn)》這篇文章中使用多線程來(lái)實(shí)現(xiàn)簡(jiǎn)單聊天的案例吧,在這個(gè)案例中我們需要開(kāi)啟兩個(gè)線程來(lái)不斷監(jiān)聽(tīng)客戶端的連接和客戶端的消息,這樣的效率肯定是很低的。那么現(xiàn)在好了,我們可以通過(guò)異步過(guò)程來(lái)解決這個(gè)問(wèn)題,下面我們就來(lái)看看如何實(shí)現(xiàn)Socket的異步通信。
如何實(shí)現(xiàn)Socket異步通信
服務(wù)端
基本流程
- 創(chuàng)建套接字
- 綁定套接字的IP和端口號(hào)——Bind()
- 使套接字處于監(jiān)聽(tīng)狀態(tài)等待客戶端的連接請(qǐng)求——Listen()
- 當(dāng)請(qǐng)求到來(lái)后,使用BeginAccept()和EndAccept()方法接受請(qǐng)求,返回新的套接字
- 使用BeginSend()/EndSend和BeginReceive()/EndReceive()兩組方法與客戶端進(jìn)行收發(fā)通信
- 返回,再次等待新的連接請(qǐng)求
- 關(guān)閉套接字
代碼示例
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace AsyncServer
{
public class AsyncTCPServer
{
public void Start()
{
//創(chuàng)建套接字
IPEndPoint ipe = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6065);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//綁定端口和IP
socket.Bind(ipe);
//設(shè)置監(jiān)聽(tīng)
socket.Listen(10);
//連接客戶端
AsyncAccept(socket);
}
/// <summary>
/// 連接到客戶端
/// </summary>
/// <param name="socket"></param>
private void AsyncAccept(Socket socket)
{
socket.BeginAccept(asyncResult =>
{
//獲取客戶端套接字
Socket client = socket.EndAccept(asyncResult);
Console.WriteLine(string.Format("客戶端{(lán)0}請(qǐng)求連接...", client.RemoteEndPoint));
AsyncSend(client, "服務(wù)器收到連接請(qǐng)求");
AsyncSend(client, string.Format("歡迎你{0}",client.RemoteEndPoint));
AsyncReveive(client);
}, null);
}
/// <summary>
/// 接收消息
/// </summary>
/// <param name="client"></param>
private void AsyncReveive(Socket socket)
{
byte[] data = new byte[1024];
try
{
//開(kāi)始接收消息
socket.BeginReceive(data, 0, data.Length, SocketFlags.None,
asyncResult =>
{
int length = socket.EndReceive(asyncResult);
Console.WriteLine(string.Format("客戶端發(fā)送消息:{0}", Encoding.UTF8.GetString(data)));
}, null);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
/// <summary>
/// 發(fā)送消息
/// </summary>
/// <param name="client"></param>
/// <param name="p"></param>
private void AsyncSend(Socket client, string p)
{
if (client == null || p == string.Empty) return;
//數(shù)據(jù)轉(zhuǎn)碼
byte[] data = new byte[1024];
data = Encoding.UTF8.GetBytes(p);
try
{
//開(kāi)始發(fā)送消息
client.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
{
//完成消息發(fā)送
int length = client.EndSend(asyncResult);
//輸出消息
Console.WriteLine(string.Format("服務(wù)器發(fā)出消息:{0}", p));
}, null);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}客戶端
基本流程
- 創(chuàng)建套接字并保證與服務(wù)器的端口一致
- 使用BeginConnect()和EndConnect()這組方法向服務(wù)端發(fā)送連接請(qǐng)求
- 使用BeginSend()/EndSend和BeginReceive()/EndReceive()兩組方法與服務(wù)端進(jìn)行收發(fā)通信
- 關(guān)閉套接字
代碼示例
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace AsyncClient
{
public class AsyncTCPClient
{
/// <summary>
/// 連接到服務(wù)器
/// </summary>
public void AsynConnect()
{
//端口及IP
IPEndPoint ipe = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6065);
//創(chuàng)建套接字
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//開(kāi)始連接到服務(wù)器
client.BeginConnect(ipe, asyncResult =>
{
client.EndConnect(asyncResult);
//向服務(wù)器發(fā)送消息
AsynSend(client,"你好我是客戶端");
AsynSend(client, "第一條消息");
AsynSend(client, "第二條消息");
//接受消息
AsynRecive(client);
}, null);
}
/// <summary>
/// 發(fā)送消息
/// </summary>
/// <param name="socket"></param>
/// <param name="message"></param>
public void AsynSend(Socket socket, string message)
{
if (socket == null || message == string.Empty) return;
//編碼
byte[] data = Encoding.UTF8.GetBytes(message);
try
{
socket.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
{
//完成發(fā)送消息
int length = socket.EndSend(asyncResult);
Console.WriteLine(string.Format("客戶端發(fā)送消息:{0}", message));
}, null);
}
catch (Exception ex)
{
Console.WriteLine("異常信息:{0}", ex.Message);
}
}
/// <summary>
/// 接收消息
/// </summary>
/// <param name="socket"></param>
public void AsynRecive(Socket socket)
{
byte[] data = new byte[1024];
try
{
//開(kāi)始接收數(shù)據(jù)
socket.BeginReceive(data, 0, data.Length, SocketFlags.None,
asyncResult =>
{
int length = socket.EndReceive(asyncResult);
Console.WriteLine(string.Format("收到服務(wù)器消息:{0}", Encoding.UTF8.GetString(data)));
AsynRecive(socket);
}, null);
}
catch (Exception ex)
{
Console.WriteLine("異常信息:", ex.Message);
}
}
}
}從總體上來(lái)講Socket異步編程的邏輯性更加明確了,因?yàn)槲覀冎恍枰獮槊恳粋€(gè)過(guò)程寫(xiě)好回調(diào)函數(shù)就好了。那么這個(gè)示例的效果如何呢?我們來(lái)看看它的演示效果:
總結(jié)
和Socket同步編程的案例相比,今天的這個(gè)案例可能只是對(duì)Socket異步編程內(nèi)容的一個(gè)簡(jiǎn)單應(yīng)用,因?yàn)椴┲鞯浆F(xiàn)在為止都還沒(méi)有寫(xiě)出一個(gè)可以進(jìn)行交互聊天的程序來(lái)。在Socket的異步編程中,服務(wù)端不需要為一個(gè)客戶端單獨(dú)創(chuàng)建一個(gè)線程來(lái)維護(hù)其連接,可是這樣帶來(lái)的一個(gè)問(wèn)題就是博主不知道該如何實(shí)現(xiàn)一個(gè)多客戶端的異步編程的實(shí)例。如果有朋友知道如何實(shí)現(xiàn)的話,還希望能夠告訴我,畢竟學(xué)習(xí)就是一個(gè)相互促進(jìn)的過(guò)程啊。好了,最后想說(shuō)的是博主這段時(shí)間研究Socket異步編程中關(guān)于異步方法調(diào)用的寫(xiě)法問(wèn)題。我們知道Socket異步編程中的方法是成對(duì)出現(xiàn)的,每一個(gè)方法都有一個(gè)回調(diào)函數(shù),對(duì)于回調(diào)函數(shù),這里有兩種寫(xiě)法,以BeginConnect方法為例:
m_Socket.BeginConnect(this.m_ipEndPoint,
new AsyncCallback(this.ConnectCallBack),
this.m_Socket);//其中ConnectCallBack是一個(gè)回調(diào)函數(shù)或者
m_Socket.BeginConnect(this.m_ipEndPoint,asyncResult=>
{
//在這里添加更多代碼
},null)博主為什么要在這里說(shuō)這兩種寫(xiě)法呢,有兩個(gè)原因:
* 第二種寫(xiě)法更為簡(jiǎn)潔,無(wú)需去構(gòu)造容器傳遞Socket和消息,因?yàn)樗鼈兌际蔷植孔兞?。如果我們使用第一種方法,因?yàn)橹骱瘮?shù)和回調(diào)函數(shù)是兩個(gè)不同的函數(shù),因此如果想要共享變量就需要通過(guò)IAsyncResult接口來(lái)訪問(wèn)容器中的值,這樣顯然增加了我們的工作量。
* 第二種寫(xiě)法更為優(yōu)雅,這似乎是C#語(yǔ)言中某種高級(jí)語(yǔ)法,具體叫什么我忘了,反正在Linq中經(jīng)??吹竭@種寫(xiě)法的影子。
綜合以上兩個(gè)觀點(diǎn),博主還是建議大家使用第二種寫(xiě)法,博主打算有空的話將之前寫(xiě)的程序再重新寫(xiě)一遍,看看能不能找出代碼中的問(wèn)題。好了,今天的內(nèi)容就是這樣了,謝謝大家,希望大家喜歡!
到此這篇關(guān)于C#中Socket通信編程的異步實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C# Socket通信編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
gridview的buttonfield獲取該行的索引值(實(shí)例講解)
本篇文章主要介紹了gridview的buttonfield獲取該行的索引值(實(shí)例講解)需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-01-01
使用C#獲取遠(yuǎn)程圖片 Form用戶名與密碼Authorization認(rèn)證的實(shí)現(xiàn)
本篇文章介紹了,使用C#獲取遠(yuǎn)程圖片 Form用戶名與密碼Authorization認(rèn)證的實(shí)現(xiàn)。需要的朋友參考下2013-04-04
WPF輕松實(shí)現(xiàn)進(jìn)度條的示例代碼
WPF中的ProgressBar控件用于表示任務(wù)進(jìn)度,適用于文件下載、數(shù)據(jù)處理等場(chǎng)景,本文將通過(guò)XAML和C#代碼展示如何創(chuàng)建一個(gè)基本的WPF進(jìn)度條,并演示如何通過(guò)事件處理程序更新進(jìn)度條的值來(lái)模擬耗時(shí)操作,感興趣的小伙伴跟著小編一起來(lái)看看吧2024-12-12
C#調(diào)用非托管動(dòng)態(tài)庫(kù)中的函數(shù)方法
這篇文章主要介紹了C#調(diào)用非托管動(dòng)態(tài)庫(kù)中的函數(shù)方法,本文講解創(chuàng)建一個(gè)非托管動(dòng)態(tài)庫(kù),然后在C#中調(diào)用它,需要的朋友可以參考下2015-02-02
C#實(shí)現(xiàn)十進(jìn)制數(shù)轉(zhuǎn)換為十六進(jìn)制的幾種方式
在C#中,十進(jìn)制和十六進(jìn)制轉(zhuǎn)換非常簡(jiǎn)單,本文給大家介紹了C#實(shí)現(xiàn)十進(jìn)制數(shù)轉(zhuǎn)換為十六進(jìn)制的幾種方式,并通過(guò)代碼示例講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-04-04

