国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > 綜合 > 正文

續(xù)實(shí)例解析SOCKET編程模型之異步通信篇(上)

2024-07-21 02:23:36
字體:
供稿:網(wǎng)友
.net 框架的 socket 類實(shí)際上是 winsock32 api 提供的套接字服務(wù)的托管代碼版本。其中socket 類為網(wǎng)絡(luò)通信提供了一套豐富的方法和屬性,大多數(shù)情況下,socket 類方法只是將數(shù)據(jù)封送到它們的本機(jī)win32 副本中并處理任何必要的安全檢查。socket 類允許使用 protocoltype 枚舉中所列出的任何一種協(xié)議執(zhí)行異步和同步數(shù)據(jù)傳輸。socket 類遵循異步方法的 .net framework 命名模式;例如,同步 receive 方法對(duì)應(yīng)于異步 beginreceive 和 endreceive 方法。

事實(shí)上socket可以象流stream一樣被視為一個(gè)應(yīng)用程序端(客戶端)和遠(yuǎn)程服務(wù)器端之間數(shù)據(jù)通道,通過這個(gè)通道來對(duì)數(shù)據(jù)進(jìn)行讀取(接收)和寫入(發(fā)送)。

異步模式所提供的革新之一就是調(diào)用方確定特定調(diào)用是否應(yīng)是異步的。對(duì)于被調(diào)用的對(duì)象,沒有必要執(zhí)行附加的編程來用于支持其客戶端的異步行為;在該模式中異步委托提供此功能。

如果應(yīng)用程序在執(zhí)行期間只需要一個(gè)線程,請(qǐng)使用我在《實(shí)例解析socket編程模型》中介紹的方法,這些方法適用于單線程同步操作模式。同步操作模式對(duì)執(zhí)行網(wǎng)絡(luò)操作的函數(shù)(如 send 和 receive)的調(diào)用一直等到操作完成后才將控制返回給調(diào)用程序。

若要在執(zhí)行過程中使用單獨(dú)的線程處理通信,請(qǐng)使用下面的方法,這些方法適用于異步操作模式。異步操作模式對(duì)執(zhí)行網(wǎng)絡(luò)操作的函數(shù)的調(diào)用立即返回。

如果當(dāng)前使用的是面向連接的協(xié)議(如 tcp),則可使用 socket、beginconnect 和 endconnect 方法來連接偵聽主機(jī)。通過使用 beginsend 和 endsend 方法,或者使用 beginreceive 和 endreceive 方法,可以進(jìn)行異步數(shù)據(jù)通信。可以使用 beginaccept 和 endaccept 處理傳入的連接請(qǐng)求。
如果當(dāng)前使用的是無連接協(xié)議(如 udp),則可以使用 beginsendto 和 endsendto 來發(fā)送數(shù)據(jù)報(bào),而使用 beginreceivefrom 和 endreceivefrom 來接收數(shù)據(jù)報(bào)。
當(dāng)數(shù)據(jù)發(fā)送和數(shù)據(jù)接收完成之后,可使用 shutdown 方法來禁用 socket。在調(diào)用 shutdown 之后,可調(diào)用 close 方法來釋放與 socket 關(guān)聯(lián)的所有資源。

socket 類允許使用 setsocketoption 方法來配置 socket。可使用 getsocketoption 方法來檢索這些設(shè)置。

注意 如果編寫較簡(jiǎn)單的應(yīng)用程序,而且只需同步數(shù)據(jù)傳輸,則可以考慮使用 tcpclient、tcplistener 和 udpclient。這些類為 socket 通信提供了更簡(jiǎn)單、對(duì)用戶更友好的接口。


從tcp/ip模型上來看, 像 system.net命名空間中的httpwebreqeust類和httpwebresponse類屬于請(qǐng)求/響應(yīng)層。tcpclient、tcplistener 和 udpclient這些類屬于應(yīng)用協(xié)議層,處中間。而socket類處于傳輸層,是最底層。當(dāng)其上面的請(qǐng)求/響應(yīng)層和應(yīng)用協(xié)議層不能滿足應(yīng)用程序的特殊需要時(shí),就需要使用傳輸層進(jìn)行socket套接字編程。



異步服務(wù)器套接字使用 .net framework 異步編程模型處理網(wǎng)絡(luò)服務(wù)請(qǐng)求。socket 類遵循標(biāo)準(zhǔn) .net framework 異步命名模式;例如,同步 accept 方法對(duì)應(yīng)異步 beginaccept 和 endaccept 方法。

異步服務(wù)器套接字需要一個(gè)開始接受網(wǎng)絡(luò)連接請(qǐng)求的方法,一個(gè)處理連接請(qǐng)求并開始接收網(wǎng)絡(luò)數(shù)據(jù)的回調(diào)方法以及一個(gè)結(jié)束接收數(shù)據(jù)的回調(diào)方法。本節(jié)將進(jìn)一步討論所有這些方法。

在下面的源碼中,為開始接受網(wǎng)絡(luò)連接請(qǐng)求,方法 startlistening 初始化 socket,然后使用 beginaccept 方法開始接受新連接。當(dāng)套接字上接收到新連接請(qǐng)求時(shí),將調(diào)用接受回調(diào)方法。它負(fù)責(zé)獲取將處理連接的 socket 實(shí)例,并將 socket 提交給將處理請(qǐng)求的線程。接受回調(diào)方法實(shí)現(xiàn) asynccallback 委托;它返回 void,并帶一個(gè) iasyncresult 類型的參數(shù)。下面的示例是接受回調(diào)方法的外殼程序: private void acceptcallback(iasyncresult ar){}

beginaccept 方法帶兩個(gè)參數(shù):指向接受回調(diào)方法的 asynccallback 委托和一個(gè)用于將狀態(tài)信息傳遞給回調(diào)方法的對(duì)象。在下面的示例中,偵聽 socket 通過狀態(tài)參數(shù)傳遞給回調(diào)方法。本示例創(chuàng)建一個(gè) asynccallback 開始一個(gè)異步操作來接受一個(gè)傳入的連接嘗試 listeningsocket.beginaccept(new asynccallback(acceptcallback),listeningsocket);//

異步套接字使用系統(tǒng)線程池中的線程處理傳入的連接。一個(gè)線程負(fù)責(zé)接受連接,另一線程用于處理每個(gè)傳入的連接,還有一個(gè)線程負(fù)責(zé)接收連接數(shù)據(jù)。這些線程可以是同一個(gè)線程,具體取決于線程池所分配的線程。system.threading.manualresetevent 類掛起主線程的執(zhí)行并在執(zhí)行可以繼續(xù)時(shí)發(fā)出信號(hào)。

接受回調(diào)方法(即前例中的 acceptcallback)負(fù)責(zé)向主應(yīng)用程序發(fā)出信號(hào),讓它繼續(xù)執(zhí)行處理、建立與客戶端的連接并開始異步讀取客戶端數(shù)據(jù)。下面的示例是 acceptcallback 方法實(shí)現(xiàn)的第一部分。該方法的此節(jié)向主應(yīng)用程序線程發(fā)出信號(hào),讓它繼續(xù)處理并建立與客戶端的連接。

開始從客戶端套接字接收數(shù)據(jù)的 acceptcallback 方法的此節(jié)首先初始化 stateobject 類的一個(gè)實(shí)例,然后調(diào)用 beginreceive 方法以開始從客戶端套接字異步讀取數(shù)據(jù)。需要為異步套接字服務(wù)器實(shí)現(xiàn)的 final 方法是返回客戶端發(fā)送的數(shù)據(jù)的讀取回調(diào)方法。與接受回調(diào)方法一樣,讀取回調(diào)方法也是一個(gè) asynccallback 委托。該方法將來自客戶端套接字的一個(gè)或多個(gè)字節(jié)讀入數(shù)據(jù)緩沖區(qū),然后再次調(diào)用 beginreceive 方法,直到客戶端發(fā)送的數(shù)據(jù)完成為止。

創(chuàng)建線程時(shí),將使用采用 threadstart 委托作為其唯一參數(shù)的構(gòu)造函數(shù)創(chuàng)建 thread 類的新實(shí)例。但線程在調(diào)用 start 方法前不會(huì)開始執(zhí)行。調(diào)用 start 后,將從由 threadstart 委托引用的方法的第一行開始執(zhí)行。如下例所示: thread thread=new thread(new threadstart(threadproc));

thread.start();



在以下的源碼中我們使用了manualresetevent 來允許線程通過發(fā)信號(hào)互相通信。通常,此通信涉及一個(gè)線程在其他線程進(jìn)行之前必須完成的任務(wù)。

當(dāng)線程開始一個(gè)活動(dòng)(此活動(dòng)必須在其他線程進(jìn)行之前完成)時(shí),它調(diào)用 reset 將 manualresetevent 設(shè)置為非終止?fàn)顟B(tài)。此線程可被視為控制 manualresetevent。調(diào)用 manualresetevent 上的 waitone 的線程將阻塞,并等待信號(hào)。當(dāng)控制線程完成活動(dòng)時(shí),它調(diào)用 set 以發(fā)出等待線程可以繼續(xù)進(jìn)行的信號(hào)。并釋放所有等待線程。

一旦它被終止,manualresetevent 將保持終止?fàn)顟B(tài),直到它被手動(dòng)重置。即對(duì) waitone 的調(diào)用將立即返回。

可以通過將布爾值傳遞給構(gòu)造函數(shù)來控制 manualresetevent 的初始狀態(tài),如果初始狀態(tài)處于終止?fàn)顟B(tài),為 true;否則為 false。



//以下是異步聊天服務(wù)器端詳細(xì)實(shí)現(xiàn)方法

using system;
using system.drawing;
using system.collections;
using system.componentmodel;
using system.windows.forms;
using system.data;
using system.net;
using system.net.sockets;
using system.threading;
using system.text;
namespace 聊天_socket
{
/// <summary>
/// form1 的摘要說明。
/// </summary>
public class form1 : system.windows.forms.form
{
private system.windows.forms.statusbar statusbar1;
private system.windows.forms.label label1;
private system.windows.forms.label label2;
private system.windows.forms.label label3;
private system.windows.forms.label label4;
private system.windows.forms.richtextbox rtbreceive;
private system.windows.forms.richtextbox rtbsend;
private system.windows.forms.textbox txtserver;
private system.windows.forms.textbox txtport;
private system.windows.forms.button btnlisten;
private system.windows.forms.button btnsend;
private system.windows.forms.button btnstop;
private ipaddress hostipaddress=ipaddress.parse("127.0.0.1");
private ipendpoint server;
private socket listeningsocket;
private socket handler;
private socket mysocket;
private static manualresetevent done=new manualresetevent(false);
private const int buffersize=256;
private byte[] buffer=new byte[buffersize];
string port;
/// <summary>
/// 必需的設(shè)計(jì)器變量。
/// </summary>
private system.componentmodel.container components = null;

public form1()
{
//
// windows 窗體設(shè)計(jì)器支持所必需的
//
initializecomponent();

//
// todo: 在 initializecomponent 調(diào)用后添加任何構(gòu)造函數(shù)代碼
//
}

/// <summary>
/// 清理所有正在使用的資源。
/// </summary>
protected override void dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.dispose();
}
}
base.dispose( disposing );
}

#region windows 窗體設(shè)計(jì)器生成的代碼
/// <summary>
/// 設(shè)計(jì)器支持所需的方法 - 不要使用代碼編輯器修改
/// 此方法的內(nèi)容。
/// </summary>
private void initializecomponent()
{
this.rtbreceive = new system.windows.forms.richtextbox();
this.rtbsend = new system.windows.forms.richtextbox();
this.txtserver = new system.windows.forms.textbox();
this.txtport = new system.windows.forms.textbox();
this.statusbar1 = new system.windows.forms.statusbar();
this.btnlisten = new system.windows.forms.button();
this.btnsend = new system.windows.forms.button();
this.btnstop = new system.windows.forms.button();
this.label1 = new system.windows.forms.label();
this.label2 = new system.windows.forms.label();
this.label3 = new system.windows.forms.label();
this.label4 = new system.windows.forms.label();
this.suspendlayout();
//
// rtbreceive
//
this.rtbreceive.location = new system.drawing.point(80, 56);
this.rtbreceive.name = "rtbreceive";
this.rtbreceive.size = new system.drawing.size(264, 96);
this.rtbreceive.tabindex = 0;
this.rtbreceive.text = "";
//
// rtbsend
//
this.rtbsend.location = new system.drawing.point(80, 152);
this.rtbsend.name = "rtbsend";
this.rtbsend.size = new system.drawing.size(264, 96);
this.rtbsend.tabindex = 1;
this.rtbsend.text = "";
//
// txtserver
//
this.txtserver.location = new system.drawing.point(72, 16);
this.txtserver.name = "txtserver";
this.txtserver.tabindex = 2;
this.txtserver.text = "127.0.0.1";
//
// txtport
//
this.txtport.location = new system.drawing.point(288, 16);
this.txtport.name = "txtport";
this.txtport.size = new system.drawing.size(48, 21);
this.txtport.tabindex = 3;
this.txtport.text = "19811";
//
// statusbar1
//
this.statusbar1.location = new system.drawing.point(0, 287);
this.statusbar1.name = "statusbar1";
this.statusbar1.showpanels = true;
this.statusbar1.size = new system.drawing.size(360, 22);
this.statusbar1.tabindex = 4;
this.statusbar1.text = "statusbar1";
//
// btnlisten
//
this.btnlisten.location = new system.drawing.point(32, 256);
this.btnlisten.name = "btnlisten";
this.btnlisten.tabindex = 5;
this.btnlisten.text = "開始監(jiān)聽";
this.btnlisten.click += new system.eventhandler(this.btnlisten_click);
//
// btnsend
//
this.btnsend.location = new system.drawing.point(144, 256);
this.btnsend.name = "btnsend";
this.btnsend.tabindex = 6;
this.btnsend.text = "發(fā)送信息";
this.btnsend.click += new system.eventhandler(this.btnsend_click);
//
// btnstop
//
this.btnstop.location = new system.drawing.point(256, 256);
this.btnstop.name = "btnstop";
this.btnstop.tabindex = 7;
this.btnstop.text = "停止監(jiān)聽";
this.btnstop.click += new system.eventhandler(this.btnstop_click);
//
// label1
//
this.label1.location = new system.drawing.point(16, 16);
this.label1.name = "label1";
this.label1.size = new system.drawing.size(56, 23);
this.label1.tabindex = 8;
this.label1.text = "服務(wù)器:";
//
// label2
//
this.label2.location = new system.drawing.point(216, 16);
this.label2.name = "label2";
this.label2.size = new system.drawing.size(64, 23);
this.label2.tabindex = 9;
this.label2.text = "監(jiān)聽端口:";
//
// label3
//
this.label3.location = new system.drawing.point(16, 64);
this.label3.name = "label3";
this.label3.size = new system.drawing.size(64, 23);
this.label3.tabindex = 10;
this.label3.text = "接收信息:";
//
// label4
//
this.label4.location = new system.drawing.point(16, 152);
this.label4.name = "label4";
this.label4.size = new system.drawing.size(64, 23);
this.label4.tabindex = 11;
this.label4.text = "發(fā)送信息:";
//
// form1
//
this.autoscalebasesize = new system.drawing.size(6, 14);
this.clientsize = new system.drawing.size(360, 309);
this.controls.add(this.label4);
this.controls.add(this.label3);
this.controls.add(this.label2);
this.controls.add(this.label1);
this.controls.add(this.btnstop);
this.controls.add(this.btnsend);
this.controls.add(this.btnlisten);
this.controls.add(this.statusbar1);
this.controls.add(this.txtport);
this.controls.add(this.txtserver);
this.controls.add(this.rtbsend);
this.controls.add(this.rtbreceive);
this.name = "form1";
this.text = "聊天程序-服務(wù)器";
this.topmost = true;
this.closing += new system.componentmodel.canceleventhandler(this.form1_closing);
this.resumelayout(false);

}
#endregion

/// <summary>
/// 應(yīng)用程序的主入口點(diǎn)。
/// </summary>
[stathread]
static void main()
{
application.run(new form1());
}

private void btnlisten_click(object sender, system.eventargs e)
{
try
{
hostipaddress=ipaddress.parse(txtserver.text);
port=txtport.text;
}
catch{messagebox.show("請(qǐng)輸入正確的ip地址格式");}
try
{ //通過組合服務(wù)的主機(jī) ip 地址和端口號(hào),ipendpoint 類形成到服務(wù)的連接點(diǎn)。
server=new ipendpoint(hostipaddress,int32.parse(port));
// create a socket object to establish a connection with the server
listeningsocket=new socket(addressfamily.internetwork,sockettype.stream,protocoltype.tcp);
listeningsocket.bind(server); //綁定該主機(jī)端口
listeningsocket.listen(50); //監(jiān)聽端口,等待客戶端連接請(qǐng)求。50是隊(duì)列中最多可容納的等待接受的傳入連接數(shù)
statusbar1.text="主機(jī)"+txtserver.text+"端口"+txtport.text+"開始監(jiān)聽.....";
//accept 以同步方式從偵聽套接字的連接請(qǐng)求隊(duì)列中提取第一個(gè)掛起的連接請(qǐng)求,然后創(chuàng)建并返回新的 socket。
//mysocket=listeningsocket.accept();
//一個(gè)進(jìn)程可以創(chuàng)建一個(gè)或多個(gè)線程以執(zhí)行與該進(jìn)程關(guān)聯(lián)的部分程序代碼。使用 threadstart 委托指定由線程執(zhí)行的程序代碼。
thread thread=new thread(new threadstart(threadproc));
thread.start();
}
catch(exception ee){statusbar1.text=ee.message;}
}
private void threadproc()
{
//if(mysocket.connected)
//{
//statusbar1.text="與客戶建立連接.";
while(true)
{
/*byte[] byterecv=new byte[256];
mysocket.receive(byterecv,byterecv.length,0);
string strrecv=encoding.bigendianunicode.getstring(byterecv);
rtbreceive.appendtext(strrecv+"/r/n");*/
done.reset(); //將狀態(tài)設(shè)為非終止
listeningsocket.beginaccept(new asynccallback(acceptcallback),listeningsocket);//開始一個(gè)異步操作來接受一個(gè)傳入的連接嘗試
done.waitone(); //阻塞當(dāng)前線程,直到當(dāng)前線程收到信號(hào)。
}
//}
}
private void acceptcallback(iasyncresult ar)//ar表示異步操作的狀態(tài)。
{
done.set();//設(shè)為終止
mysocket=(socket)ar.asyncstate; //獲取狀態(tài)
handler=mysocket.endaccept(ar); //異步接受傳入的連接嘗試,并創(chuàng)建新的 socket 來處理遠(yuǎn)程主機(jī)通信,獲取結(jié)果
try
{
byte[] bytedata=encoding.bigendianunicode.getbytes("準(zhǔn)備完畢,可以通話"+"/r/n");
//調(diào)用sendcallback異步發(fā)送數(shù)據(jù),
handler.beginsend(bytedata,0,bytedata.length,0,new asynccallback(sendcallback),handler);
}
catch(exception ee){messagebox.show(ee.message);}
thread thread=new thread(new threadstart(threadrev));
thread.start();
}

private void sendcallback(iasyncresult ar)
{
try
{
handler=(socket)ar.asyncstate; //獲取狀態(tài)
int bytessent=handler.endsend(ar);//結(jié)束掛起的異步發(fā)送,返回向 socket 發(fā)送的字節(jié)數(shù)
}
catch{}
}
private void threadrev()
{
handler.beginreceive(buffer,0,buffersize,0,new asynccallback(readcallback),handler);
}

private void readcallback(iasyncresult ar)
{
int bytesread=handler.endreceive(ar); //結(jié)束掛起的異步讀取,返回接收到的字節(jié)數(shù)。
stringbuilder sb=new stringbuilder(); //接收數(shù)據(jù)的可變字符字符串,在通過追加、移除、替換或插入字符而創(chuàng)建它后可以對(duì)它進(jìn)行修改。
sb.append(encoding.bigendianunicode.getstring(buffer,0,bytesread));//追加字符串
string content=sb.tostring(); //轉(zhuǎn)換為字符串
sb.remove(0,content.length); //清除sb內(nèi)容
rtbreceive.appendtext(content+"/r/n");
handler.beginreceive(buffer,0,buffersize,0,new asynccallback(readcallback),handler);
}
private void btnstop_click(object sender, system.eventargs e)
{
try
{
listeningsocket.close();
statusbar1.text="主機(jī)"+txtserver.text+"端口"+txtport.text+"監(jiān)聽停止";
}
catch{messagebox.show("監(jiān)聽尚未開始,關(guān)閉無效");}
}

private void btnsend_click(object sender, system.eventargs e)
{
try
{
string strsend ="server--->"+rtbsend.text+"/r/n";
byte[] bytesend = encoding.bigendianunicode.getbytes(strsend);
handler.beginsend(bytesend,0,bytesend.length,0,new asynccallback(sendcallback),handler);
}
catch{messagebox.show("連接尚未建立,無法發(fā)送.");}
}

private void form1_closing(object sender, system.componentmodel.canceleventargs e)
{
try
{
listeningsocket.close();//在窗口關(guān)閉之前關(guān)閉scoket連接并釋放所有關(guān)聯(lián)的資源。
}
catch{}
}
}
}





發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 青阳县| 福州市| 宜宾市| 铅山县| 老河口市| 东阳市| 繁峙县| 泾源县| 桐庐县| 垦利县| 凉山| 杭锦旗| 涞水县| 民乐县| 泸定县| 韶山市| 义乌市| 合江县| 双牌县| 高阳县| 汉寿县| 大理市| 武清区| 丹巴县| 广丰县| 大姚县| 常熟市| 舞阳县| 饶平县| 布拖县| 沁阳市| 聂拉木县| 青州市| 扎鲁特旗| 太仆寺旗| 北票市| 云浮市| 上思县| 莱阳市| 常德市| 金乡县|