自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

淺析C#中的套接字編程

開(kāi)發(fā) 后端
本文向大家初步介紹了套接字的基本概念和實(shí)現(xiàn)套接字編程的基本原理,還通過(guò)一個(gè)很好的實(shí)例向大家展示了在C#下進(jìn)行套接字編程的實(shí)現(xiàn)方法和一些編程技巧。

C#是微軟隨著VS.net新推出的一門語(yǔ)言。它作為一門新興的語(yǔ)言,有著C++的強(qiáng)健,又有著VB等的RAD特性。而且,微軟推出C#主要的目的是為了對(duì)抗Sun公司的Java。大家都知道Java語(yǔ)言的強(qiáng)大功能,尤其在網(wǎng)絡(luò)編程方面。于是,C#在網(wǎng)絡(luò)編程方面也自然不甘落后于人。本文就向大家介紹一下C#下實(shí)現(xiàn)套接字(Sockets)編程的一些基本知識(shí),以期能使大家對(duì)此有個(gè)大致了解。首先,我向大家介紹一下套接字的概念。

套接字基本概念:

套接字是通信的基石,是支持TCP/IP協(xié)議的網(wǎng)絡(luò)通信的基本操作單元??梢詫⑻捉幼挚醋鞑煌鳈C(jī)間的進(jìn)程進(jìn)行雙向通信的端點(diǎn),它構(gòu)成了單個(gè)主機(jī)內(nèi)及整個(gè)網(wǎng)絡(luò)間的編程界面。套接字存在于通信域中,通信域是為了處理一般的線程通過(guò)套接字通信而引進(jìn)的一種抽象概念。套接字通常和同一個(gè)域中的套接字交換數(shù)據(jù)(數(shù)據(jù)交換也可能穿越域的界限,但這時(shí)一定要執(zhí)行某種解釋程序)。各種進(jìn)程使用這個(gè)相同的域互相之間用Internet協(xié)議簇來(lái)進(jìn)行通信。

套接字可以根據(jù)通信性質(zhì)分類,這種性質(zhì)對(duì)于用戶是可見(jiàn)的。應(yīng)用程序一般僅在同一類的套接字間進(jìn)行通信。不過(guò)只要底層的通信協(xié)議允許,不同類型的套接字間也照樣可以通信。套接字有兩種不同的類型:流套接字和數(shù)據(jù)報(bào)套接字。

套接字工作原理:

要通過(guò)互聯(lián)網(wǎng)進(jìn)行通信,你至少需要一對(duì)套接字,其中一個(gè)運(yùn)行于客戶機(jī)端,我們稱之為ClientSocket,另一個(gè)運(yùn)行于服務(wù)器端,我們稱之為ServerSocket。

根據(jù)連接啟動(dòng)的方式以及本地套接字要連接的目標(biāo),套接字之間的連接過(guò)程可以分為三個(gè)步驟:服務(wù)器監(jiān)聽(tīng),客戶端請(qǐng)求,連接確認(rèn)。

所謂服務(wù)器監(jiān)聽(tīng),是服務(wù)器端套接字并不定位具體的客戶端套接字,而是處于等待連接的狀態(tài),實(shí)時(shí)監(jiān)控網(wǎng)絡(luò)狀態(tài)。

所謂客戶端請(qǐng)求,是指由客戶端的套接字提出連接請(qǐng)求,要連接的目標(biāo)是服務(wù)器端的套接字。為此,客戶端的套接字必須首先描述它要連接的服務(wù)器的套接字,指出服務(wù)器端套接字的地址和端口號(hào),然后就向服務(wù)器端套接字提出連接請(qǐng)求。

所謂連接確認(rèn),是指當(dāng)服務(wù)器端套接字監(jiān)聽(tīng)到或者說(shuō)接收到客戶端套接字的連接請(qǐng)求,它就響應(yīng)客戶端套接字的請(qǐng)求,建立一個(gè)新的線程,把服務(wù)器端套接字的描述發(fā)給客戶端,一旦客戶端確認(rèn)了此描述,連接就建立好了。而服務(wù)器端套接字繼續(xù)處于監(jiān)聽(tīng)狀態(tài),繼續(xù)接收其他客戶端套接字的連接請(qǐng)求。
C#中的套接字編程實(shí)例:

通過(guò)向大家簡(jiǎn)單的介紹套接字的基本概念和實(shí)現(xiàn)套接字編程的基本原理,我想大家對(duì)套接字編程已有了初步的了解。不過(guò),上面介紹的僅僅是基本概念和原理,要真正運(yùn)用還是需要一定的工作的。對(duì)基本概念和原理的真正理解的最好方法莫過(guò)于自己動(dòng)手做一個(gè)實(shí)例,下面我就向大家介紹一個(gè)很好的用C#實(shí)現(xiàn)套接字編程的實(shí)例――聊天室程序。

本程序是基于C/S(服務(wù)器/客戶端)構(gòu)架的,程序包含一個(gè)服務(wù)器端的應(yīng)用程序和一個(gè)客戶端的應(yīng)用程序。首先,在服務(wù)器上運(yùn)行服務(wù)器端的應(yīng)用程序,該程序一運(yùn)行就開(kāi)始服務(wù)器監(jiān)聽(tīng)。然后,在客戶機(jī)上就可以打開(kāi)客戶端的應(yīng)用程序。程序打開(kāi)后可以與服務(wù)器端應(yīng)用程序進(jìn)行連接,即進(jìn)行客戶端請(qǐng)求。在連接確認(rèn)后,客戶端用戶可以和其他的客戶端用戶進(jìn)行聊天??蛻舳巳藬?shù)沒(méi)有限制,同時(shí)還支持“悄悄話”聊天模式,支持聊天記錄。所以這是一個(gè)學(xué)習(xí)套接字編程的相當(dāng)不錯(cuò)的例子。而且,程序中為了處理每個(gè)客戶端的信息還用到了多線程機(jī)制。在每個(gè)客戶端與服務(wù)器端連接成功后,它們之間就建立一個(gè)線程。這樣運(yùn)用了多線程之后,客戶端之間就不會(huì)相互影響,即使其中一個(gè)出了錯(cuò)誤也不會(huì)影響到另一個(gè)。

下面,我就向大家具體介紹該實(shí)例:

服務(wù)器端程序:

打開(kāi)VS.net,新建一個(gè)C#的模板為“Windows 應(yīng)用程序”的項(xiàng)目,不妨命名為“ChatServer”。

布置界面。只需在界面上添加一個(gè)ListBox控件即可,該控件主要用于顯示客戶端的用戶的一些信息的。圖象如下:

 

服務(wù)器端程序的代碼編寫。

對(duì)于服務(wù)器端,主要的作用是監(jiān)聽(tīng)客戶端的連接請(qǐng)求并確認(rèn)其請(qǐng)求。程序一開(kāi)始便打開(kāi)一個(gè)StartListening()線程。

 

private void StartListening() 
{
  listener = new TcpListener(listenport);
  listener.Start();
  while (true)
  {
   try
   {
    Socket s = listener.AcceptSocket();
    clientsocket = s;
    clientservice = new Thread(new ThreadStart(ServiceClient));
    clientservice.Start();
   }
   catch(Exception e)
   {
    Console.WriteLine(e.ToString() );
   }
  }
}
該線程是一直處于運(yùn)行狀態(tài)的。當(dāng)服務(wù)器端接收到一個(gè)來(lái)自客戶端的連接請(qǐng)求后,它就打開(kāi)一個(gè)ServiceClient()線程來(lái)服務(wù)客戶端。當(dāng)一個(gè)連接被建立后,每個(gè)客戶端就被賦予一個(gè)屬于它自己的套接字。同時(shí),一個(gè)Client類的對(duì)象被建立。該對(duì)象包含了客戶端的一些相關(guān)信息,該信息被保存在一個(gè)數(shù)組列表中。
 Client類如下:
using System; 
using System.Threading;
namespace ChatServer 
{
using System.Net.Sockets;
using System.Net;
/// 
/// Client 的摘要說(shuō)明。
///
public class Client
{
private Thread clthread;
private EndPoint endpoint;
private string name;
private Socket sock;
public Client(string _name, EndPoint _endpoint, Thread _thread, Socket _sock) 
{
// TODO: 在此處添加構(gòu)造函數(shù)邏輯
clthread = _thread;
endpoint = _endpoint;
name = _name;
sock = _sock;
}
public override string ToString() 
{
return endpoint.ToString()+ " : " + name;
}
public Thread CLThread 
{
get{return clthread;}
set{clthread = value;}
}
public EndPoint Host 
{
get{return endpoint;}
set{endpoint = value;}
}
public string Name 
{
get{return name;}
set{name = value;}
}
public Socket Sock 
{
get{return sock;}
set{sock = value;}
}
}
}

程序的主體部分應(yīng)是ServiceClient()函數(shù)。該函數(shù)是一個(gè)獨(dú)立的線程,其主要部分是一個(gè)while循環(huán)。在循環(huán)體內(nèi),程序處理各種客戶端命令。服務(wù)器端接收來(lái)自客戶端的以ASCII碼給出的字符串,其中包含了一個(gè)“|”形式的分隔符。字符串中“|”以前的部分就是具體的命令,包括CONN、CHAT、PRIV、GONE四種類型。CONN命令建立一個(gè)新的客戶端連接,將現(xiàn)有的用戶列表發(fā)送給新用戶并告知其他用戶有一個(gè)新用戶加入。CHAT命令將新的信息發(fā)送給所有用戶。PRIV命令將悄悄話發(fā)送給某個(gè)用戶。GONE命令從用戶列表中除去一個(gè)已離開(kāi)的用戶并告知其他的用戶某某已經(jīng)離開(kāi)了。同時(shí),GONE命令可以設(shè)置布爾型的變量keepalive為false從而結(jié)束與客戶端連接的線程。

ServiceClient()函數(shù)如下:

private void ServiceClient() 
{
Socket client = clientsocket;
bool keepalive = true;

 


while (keepalive)
{
Byte[] buffer = new Byte[1024];
client.Receive(buffer);
string clientcommand = System.Text.Encoding.ASCII.GetString(buffer);

 


string[] tokens = clientcommand.Split(new Char[]{'|'});
Console.WriteLine(clientcommand);

 


if (tokens[0] == "CONN")
{
for(int n=0; n
{
Client cl = (Client)clients[n];
SendToClient(cl, "JOIN|" + tokens[1]);
}
EndPoint ep = client.RemoteEndPoint;
Client c = new Client(tokens[1], ep, clientservice, client);
clients.Add(c);
string message = "LIST|" + GetChatterList() +"\r\n";
SendToClient(c, message);

 


lbClients.Items.Add(c);

 


}
if (tokens[0] == "CHAT")
{
for(int n=0; n
{
Client cl = (Client)clients[n];
SendToClient(cl, clientcommand);
}
}
if (tokens[0] == "PRIV")
{
string destclient = tokens[3];
for(int n=0; n
{
Client cl = (Client)clients[n];
if(cl.Name.CompareTo(tokens[3]) == 0)
SendToClient(cl, clientcommand);
if(cl.Name.CompareTo(tokens[1]) == 0)
SendToClient(cl, clientcommand);
}
}
if (tokens[0] == "GONE")
{
int remove = 0;
bool found = false;
int c = clients.Count;
for(int n=0; n
{
Client cl = (Client)clients[n];
SendToClient(cl, clientcommand);
if(cl.Name.CompareTo(tokens[1]) == 0)
{
remove = n;
found = true;
lbClients.Items.Remove(cl);
}
}
if(found)
clients.RemoveAt(remove);
client.Close();
keepalive = false;
}
}
}

這樣,服務(wù)器端程序就基本完成了。(其他略次要的代碼可以參見(jiàn)源代碼中的Form1.cs文件)程序運(yùn)行圖示如下:

 

#p#

客戶端程序:

打開(kāi)VS.net,新建一個(gè)C#的模板為“Windows 應(yīng)用程序”的項(xiàng)目,不妨命名為“ChatClient”。

布置界面。

往界面上添加一個(gè)ListBox控件(用于顯示用戶列表),一個(gè)RichTextBox控件(用于顯示聊天消息以及系統(tǒng)消息),一個(gè)TextBox控件(用于發(fā)送消息),一個(gè)CheckBox控件(確定是否為悄悄話),一個(gè)StatusBar控件以及四個(gè)Button控件(分別為“連接”、“斷開(kāi)連接”、“開(kāi)始記錄”、“發(fā)送”)。各個(gè)控件的屬性設(shè)置可以參見(jiàn)源代碼中的具體設(shè)置,這里從略。界面設(shè)計(jì)好后的圖象如下:

 

客戶端程序的代碼編寫。

當(dāng)客戶端試圖和服務(wù)器端進(jìn)行連接時(shí),一個(gè)連接必須建立而且得向服務(wù)器端進(jìn)行注冊(cè)。 EstablishConnection()函數(shù)運(yùn)用一個(gè)TcpClient來(lái)和服務(wù)器端取得連接,同時(shí)創(chuàng)建一個(gè)NetworkStream來(lái)發(fā)送消息。還有,端口號(hào)和服務(wù)器端的是保持一致的,均為5555。

EstablishConnection()函數(shù)如下:

 

private void EstablishConnection() 
{
statusBar1.Text = "正在連接到服務(wù)器";
try
{
clientsocket = new TcpClient(serveraddress,serverport);
ns = clientsocket.GetStream();
sr = new StreamReader(ns);
connected = true;
}
catch (Exception)
{
MessageBox.Show("不能連接到服務(wù)器!","錯(cuò)誤",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
statusBar1.Text = "已斷開(kāi)連接";
}
}

在和服務(wù)器端連接成功后,程序就用RegisterWithServer()函數(shù)向服務(wù)器端發(fā)送一個(gè)CONN命令。該命令先是發(fā)送該用戶的名稱,然后從服務(wù)器端獲得其他所有用戶的列表,所有用戶列表是在ListBox控件中顯示的。

該函數(shù)如下:

 

private void RegisterWithServer() 
{
try
{
string command = "CONN|" + ChatOut.Text;
Byte[] outbytes = System.Text.Encoding.ASCII.GetBytes(command.ToCharArray());
ns.Write(outbytes,0,outbytes.Length);
string serverresponse = sr.ReadLine();
serverresponse.Trim();
string[] tokens = serverresponse.Split(new Char[]{'|'});
if(tokens[0] == "LIST")
{
statusBar1.Text = "已連接";
btnDisconnect.Enabled = true;
}
for(int n=1; n
lbChatters.Items.Add(tokens[n].Trim(new char[]{'\r','\n'}));
this.Text = clientname + ":已連接到服務(wù)器";
}
catch (Exception)
{
MessageBox.Show("注冊(cè)時(shí)發(fā)生錯(cuò)誤!","錯(cuò)誤",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}

在此之后,當(dāng)然就是用戶之間的聊天了,由ReceiveChat()函數(shù)來(lái)完成。該函數(shù)是一個(gè)獨(dú)立的線程,它處理所有用戶獲得的消息和用戶發(fā)送的消息。它主要處理了CHAT、PRIV、JOIN、GONE、QUIT等命令,處理的方法和服務(wù)器端的類似。

具體函數(shù)實(shí)現(xiàn)如下:

private void ReceiveChat() 
{
bool keepalive = true;
while (keepalive)
{
try
{
Byte[] buffer = new Byte[2048];
ns.Read(buffer,0,buffer.Length);
string chatter = System.Text.Encoding.ASCII.GetString(buffer);
string[] tokens = chatter.Split(new Char[]{'|'});
if (tokens[0] == "CHAT") 
{
rtbChatIn.AppendText(tokens[1]);
if(logging)
logwriter.WriteLine(tokens[1]);
}
if (tokens[0] == "PRIV")
{
rtbChatIn.AppendText("Private from ");
rtbChatIn.AppendText(tokens[1].Trim() );
rtbChatIn.AppendText(tokens[2] + "\r\n");
if(logging)
{
logwriter.Write("Private from ");
logwriter.Write(tokens[1].Trim() );
logwriter.WriteLine(tokens[2] + "\r\n");
}
}
if (tokens[0] == "JOIN")
{
rtbChatIn.AppendText(tokens[1].Trim() );
rtbChatIn.AppendText(" has joined the Chat\r\n");
if(logging)
{
logwriter.WriteLine(tokens[1]+" has joined the Chat");
}
string newguy = tokens[1].Trim(new char[]{'\r','\n'});
lbChatters.Items.Add(newguy);
}
if (tokens[0] == "GONE")
{
rtbChatIn.AppendText(tokens[1].Trim() );
rtbChatIn.AppendText(" has left the Chat\r\n");
if(logging)
{
logwriter.WriteLine(tokens[1]+" has left the Chat");
}
lbChatters.Items.Remove(tokens[1].Trim(new char[]{'\r','\n'}));
}
if (tokens[0] == "QUIT")
{
ns.Close();
clientsocket.Close();
keepalive = false;
statusBar1.Text = "服務(wù)器端已停止";
connected= false;
btnSend.Enabled = false;
btnDisconnect.Enabled = false;
}
}
catch(Exception){}
}
}

通過(guò)以上的一些函數(shù),客戶端程序之間就可以進(jìn)行自由地聊天了,各個(gè)用戶之間還可以互相發(fā)送悄悄話。所以程序已經(jīng)實(shí)現(xiàn)了聊天室的基本功能了,不過(guò)最后各個(gè)用戶還要正常地退出,那就要用到QuitChat()函數(shù)了。

該函數(shù)的具體實(shí)現(xiàn)如下:

private void QuitChat() 
{
if(connected)
{
try
{
string command = "GONE|" + clientname;
Byte[] outbytes = System.Text.Encoding.ASCII.GetBytes(command.ToCharArray());
ns.Write(outbytes,0,outbytes.Length);
clientsocket.Close();
}
catch(Exception)
{
}
}
if(logging)
logwriter.Close();
if(receive != null && receive.IsAlive)
receive.Abort();
this.Text = "客戶端";
}

到此為止,客戶端程序的主要部分都已經(jīng)介紹完畢。還有一些按鈕控件的消息處理函數(shù)可以參見(jiàn)源代碼。同時(shí),程序中還有一個(gè)聊天記錄功能,該功能和現(xiàn)在流行的聊天軟件的記錄功能類似。不過(guò)限于篇幅,在這里就不一一介紹了,有興趣的讀者可以研究一下本文后面的源代碼。

這樣,客戶端程序就完成了。程序運(yùn)行圖示如下

 

總結(jié):

本文向大家初步介紹了套接字的基本概念和實(shí)現(xiàn)套接字編程的基本原理,還通過(guò)一個(gè)很好的實(shí)例向大家展示了在C#下進(jìn)行套接字編程的實(shí)現(xiàn)方法和一些編程技巧。從中,我們不難發(fā)現(xiàn)運(yùn)用C#進(jìn)行套接字編程乃至網(wǎng)絡(luò)編程有許多優(yōu)越之處。實(shí)例程序?qū)崿F(xiàn)的思路清晰明了而且通俗易懂,是一個(gè)相當(dāng)不錯(cuò)的例子,希望各位能好好研讀。同時(shí)還希望大家能進(jìn)一步完善該程序,使之功能更強(qiáng)大、界面更友好。

【編輯推薦】

  1. 詳解C#4.0中的OptionalAndNamedParams特性
  2. 深入解析C#編程中的方法重載
  3. 淺談C#中的接口問(wèn)題
  4. C#實(shí)用基礎(chǔ)教程
責(zé)任編輯:book05 來(lái)源: cnblog
相關(guān)推薦

2009-08-21 09:20:44

C#異步套接字

2009-08-26 09:48:48

C#異步套接字

2020-10-15 19:10:05

LinuxAPI函數(shù)

2009-01-16 09:58:07

C#編程C#內(nèi)存管理垃圾收集

2009-08-27 14:12:02

C# interfac

2009-08-13 17:44:34

C# using關(guān)鍵字

2009-09-17 16:41:12

C#組件編程

2009-08-20 17:30:56

C#異步編程模式

2009-08-20 17:47:54

C#異步編程模式

2009-08-25 15:52:27

C#工具欄

2014-12-11 09:20:30

TCP

2009-08-21 14:03:04

C#網(wǎng)絡(luò)編程

2009-08-31 17:02:28

C#接口編程

2009-04-29 09:06:18

C#設(shè)計(jì)模式Adapter

2010-01-26 14:35:11

C++關(guān)鍵字

2009-08-07 17:25:37

C# SortedLi

2009-07-31 18:39:31

C#中foreach引

2009-08-14 17:45:52

C# ArrayLis

2009-08-17 18:34:50

C# ChangeCo

2009-08-21 14:47:39

C#網(wǎng)絡(luò)編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)