C# 如何實現簡單的Socket通訊(附示例)

weixin_33766168發表於2017-11-15

上週由於有個專案需要用到網路通訊這塊,然後就花了點時間研究了一下,本來想上週就寫出來的,但是突然要忙,所以等到現在。

話說對於網路通訊,以前寫C++的時候,天天面對著執行緒和Socket,所以換成C#也就沒那麼怕了,雖然C++下也沒有掌握的多好,但畢竟只是一個小Demo,只作為了解一下過程。

自己寫了一個服務端和一個客戶端,剛開始比較簡單,只是能達到連通,收發資訊的目的,但是很粗糙。而後稍加改進呢~加上了多執行緒,所以能感覺更科學一些,不過自己真的很菜,程式碼寫的不是很好看,下面分兩個版本給大家表出來,希望幫助剛接觸C#網路通訊的朋友。

對了,還要說一點的就是,我這個小Demo是用控制檯程式寫的j_0016.gifj_0016.gifj_0016.gif為什麼選擇控制檯?我也不清楚,可能是因為有個main函式的原因,在一個檔案裡就能搞定吧~有點懶。

簡單連通版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//Server端
//建立一個socket物件,三個引數分別代表:
//AddressFamily.InterNetwork: IPV4協議,SocketType.Stream:流型別,ProtocolType.Tcp:TCP方式連線
Socket ss = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//獲取當前機器的IP地址,在C#中用IPAddress這個類去存放IP地址
IPAddress ipa = Dns.GetHostByName(Dns.GetHostName()).AddressList[0];
//IPEndPoint是用來把IP地址和埠號整合在一起的一個型別,在C#中叫做“網路端點”
//我這裡假設伺服器開的埠號是11000,這個是自己隨意設定的。不是唯一
IPEndPoint iep = new IPEndPoint(ipa, 11000);
//之前建立的Socket與我們本機的IP和所設的埠號繫結
ss.Bind(iep);
//進行監聽,也就是說我們開始偵聽網路上對本機IP和11000這個埠進行連線的資訊了
//引數列表官方定義為:掛起連線佇列的最大長度。這個稍後單獨說。
ss.Listen(50);
byte[] bMessage = new byte[1024*10];
string sMsg = "Can I help you ?";
//當ss這個用於監聽的socket收到一個連線請求之後,會接受對方請求,並建立一個新的連線
//而新的這個s就是接下來用於真正通訊的socket了。
Socket s = ss.Accept();
while (true)
{
      try
      {
         //bMessage = System.Text.Encoding.BigEndianUnicode.GetBytes(sMsg.ToCharArray());
         //s.Send(bMessage);
         //bMessage = null;
         //顧名思義啦,這是一個接收資訊的方法,把通過網路傳過來的流存入byte陣列中去。
         //之所以把它寫在這裡是因為我的設計之初是,當socket連通成功之後,Client端會首先給Server端發一個資訊。
         s.Receive(bMessage);
         //用於把byte轉成string的一個方法,注意我用的是Unicode,通常我們還可以用UTF8,ASCII編解碼
         //之前最坑的是當時不知道在C#下如何轉,在網上看到了一個用BigEndianUnicode編碼的,
         //當時沒有想太多就使了,結果就是解出來各種亂碼的樣子,這塊跟大夥提一下
         sMsg = System.Text.Encoding.Unicode.GetString(bMessage);
                    Console.WriteLine("Client(" + DateTime.Now.ToShortTimeString() + "):" + sMsg);
         //接下來就是輸入一個字串,並把其轉成byte陣列,然後Send出去。
         bMessage = System.Text.Encoding.Unicode.GetBytes(Console.ReadLine().ToCharArray());
         s.Send(bMessage);
       }
       catch (System.Exception ex)
       {
                                                         
       }
}

這塊說一下.Listen(backlog)方法裡的引數到底是表示什麼東東,其實之前我也不太明白,也是寫博的時候現去查的,就看到了一個比較靠譜的說法。

大概意思就是:當我們把IP和埠號暴露在網路當中,就是讓其他IP去進行連線,而我們不能保證某一段時刻只有一個IP來進行連線請求,也就是說在同一時間段範圍內,可能有多個連線請求,這個時候我們就需要用一個佇列來維護請求連線的先後順序。在說的細緻一點呢,就是連線的過程不會是一瞬時間完成的,我們都知道TCP協議是要經過三次握手的,而在這個過程中,就有可能別的IP來進行連線請求,所以這個backlog就是表示這個佇列的長度的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//Client端
//同伺服器端一樣,需要建立一個socket
Socket ss = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//這塊主要就是把你想連線的目標主機IP地址進行一下解析,並存入IPAddress型別的一個例項當中
IPAddress myIP = IPAddress.Parse("192.168.xxx.xxx");
//這也同伺服器是一個意思,把IP和埠號整合在一個網路端點中
IPEndPoint ipe = new IPEndPoint(myIP, 11000);
byte[] bMessage = null;
string sMsg = "hello world";
try
{
    //這是客戶端的一個方法,表示連線的物件就是引數的網路端點中的IP地址和埠號
    //但是注意這裡不需要返回一個新的socket作為通訊socket
    //而是進行連線的這個ss就是將來一直維持此次連線的socket,直到該通道關閉或斷開
    ss.Connect(ipe);
    bMessage = System.Text.Encoding.Unicode.GetBytes(sMsg.ToCharArray());
    //send方法的返回值表示已傳送到socket的位元組數,就像我在server端說的那樣
    //這個Demo的設計思路就是連通後,客戶端先向伺服器端傳送一個資訊
    int count = ss.Send(bMessage);
    if (count > 0)
    {
       while (true)
       {
           //bMessage = null;
           ss.Receive(bMessage);
           sMsg = System.Text.Encoding.Unicode.GetString(bMessage);
           Console.WriteLine("Server(" + DateTime.Now.ToShortTimeString() + "):" + sMsg);
           bMessage = System.Text.Encoding.Unicode.GetBytes(Console.ReadLine().ToCharArray());
           ss.Send(bMessage);
        }
     }
}
catch (ArgumentNullException ae)
{
                                        
}
catch (SocketException se)
{
     Console.WriteLine("SocketException : {0}", se.ToString());
}
catch (Exception e)
{
}

這樣一個簡單的C#下的socket通訊就寫完了,說實話,我也只是懂個大概,現在很少會去深究什麼了,只是到了需要的程度,才會去做一個研究,另外大家注意我這裡用了try...catch,大家留意一下,我記得可能是必加的。

下面表一下另外一個稍稍改進版的,客戶端沒有變,主要是改服務端的程式碼。

哎呀,剛剛看了一下,感覺寫的好垃圾,都不好意思表上來了。其實我當時寫這個增進版是想了解一下C#下的多執行緒來著(時間有點久我給忘了,以為能歸到socket通訊這塊呢),正好socket通訊可以利用到多執行緒,就省得自己再去想象一個環境了,所以這個增進版主要是想說關於多執行緒的一些東西,這個我之後單獨寫一篇來講吧,就不在這篇裡表了。

不過我給大家說一下大概思路,利用多執行緒就是想能在Console環境下隨時進行訊息的傳送和接收,而不是收一條才能發一條這樣子。另外,我也擴充了一下,就是可以多臺客戶端對伺服器端進行請求連線,這樣就變成一對多的關係了,主要實現就是在伺服器端開一個專門用於監聽連線請求的執行緒,然後接受請求後對每個socket單獨再開一個執行緒這樣子。

具體的實現,我會在下一篇《C# 多執行緒如何傳遞兩個以上引數的實現方法》中使用這個擴充套件版的socket通訊作為例子來表給大家。










本文轉自 我不會抽菸 51CTO部落格,原文連結:http://blog.51cto.com/zhouhongyu1989/1410118,如需轉載請自行聯絡原作者

相關文章