注冊會員,創(chuàng)建你的web開發(fā)資料庫,點對點即peer-to-peer,通常簡寫為p2p。所謂網(wǎng)絡(luò)中的點對點,其實可以看成是一種對等的網(wǎng)絡(luò)模型。p2p其實是實現(xiàn)網(wǎng)絡(luò)上不同計算機(jī)之間,不經(jīng)過中繼設(shè)備直接交換數(shù)據(jù)或服務(wù)的一種技術(shù)。p2p由于允許網(wǎng)絡(luò)中任一臺計算機(jī)可以直接連接到網(wǎng)絡(luò)中其他計算機(jī),并與之進(jìn)行數(shù)據(jù)交換,這樣既消除了中間環(huán)節(jié),也使得網(wǎng)絡(luò)上的溝通變得更容易、更直接。
p2p作為一種網(wǎng)絡(luò)的模型,它有別于傳統(tǒng)的客戶/服務(wù)器模型。客戶/服務(wù)器模型一般都有預(yù)定義的客戶機(jī)和服務(wù)器。而在p2p模型轉(zhuǎn)并沒有明確的客戶端和服務(wù)器,但其實在p2p模型中,每一臺計算機(jī)既可以看成是服務(wù)器,也可以看成是客戶機(jī)。在網(wǎng)絡(luò)中,傳統(tǒng)上的客戶機(jī)/服務(wù)器通訊模型中,發(fā)送服務(wù)請求或者發(fā)送數(shù)據(jù)的計算機(jī),一般稱為客戶機(jī);而接收、處理服務(wù)或接收數(shù)據(jù)的計算機(jī)稱為服務(wù)器。而在p2p網(wǎng)絡(luò)模型中,計算機(jī)不僅接收數(shù)據(jù),還有發(fā)送數(shù)據(jù),不僅提出服務(wù)請求,還有接收對方的服務(wù)請求。
在下面介紹的用visual c#實現(xiàn)的局域網(wǎng)點對點通訊程序,就有如下特點,在網(wǎng)絡(luò)利用此通訊程序進(jìn)行通訊的任一計算機(jī),在通訊之前,都需要偵聽端口號,接受其他機(jī)器的連接申請,并在連接建立后,就可以接收對方發(fā)送來的數(shù)據(jù);同時也可以向其他機(jī)器提出連接申請,并在對方計算機(jī)允許建立連接請求后,發(fā)送數(shù)據(jù)到對方。可見在網(wǎng)絡(luò)中利用此軟件進(jìn)行p2p網(wǎng)絡(luò)通訊的任一計算機(jī)既是客戶機(jī),同樣也是服務(wù)器。
一.程序的設(shè)計、調(diào)試、運(yùn)行的軟件環(huán)境:
(1).微軟公司視窗2000服務(wù)器版
(2).visual studio .net正式版,.net framework sdk版本號3705
二.關(guān)鍵步驟及其解決方法: 關(guān)鍵步驟就是實現(xiàn)信息在網(wǎng)絡(luò)中的發(fā)送和接收。數(shù)據(jù)接收使用的是socket,數(shù)據(jù)發(fā)送使用的是networkstream。
1.利用socket來接收信息:
為了更清楚的說明問題,程序在處理數(shù)據(jù)發(fā)送和接收時采用了不通的端口號,發(fā)送數(shù)據(jù)程序在缺省狀態(tài)設(shè)定的端口號為"8889"。下面代碼是偵聽端口號"8889",接受網(wǎng)絡(luò)中對此端口號的連接請求,并在建立連接后,通過socket接收遠(yuǎn)程計算機(jī)發(fā)送來的數(shù)據(jù):
try
{
tcplistener tllisten1 = new tcplistener ( 8889 ) ;
//偵聽端口號
tllisten1.start ( ) ;
socket sksocket = tllisten1.acceptsocket ( );
//接受遠(yuǎn)程計算機(jī)的連接請求,并獲得用以接收數(shù)據(jù)的socket實例
endpoint tempremoteep = sksocket.remoteendpoint;
//獲得遠(yuǎn)程計算機(jī)對應(yīng)的網(wǎng)絡(luò)遠(yuǎn)程終結(jié)點
while (true)
{
byte [] bystream = new byte[80];
//定義從遠(yuǎn)程計算機(jī)接收到數(shù)據(jù)存放的數(shù)據(jù)緩沖區(qū)
int i = sksocket.receivefrom(bystream,ref tempremoteep);
//接收數(shù)據(jù),并存放到定義的緩沖區(qū)中
string smessage = system.text.encoding.utf8.getstring(bystream);
//以指定的編碼,從緩沖區(qū)中解析出內(nèi)容
messagebox.show ( smessage );
//顯示傳送來的數(shù)據(jù)
}
}
catch ( system.security.securityexception )
{
messagebox.show ( "防火墻安全錯誤!","錯誤",
messageboxbuttons.ok , messageboxicon.exclamation);
}
2.利用networkstream來傳送信息:
在使用streamwriter處理networkstream傳送數(shù)據(jù)時,數(shù)據(jù)傳送的編碼類型是"utf8",下列代碼是對ip地址為"10.138.198.213"的計算機(jī)的"8888"端口號提出連接申請,并在連接申請建立后,以utf8編碼發(fā)送字符串"您好,見到您很高興"到對方,由于下列代碼中的注釋比較詳細(xì),這里就不具體介紹了,下列代碼也是使用networkstream傳送數(shù)據(jù)的典型代碼:
try
{
tcpclient tcpc = new tcpclient ("10.138.198.213",8888);
//對ip地址為"10.138.198.213"的計算機(jī)的8888端口提出連接申請
networkstream tcpstream = tcpc.getstream ( );
//如果連接申請建立,則獲得用以傳送數(shù)據(jù)的數(shù)據(jù)流
}
catch ( exception )
{
messagebox.show ( "目標(biāo)計算機(jī)拒絕連接請求!" ) ;
break ;
}
try
{
string smsg = "您好,見到您很高興" ;
streamwriter reqstreamw = new streamwriter (tcpstream);
//以特定的編碼往向數(shù)據(jù)流中寫入數(shù)據(jù) ,默認(rèn)為utf8編碼
reqstreamw.write (smsg);
//將字符串寫入數(shù)據(jù)流中
reqstreamw.flush ( );
//清理當(dāng)前編寫器的所有緩沖區(qū),并使所有緩沖數(shù)據(jù)寫入基礎(chǔ)流
}
catch(exception)
{
messagebox.show ("無法發(fā)送信息到目標(biāo)計算機(jī)!") ;
}
當(dāng)然在具體用visual c#實現(xiàn)網(wǎng)絡(luò)點對點通訊程序時,還必須掌握很多其他方面的知識,如資源的回收。在用visual c#編寫網(wǎng)絡(luò)應(yīng)用程序的時候,很多朋友遇到這樣的情況。當(dāng)程序退出后,通過windows的"資源管理器"看到的是進(jìn)程數(shù)目并沒有減少。這是因為程序中使用的線程可能并沒有有效退出。雖然thread類中提供了"abort"方法用以中止進(jìn)程,但并不能夠保證成功退出。因為進(jìn)程中使用的某些資源并沒有回收。在某些情況下垃圾回收器也不能保證完全的回收資源,還是需要我們自己手動回收資源的。在本文介紹的程序中也涉及到資源手動回收的問題。實現(xiàn)方法可參閱下面具體實現(xiàn)步驟中的第十二步。
三.具體步驟:
在了解、掌握了上面的關(guān)鍵問題及其解決方法后,再實現(xiàn)用visual c#實現(xiàn)網(wǎng)絡(luò)點對點通訊程序相對就容易許多,下面是具體的實現(xiàn)步驟:
1.啟動visual studio .net,并新建一個visual c#項目,名稱為【visual c#實現(xiàn)網(wǎng)絡(luò)點對點通訊程序】。
2.在visual studio .net集成開發(fā)環(huán)境中的【解決方案資源管理器】窗口中,雙擊form1.cs文件,進(jìn)入form1.cs文件的編輯界面。
3.在form1.cs文件的開頭,用下列導(dǎo)入命名空間代碼替代系統(tǒng)缺省的導(dǎo)入命名空間代碼。
using system ;
using system.drawing ;
using system.collections ;
using system.componentmodel ;
using system.windows.forms ;
using system.data ;
using system.net.sockets ;
using system.net ;
using system.io ;
using system.text ;
using system.threading ;
4.再把visual studio.net的當(dāng)前窗口切換到【form1.cs(設(shè)計)】窗口,并從【工具箱】中的【windows窗體組件】選項卡中往窗體中拖入下列組件:
四個button組件;二個listbox組件;四個textbox組件;一個statusbar組件;五個label組件。并在四個button組件拖入窗體后,分別在窗體設(shè)計界面中雙擊它們,則系統(tǒng)會在form1.cs文件中分別產(chǎn)生這四個組件的click事件對應(yīng)的處理代碼。
5.在【解決方案資源管理器】窗口中,雙擊form1.cs文件,進(jìn)入form1.cs文件的編輯界面。以下面代碼替代系統(tǒng)產(chǎn)生的initializecomponent過程。下面代碼是對上面添加的組件進(jìn)行初始化:
private void initializecomponent ( )
{
this.listbox1 = new system.windows.forms.listbox ( ) ;
this.textbox1 = new system.windows.forms.textbox ( ) ;
this.label3 = new system.windows.forms.label ( ) ;
this.label2 = new system.windows.forms.label ( ) ;
this.textbox3 = new system.windows.forms.textbox ( ) ;
this.button1 = new system.windows.forms.button ( ) ;
this.textbox2 = new system.windows.forms.textbox ( ) ;
this.label1 = new system.windows.forms.label ( ) ;
this.label4 = new system.windows.forms.label ( ) ;
this.label5 = new system.windows.forms.label ( ) ;
this.button2 = new system.windows.forms.button ( ) ;
this.button3 = new system.windows.forms.button ( ) ;
this.button4 = new system.windows.forms.button ( ) ;
this.textbox4 = new system.windows.forms.textbox ( ) ;
this.statusbar1 = new system.windows.forms.statusbar ( ) ;
this.statusbarpanel1 = new system.windows.forms.statusbarpanel( );
this.statusbarpanel2 = new system.windows.forms.statusbarpanel( );
this.label6 = new system.windows.forms.label ( ) ;
this.listbox2 = new system.windows.forms.listbox ( ) ;
( ( system.componentmodel.isupportinitialize )
( this.statusbarpanel1 ) ).begininit ( ) ;
( ( system.componentmodel.isupportinitialize )
( this.statusbarpanel2 ) ).begininit ( ) ;
this.suspendlayout ( ) ;
this.listbox1.itemheight = 12 ;
this.listbox1.location = new system.drawing.point ( 122 , 110 ) ;
this.listbox1.name = "listbox1" ;
this.listbox1.size = new system.drawing.size ( 212 , 88 ) ;
this.listbox1.tabindex = 4 ;
this.textbox1.location = new system.drawing.point ( 122 , 18 ) ;
this.textbox1.name = "textbox1" ;
this.textbox1.size = new system.drawing.size ( 210 , 21 ) ;
this.textbox1.tabindex = 1 ;
this.textbox1.text = "" ;
this.label3.location = new system.drawing.point ( 220 , 52 ) ;
this.label3.name = "label3" ;
this.label3.size = new system.drawing.size ( 66 , 23 ) ;
this.label3.tabindex = 7 ;
this.label3.text = "本地端口:" ;
this.label2.location = new system.drawing.point ( 38 , 54 ) ;
this.label2.name = "label2" ;
this.label2.size = new system.drawing.size ( 80 , 23 ) ;
this.label2.tabindex = 20 ;
this.label2.text = "遠(yuǎn)程端口號:" ;
this.textbox3.location = new system.drawing.point ( 294 , 50 );
this.textbox3.name = "textbox3" ;
this.textbox3.size = new system.drawing.size ( 38 , 21 ) ;
this.textbox3.tabindex = 3 ;
this.textbox3.text = "8889" ;
this.button1.flatstyle = system.windows.forms.flatstyle.flat ;
this.button1.location = new system.drawing.point ( 348 , 16 );
this.button1.name = "button1" ;
this.button1.size = new system.drawing.size ( 92 , 40 );
this.button1.tabindex = 6 ;
this.button1.text = "連接遠(yuǎn)程機(jī)" ;
this.button1.click += new system.eventhandler(this.button1_click);
this.textbox2.location = new system.drawing.point ( 122 , 50 ) ;
this.textbox2.name = "textbox2" ;
this.textbox2.size = new system.drawing.size ( 38 , 21 ) ;
this.textbox2.tabindex = 2 ;
this.textbox2.text = "8888" ;
this.label1.location = new system.drawing.point (38,22);
this.label1.name = "label1" ;
this.label1.size = new system.drawing.size ( 80 , 23 ) ;
this.label1.tabindex = 16 ;
this.label1.text = "遠(yuǎn)程ip地址:" ;
this.label4.location = new system.drawing.point ( 50 , 84 ) ;
this.label4.name = "label4" ;
this.label4.size = new system.drawing.size ( 66 , 23 ) ;
this.label4.tabindex = 23 ;
this.label4.text = "發(fā)送信息:" ;
this.label5.location = new system.drawing.point ( 36 , 112 ) ;
this.label5.name = "label5" ;
this.label5.size = new system.drawing.size ( 80 , 23 ) ;
this.label5.tabindex = 24 ;
this.label5.text = "發(fā)送的信息:" ;
this.button2.enabled = false ;
this.button2.flatstyle = system.windows.forms.flatstyle.flat ;
this.button2.location = new system.drawing.point ( 352 , 192 ) ;
this.button2.name = "button2" ;
this.button2.size = new system.drawing.size ( 92 , 40 ) ;
this.button2.tabindex = 7 ;
this.button2.text = "斷開連接" ;
this.button2.click += new system.eventhandler(this.button2_click);
this.button3.flatstyle = system.windows.forms.flatstyle.flat ;
this.button3.location = new system.drawing.point ( 348 , 74 );
this.button3.name = "button3" ;
this.button3.size = new system.drawing.size ( 92 , 40 ) ;
this.button3.tabindex = 8 ;
this.button3.text = "偵聽端口" ;
this.button3.click += new system.eventhandler(this.button3_click);
this.button4.enabled = false ;
this.button4.flatstyle = system.windows.forms.flatstyle.flat ;
this.button4.location = new system.drawing.point ( 350 , 132 ) ;
this.button4.name = "button4" ;
this.button4.size = new system.drawing.size ( 92 , 40 );
this.button4.tabindex = 9 ;
this.button4.text = "發(fā)送信息" ;
this.button4.click += new system.eventhandler(this.button4_click);
this.textbox4.location = new system.drawing.point ( 122 , 82 ) ;
this.textbox4.name = "textbox4" ;
this.textbox4.size = new system.drawing.size ( 212 , 21 ) ;
this.textbox4.tabindex = 25 ;
this.textbox4.text = "" ;
this.statusbar1.location = new system.drawing.point ( 0 , 301 ) ;
this.statusbar1.name = "statusbar1" ;
this.statusbar1.panels.addrange ( new system.windows.forms.
statusbarpanel[] {
this.statusbarpanel1 ,this.statusbarpanel2} ) ;
this.statusbar1.showpanels = true ;
this.statusbar1.size = new system.drawing.size ( 456 , 22 ) ;
this.statusbar1.tabindex = 26 ;
this.statusbarpanel1.width = 200 ;
this.statusbarpanel2.width = 230 ;
this.label6.location = new system.drawing.point ( 48 , 210 ) ;
this.label6.name = "label6" ;
this.label6.size = new system.drawing.size ( 66 , 23 ) ;
this.label6.tabindex = 28 ;
this.label6.text = "接收信息:" ;
this.listbox2.itemheight = 12 ;
this.listbox2.location = new system.drawing.point (122,206);
this.listbox2.name = "listbox2" ;
this.listbox2.size = new system.drawing.size ( 214 , 88 ) ;
this.listbox2.tabindex = 27 ;
this.autoscalebasesize = new system.drawing.size ( 6 , 14 ) ;
this.clientsize = new system.drawing.size ( 456 , 323 ) ;
this.controls.addrange ( new system.windows.forms.control[] {
this.label6 ,
this.listbox2 ,
this.statusbar1 ,
this.textbox4 ,
this.button4 ,
this.button3 ,
this.button2 ,
this.label5 ,
this.label4 ,
this.label2 ,
this.textbox3 ,
this.button1 ,
this.textbox2 ,
this.label1 ,
this.label3 ,
this.textbox1 ,
this.listbox1} ) ;
this.formborderstyle = system.windows.forms.formborderstyle.
fixedsingle ;
this.maximizebox = false ;
this.name = "form1" ;
this.text = "visual c#實現(xiàn)網(wǎng)絡(luò)點對點通訊程序" ;
this.load += new system.eventhandler ( this.form1_load ) ;
( ( system.componentmodel.isupportinitialize )
( this.statusbarpanel1 ) ).endinit ( ) ;
( ( system.componentmodel.isupportinitialize )
( this.statusbarpanel2 ) ).endinit ( ) ;
this.resumelayout ( false ) ;
}
至此,【visual c#實現(xiàn)網(wǎng)絡(luò)點對點通訊程序】項目的界面設(shè)計和功能實現(xiàn)的前期工作就完成了,設(shè)計界面如圖1所示:
圖1 項目的設(shè)計界面
6.在【解決方案資源管理器】窗口中,雙擊form1.cs文件,進(jìn)入form1.cs文件的編輯界面。并在定義form類成員代碼區(qū)中,加入下列代碼,下列代碼的作用是定義程序中使用的全局變量。
private thread th ;
//創(chuàng)建線程,用以偵聽端口號,接收信息
private tcplistener tllisten1 ;
//用以偵聽端口號
private bool listenerrun = true ;
//設(shè)定標(biāo)示位,判斷偵聽狀態(tài)
private networkstream tcpstream ;
//創(chuàng)建傳送/接收的基本數(shù)據(jù)流實例
private streamwriter reqstreamw ;
//用以實現(xiàn)向遠(yuǎn)程主機(jī)傳送信息
private tcpclient tcpc ;
//用以創(chuàng)建對遠(yuǎn)程主機(jī)的連接
private socket sksocket ;
//用以接收遠(yuǎn)程主機(jī)傳送來的數(shù)據(jù)
7.用下列代碼替換form1.cs中的button1組件的"click"事件對應(yīng)的代碼,下列代碼的作用是向遠(yuǎn)程計算機(jī)提出連接申請,如果連接建立,則獲得傳送數(shù)據(jù)的數(shù)據(jù)源:
private void button1_click (object sender, system.eventargs e)
{
try
{
tcpc = new tcpclient ( textbox1.text ,
int32.parse ( textbox3.text ) ) ;
//向遠(yuǎn)程計算機(jī)提出連接申請
tcpstream = tcpc.getstream ( ) ;
//如果連接申請建立,則獲得用以傳送數(shù)據(jù)的數(shù)據(jù)流
statusbar1.panels[0].text="成功連接遠(yuǎn)程計算機(jī)!" ;
button2.enabled = true ;
button1.enabled = false ;
button4.enabled = true ;
}
catch ( exception )
{
statusbar1.panels[0].text = "目標(biāo)計算機(jī)拒絕連接請求!" ;
}
}
8.在form1.cs中的main函數(shù)之后,添加下列代碼,下面代碼是定義一個名稱為"listen"的過程:
private void listen ( )
{
try
{
tllisten1 = new tcplistener ( int32.parse(textbox2.text));
tllisten1.start ( ) ;
//偵聽指定端口號
statusbar1.panels[1].text = "正在監(jiān)聽..." ;
//接受遠(yuǎn)程計算機(jī)的連接請求,并獲得用以接收數(shù)據(jù)的socket實例
sksocket = tllisten1.acceptsocket ( ) ;
//獲得遠(yuǎn)程計算機(jī)對應(yīng)的網(wǎng)絡(luò)遠(yuǎn)程終結(jié)點
endpoint tempremoteep = sksocket.remoteendpoint ;
ipendpoint tempremoteip = ( ipendpoint )tempremoteep ;
iphostentry host = dns.gethostbyaddress
( tempremoteip.address ) ;
string hostname = host.hostname ;
//根據(jù)獲得的遠(yuǎn)程計算機(jī)對應(yīng)的網(wǎng)絡(luò)遠(yuǎn)程終結(jié)點獲得遠(yuǎn)程計算機(jī)的名稱
statusbar1.panels[1].text = "'" + hostname +"' " +
"遠(yuǎn)程計算機(jī)正確連接!" ;
//循環(huán)偵聽
while ( listenerrun )
{
byte[] stream = new byte[80] ;
//定義從遠(yuǎn)程計算機(jī)接收到數(shù)據(jù)存放的數(shù)據(jù)緩沖區(qū)
string time = datetime.now.tostring ( ) ;
//獲得當(dāng)前的時間
int i = sksocket.receivefrom ( stream,
ref tempremoteep ) ;
//接收數(shù)據(jù),并存放到定義的緩沖區(qū)中
string smessage = system.text.encoding.utf8.
getstring ( stream ) ;
//以指定的編碼,從緩沖區(qū)中解析出內(nèi)容
listbox2.items.add(time+""+hostname+":");
listbox2.items.add ( smessage ) ;
//顯示接收到的數(shù)據(jù)
}
}
catch ( system.security.securityexception )
{
messagebox.show ( "防火墻安全錯誤!" ,"錯誤" ,
messageboxbuttons.ok , messageboxicon.exclamation) ;
}
}
9.用下列代碼替換form1.cs中的button2組件的click事件對應(yīng)的處理代碼,下列代碼的作用是斷開當(dāng)前的連接:
private void button2_click ( object sender, system.eventargs e)
{
listenerrun = false ;
tcpc.close ( ) ;
statusbar1.panels[0].text = "斷開連接!" ;
button1.enabled = true ;
button2.enabled = false ;
button4.enabled = false ;
}
10.用下列代碼替換form1.cs中的button3組件的click事件對應(yīng)的處理代碼,下列代碼的作用是以上面定義的listen過程來初始化線程實例,并啟動線程,達(dá)到偵聽端口的目的:
private void button3_click (object sender , system.eventargs e)
{
th = new thread ( new threadstart ( listen ) ) ;
//以listen過程來初始化線程實例
th.start ( ) ;
//啟動此線程
}
11.用下列代碼替換form1.cs中的button4組件的click事件對應(yīng)的處理代碼,下列代碼的作用是向遠(yuǎn)程計算機(jī)的指定端口號發(fā)送信息。
private void button4_click ( object sender,system.eventargs e)
{
try
{
string smsg = textbox4.text ;
string myname = dns.gethostname ( ) ;
//以特定的編碼往向數(shù)據(jù)流中寫入數(shù)據(jù),
//默認(rèn)為utf8encoding 的實例
reqstreamw = new streamwriter ( tcpstream ) ;
//將字符串寫入數(shù)據(jù)流中
reqstreamw.write ( smsg ) ;
//清理當(dāng)前編寫器的所有緩沖區(qū),并使所有緩沖數(shù)據(jù)寫入基礎(chǔ)流
reqstreamw.flush ( ) ;
string time = datetime.now.tostring ( ) ;
//顯示傳送的數(shù)據(jù)和時間
listbox1.items.add ( time +" " + myname +":" ) ;
listbox1.items.add (smsg ) ;
textbox4.clear ( ) ;
}
//異常處理
catch ( exception )
{
statusbar1.panels[0].text = "無法發(fā)送信息到目標(biāo)計算機(jī)!";
}
}
12.用下列代碼替換form1.cs中的"dispose"過程對應(yīng)的處理代碼,下列代碼的作用是在程序退出后,清除沒有回收的資源:
protected override void dispose ( bool disposing )
{
try
{
listenerrun = false ;
th.abort ( ) ;
th = null ;
tllisten1.stop ( ) ;
sksocket.close ( ) ;
tcpc.close ( ) ;
}
catch { }
if ( disposing )
{
if ( components != null )
{
components.dispose ( ) ;
}
}
base.dispose ( disposing ) ;
}
13.運(yùn)行程序,實現(xiàn)網(wǎng)絡(luò)點對點通訊:
單擊快捷鍵f5編譯成功后,把此程序分發(fā)到網(wǎng)絡(luò)中的二臺計算機(jī)中。在正確輸入偵聽端口號、遠(yuǎn)程計算機(jī)ip地址、遠(yuǎn)程端口號輸入正確后,單擊【偵聽端口】和【連接遠(yuǎn)程機(jī)】按鈕建立聊天的連接。就通過【發(fā)送信息】按鈕進(jìn)行聊天了。圖2是通訊時運(yùn)行界面。
圖2 運(yùn)行界面
五.總結(jié):
網(wǎng)絡(luò)點對點通訊程序并不像客戶端/服務(wù)器端模型程序那樣,分成客戶端程序和服務(wù)器端程序。它是集客戶端程序和服務(wù)器端程序與一身,所以在具體的程序設(shè)計中就相對麻煩一點。上面介紹的在用visual c#實現(xiàn)網(wǎng)絡(luò)點對點通訊的示例雖然結(jié)構(gòu)并不復(fù)雜,但涉及的知識面卻比較廣泛。如示例中涉及到許多很多網(wǎng)絡(luò)功能的實現(xiàn),如:偵聽端口號、建立連接、發(fā)送數(shù)據(jù)和接收數(shù)據(jù)等,還涉及到線程的處理、資源的回收等。了解、掌握這些問題的處理方法對編寫更復(fù)雜的網(wǎng)絡(luò)應(yīng)用程序是十分必要的。