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

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

C# 2.0 套接字編程實(shí)例初探

2024-07-21 02:28:47
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
  首先從原理上解釋一下采用socket接口的網(wǎng)絡(luò)通訊,這里以最常用的c/s模式作為范例,首先,服務(wù)端有一個(gè)進(jìn)程(或多個(gè)進(jìn)程)在指定的端口等待客戶來(lái)連接,服務(wù)程序等待客戶的連接信息,一旦連接上之后,就可以按設(shè)計(jì)的數(shù)據(jù)交換方法和格式進(jìn)行數(shù)據(jù)傳輸??蛻舳嗽谛枰臅r(shí)刻發(fā)出向服務(wù)端的連接請(qǐng)求。這里為了便于理解,提到了一些調(diào)用及其大致的功能。使用socket調(diào)用后,僅產(chǎn)生了一個(gè)可以使用的socket描述符,這時(shí)還不能進(jìn)行通信,還要使用其他的調(diào)用,以使得socket所指的結(jié)構(gòu)中使用的信息被填寫完。

  在使用tcp協(xié)議時(shí),一般服務(wù)端進(jìn)程先使用socket調(diào)用得到一個(gè)描述符,然后使用bind調(diào)用將一個(gè)名字與socket描述符連接起來(lái),對(duì)于internet域就是將internet地址聯(lián)編到socket。之后,服務(wù)端使用listen調(diào)用指出等待服務(wù)請(qǐng)求隊(duì)列的長(zhǎng)度。然后就可以使用accept調(diào)用等待客戶端發(fā)起連接,一般是阻塞等待連接,一旦有客戶端發(fā)出連接,accept返回客戶的地址信息,并返回一個(gè)新的socket描述符,該描述符與原先的socket有相同的特性,這時(shí)服務(wù)端就可以使用這個(gè)新的socket進(jìn)行讀寫操作了。一般服務(wù)端可能在accept返回后創(chuàng)建一個(gè)新的進(jìn)程進(jìn)行與客戶的通信,父進(jìn)程則再到accept調(diào)用處等待另一個(gè)連接??蛻舳诉M(jìn)程一般先使用socket調(diào)用得到一個(gè)socket描述符,然后使用connect向指定的服務(wù)器上的指定端口發(fā)起連接,一旦連接成功返回,就說(shuō)明已經(jīng)建立了與服務(wù)器的連接,這時(shí)就可以通過(guò)socket描述符進(jìn)行讀寫操作了。

  .netframework為socket通訊提供了system.net.socket命名空間,在這個(gè)命名空間里面有以下幾個(gè)常用的重要類分別是:

  ·socket類 這個(gè)低層的類用于管理連接,webrequest,tcpclient和udpclient在內(nèi)部使用這個(gè)類。

  ·networkstream類 這個(gè)類是從stream派生出來(lái)的,它表示來(lái)自網(wǎng)絡(luò)的數(shù)據(jù)流

  ·tcpclient類 允許創(chuàng)建和使用tcp連接

  ·tcplistener類 允許監(jiān)聽傳入的tcp連接請(qǐng)求

  ·udpclient類 用于udp客戶創(chuàng)建連接(udp是另外一種tcp協(xié)議,但沒(méi)有得到廣泛的使用,主要用于本地網(wǎng)絡(luò))

  下面我們來(lái)看一個(gè)基于socket的雙機(jī)通信代碼的c#版本

  首先創(chuàng)建socket對(duì)象的實(shí)例,這可以通過(guò)socket類的構(gòu)造方法來(lái)實(shí)現(xiàn):

public socket(addressfamily addressfamily,sockettype sockettype,protocoltype protocoltype);

  其中,addressfamily 參數(shù)指定 socket 使用的尋址方案,sockettype 參數(shù)指定 socket 的類型,protocoltype 參數(shù)指定 socket 使用的協(xié)議。

  下面的示例語(yǔ)句創(chuàng)建一個(gè) socket,它可用于在基于 tcp/ip 的網(wǎng)絡(luò)(如 internet)上通訊。

socket temp = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp);

  若要使用 udp 而不是 tcp,需要更改協(xié)議類型,如下面的示例所示:

socket temp = new socket(addressfamily.internetwork, sockettype.dgram, protocoltype.udp);


  一旦創(chuàng)建 socket,在客戶端,你將可以通過(guò)connect方法連接到指定的服務(wù)器(你可以在connect方法前bind端口,就是以指定的端口發(fā)起連接,如果不事先bind端口號(hào)的話,系統(tǒng)會(huì)默認(rèn)在1024到5000隨機(jī)綁定一個(gè)端口號(hào)),并通過(guò)send方法向遠(yuǎn)程服務(wù)器發(fā)送數(shù)據(jù),而后可以通過(guò)receive從服務(wù)端接收數(shù)據(jù);而在服務(wù)器端,你需要使用bind方法綁定所指定的接口使socket與一個(gè)本地終結(jié)點(diǎn)相聯(lián),并通過(guò)listen方法偵聽該接口上的請(qǐng)求,當(dāng)偵聽到用戶端的連接時(shí),調(diào)用accept完成連接的操作,創(chuàng)建新的socket以處理傳入的連接請(qǐng)求。使用完 socket 后,使用 close 方法關(guān)閉 socket。

  可以看出,以上許多方法包含endpoint類型的參數(shù),在internet中,tcp/ip 使用一個(gè)網(wǎng)絡(luò)地址和一個(gè)服務(wù)端口號(hào)來(lái)唯一標(biāo)識(shí)設(shè)備。網(wǎng)絡(luò)地址標(biāo)識(shí)網(wǎng)絡(luò)上的特定設(shè)備;端口號(hào)標(biāo)識(shí)要連接到的該設(shè)備上的特定服務(wù)。網(wǎng)絡(luò)地址和服務(wù)端口的組合稱為終結(jié)點(diǎn),在 .net 框架中正是由 endpoint 類表示這個(gè)終結(jié)點(diǎn),它提供表示網(wǎng)絡(luò)資源或服務(wù)的抽象,用以標(biāo)志網(wǎng)絡(luò)地址等信息。.net同時(shí)也為每個(gè)受支持的地址族定義了 endpoint 的子代;對(duì)于 ip 地址族,該類為 ipendpoint。ipendpoint 類包含應(yīng)用程序連接到主機(jī)上的服務(wù)所需的主機(jī)和端口信息,通過(guò)組合服務(wù)的主機(jī)ip地址和端口號(hào),ipendpoint 類形成到服務(wù)的連接點(diǎn)。

  用到ipendpoint類的時(shí)候就不可避免地涉及到計(jì)算機(jī)ip地址,system.net命名空間中有兩種類可以得到ip地址實(shí)例:

  ·ipaddress類:ipaddress 類包含計(jì)算機(jī)在 ip 網(wǎng)絡(luò)上的地址。其parse方法可將 ip 地址字符串轉(zhuǎn)換為 ipaddress 實(shí)例。下面的語(yǔ)句創(chuàng)建一個(gè) ipaddress 實(shí)例:

ipaddress myip = ipaddress.parse("192.168.0.1");

  需要知道的是:socket 類支持兩種基本模式:同步和異步。其區(qū)別在于:在同步模式中,按塊傳輸,對(duì)執(zhí)行網(wǎng)絡(luò)操作的函數(shù)(如 send 和 receive)的調(diào)用一直等到所有內(nèi)容傳送操作完成后才將控制返回給調(diào)用程序。在異步模式中,是按位傳輸,需要指定發(fā)送的開始和結(jié)束。同步模式是最常用的模式,我們這里的例子也是使用同步模式。

  下面看一個(gè)完整的例子,client向server發(fā)送一段測(cè)試字符串,server接收并顯示出來(lái),給予client成功響應(yīng)。

//client端
using system;
using system.text;
using system.io;
using system.net;
using system.net.sockets;
namespace socketsample
{
 class class1
 {
  static void main()
  {
   try
   {
    int port = 2000;
    string host = "127.0.0.1";
    ipaddress ip = ipaddress.parse(host);
    ipendpoint ipe = new ipendpoint(ip, port);//把ip和端口轉(zhuǎn)化為ipendpoint實(shí)例
    socket c = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp);//創(chuàng)建一個(gè)socket
    console.writeline("conneting...");
    c.connect(ipe);//連接到服務(wù)器
    string sendstr = "hello!this is a socket test";
    byte[] bs = encoding.ascii.getbytes(sendstr);
    console.writeline("send message");
    c.send(bs, bs.length, 0);//發(fā)送測(cè)試信息
    string recvstr = "";
    byte[] recvbytes = new byte[1024];
    int bytes;
    bytes = c.receive(recvbytes, recvbytes.length, 0);//從服務(wù)器端接受返回信息
    recvstr += encoding.ascii.getstring(recvbytes, 0, bytes);
    console.writeline("client get message:{0}", recvstr);//顯示服務(wù)器返回信息
    c.close();
   }
   catch (argumentnullexception e)
   {
    console.writeline("argumentnullexception: {0}", e);
   }
   catch (socketexception e)
   {
    console.writeline("socketexception: {0}", e);
   }
   console.writeline("press enter to exit");
   console.readline();
  }
 }
}
//server端
using system;
using system.text;
using system.io;
using system.net;
using system.net.sockets;
namespace project1
{
 class class2
 {
  static void main()
  {
   try
   {
    int port = 2000;
    string host = "127.0.0.1";
    ipaddress ip = ipaddress.parse(host);
    ipendpoint ipe = new ipendpoint(ip, port);
    socket s = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp);//創(chuàng)建一個(gè)socket類
    s.bind(ipe);//綁定2000端口
    s.listen(0);//開始監(jiān)聽
    console.writeline("wait for connect");
    socket temp = s.accept();//為新建連接創(chuàng)建新的socket。
    console.writeline("get a connect");
    string recvstr = "";
    byte[] recvbytes = new byte[1024];
    int bytes;
    bytes = temp.receive(recvbytes, recvbytes.length, 0);//從客戶端接受信息
    recvstr += encoding.ascii.getstring(recvbytes, 0, bytes);
    console.writeline("server get message:{0}",recvstr);//把客戶端傳來(lái)的信息顯示出來(lái)
    string sendstr = "ok!client send message sucessful!";
    byte[] bs = encoding.ascii.getbytes(sendstr);
    temp.send(bs, bs.length, 0);//返回客戶端成功信息
    temp.close();
    s.close();
   }
   catch (argumentnullexception e)
   {
    console.writeline("argumentnullexception: {0}", e);
   }
   catch (socketexception e)
   {
    console.writeline("socketexception: {0}", e);
   }
   console.writeline("press enter to exit");
   console.readline();
  }
 }
}

  上面的例子是用的socket類,system.net.socket命名空間還提供了兩個(gè)抽象高級(jí)類tcpclient和udpclient和用于通訊流處理的networkstream,讓我們看下例子

  客戶端

tcpclient tcpclient=new tcpclient(主機(jī)ip,端口號(hào));
networkstream ns=tcp.client.getstream();

  服務(wù)端

tcplistener tcplistener=new tcplistener(監(jiān)聽端口);
tcplistener.start();
tcpclient tcpclient=tcplistener.accepttcpclient();
networkstream ns=tcpclient.getstream();

  服務(wù)端用tcplistener監(jiān)聽,然后把連接的對(duì)象實(shí)例化為一個(gè)tcpclient,調(diào)用tcpclient.getstream()方法,返回網(wǎng)絡(luò)流實(shí)例化為一個(gè)networlstream流,下面就是用流的方法進(jìn)行send,receive

  如果是udpclient的話,就直接udpclient實(shí)例化,然后調(diào)用udpclient的send和receive方法,需要注意的事,udpclient沒(méi)有返回網(wǎng)絡(luò)流的方法,就是說(shuō)沒(méi)有g(shù)etstream方法,所以無(wú)法流化,而且使用udp通信的時(shí)候,不要服務(wù)器監(jiān)聽。

  現(xiàn)在我們大致了解了.net socket通信的流程,下面我們來(lái)作一個(gè)稍微復(fù)雜點(diǎn)的程序,一個(gè)廣播式的c/s聊天程序。

  客戶端設(shè)計(jì)需要一個(gè)1個(gè)listbox,用于顯示聊天內(nèi)容,一個(gè)textbox輸入你要說(shuō)的話,一個(gè)button發(fā)送留言,一個(gè)button建立連接。

  點(diǎn)擊建立連接的button后出來(lái)一個(gè)對(duì)話框,提示輸入連接服務(wù)器的ip,端口,和你的昵稱,啟動(dòng)一個(gè)接受線程,負(fù)責(zé)接受從服務(wù)器傳來(lái)的信息并顯示在listbox上面。

  服務(wù)器端2個(gè)button,一個(gè)啟動(dòng)服務(wù),一個(gè)t掉已建立連接的客戶端,一個(gè)listbox顯示連接上的客戶端的ip和端口。

  比較重要的地方是字符串編碼的問(wèn)題,需要先把需要傳送的字符串按照utf8編碼,然后接受的時(shí)候再還原成為gb2312,不然中文顯示會(huì)是亂碼。

  還有一個(gè)就是接收線程,我這里簡(jiǎn)單寫成一個(gè)while(ture)循環(huán),不斷判斷是否有信息流入,有就接收,并顯示在listbox上,這里有問(wèn)題,在.net2.0里面,交錯(cuò)線程修改窗體空間屬性的時(shí)候會(huì)引發(fā)一個(gè)異常,不可以直接修改,需要定義一個(gè)委托來(lái)修改。

  當(dāng)客戶端需要斷開連接的時(shí)候,比如點(diǎn)擊窗體右上角的xx,就需要定義一個(gè)this.formclosing += new system.windows.forms.formclosingeventhandler(this.closing);(.net2.0是formclosing系統(tǒng)事件),在closing()函數(shù)里面,發(fā)送close字符給服務(wù)端,服務(wù)器判斷循環(huán)判斷所有的連接上的客戶端傳來(lái)的信息,如果是以close開頭,斷開與其的連接??吹竭@里,讀者就會(huì)問(wèn)了,如果我在聊天窗口輸入close是不是也斷開連接呢?不是的,在聊天窗口輸入的信息傳給服務(wù)器的時(shí)候開頭都要加上ip信息和昵稱,所以不會(huì)沖突。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 忻城县| 莱西市| 大渡口区| 定结县| 新巴尔虎右旗| 安庆市| 吴川市| 莲花县| 浠水县| 马尔康县| 左云县| 历史| 扎兰屯市| 临高县| 湄潭县| 德安县| 佳木斯市| 灵丘县| 平邑县| 密山市| 纳雍县| 新郑市| 南岸区| 诏安县| 凤城市| 安徽省| 宜章县| 华阴市| 广德县| 凤阳县| 达拉特旗| 平泉县| 精河县| 阿合奇县| 哈密市| 永靖县| 常熟市| 钟祥市| 京山县| 茂名市| 安阳市|