天天看點

C# 如何實作簡單的Socket通信(附示例)

上周由于有個項目需要用到網絡通信這塊,然後就花了點時間研究了一下,本來想上周就寫出來的,但是突然要忙,是以等到現在。

話說對于網絡通信,以前寫C++的時候,天天面對着線程和Socket,是以換成C#也就沒那麼怕了,雖然C++下也沒有掌握的多好,但畢竟隻是一個小Demo,隻作為了解一下過程。

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

C# 如何實作簡單的Socket通信(附示例)

簡單連通版:

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

<code>//Server端</code>

<code>//建立一個socket對象,三個參數分别代表:</code>

<code>//AddressFamily.InterNetwork: IPV4協定,SocketType.Stream:流類型,ProtocolType.Tcp:TCP方式連接配接</code>

<code>Socket ss = </code><code>new</code> <code>Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);</code>

<code>//擷取目前機器的IP位址,在C#中用IPAddress這個類去存放IP位址</code>

<code>IPAddress ipa = Dns.GetHostByName(Dns.GetHostName()).AddressList[0];</code>

<code>//IPEndPoint是用來把IP位址和端口号內建在一起的一個類型,在C#中叫做“網絡端點”</code>

<code>//我這裡假設伺服器開的端口号是11000,這個是自己随意設定的。不是唯一</code>

<code>IPEndPoint iep = </code><code>new</code> <code>IPEndPoint(ipa, 11000);</code>

<code>//之前建立的Socket與我們本機的IP和所設的端口号綁定</code>

<code>ss.Bind(iep);</code>

<code>//進行監聽,也就是說我們開始偵聽網絡上對本機IP和11000這個端口進行連接配接的資訊了</code>

<code>//參數清單官方定義為:挂起連接配接隊列的最大長度。這個稍後單獨說。</code>

<code>ss.Listen(50);</code>

<code>byte</code><code>[] bMessage = </code><code>new</code> <code>byte</code><code>[1024*10];</code>

<code>string</code> <code>sMsg = </code><code>"Can I help you ?"</code><code>;</code>

<code>//當ss這個用于監聽的socket收到一個連接配接請求之後,會接受對方請求,并建立一個新的連接配接</code>

<code>//而新的這個s就是接下來用于真正通信的socket了。</code>

<code>Socket s = ss.Accept();</code>

<code>while</code> <code>(</code><code>true</code><code>)</code>

<code>{</code>

<code>      </code><code>try</code>

<code>      </code><code>{</code>

<code>         </code><code>//bMessage = System.Text.Encoding.BigEndianUnicode.GetBytes(sMsg.ToCharArray());</code>

<code>         </code><code>//s.Send(bMessage);</code>

<code>         </code><code>//bMessage = null;</code>

<code>         </code><code>//顧名思義啦,這是一個接收資訊的方法,把通過網絡傳過來的流存入byte數組中去。</code>

<code>         </code><code>//之是以把它寫在這裡是因為我的設計之初是,當socket連通成功之後,Client端會首先給Server端發一個資訊。</code>

<code>         </code><code>s.Receive(bMessage);</code>

<code>         </code><code>//用于把byte轉成string的一個方法,注意我用的是Unicode,通常我們還可以用UTF8,ASCII編解碼</code>

<code>         </code><code>//之前最坑的是當時不知道在C#下如何轉,在網上看到了一個用BigEndianUnicode編碼的,</code>

<code>         </code><code>//當時沒有想太多就使了,結果就是解出來各種亂碼的樣子,這塊跟大夥提一下</code>

<code>         </code><code>sMsg = System.Text.Encoding.Unicode.GetString(bMessage);</code>

<code>                    </code><code>Console.WriteLine(</code><code>"Client("</code> <code>+ DateTime.Now.ToShortTimeString() + </code><code>"):"</code> <code>+ sMsg);</code>

<code>         </code><code>//接下來就是輸入一個字元串,并把其轉成byte數組,然後Send出去。</code>

<code>         </code><code>bMessage = System.Text.Encoding.Unicode.GetBytes(Console.ReadLine().ToCharArray());</code>

<code>         </code><code>s.Send(bMessage);</code>

<code>       </code><code>}</code>

<code>       </code><code>catch</code> <code>(System.Exception ex)</code>

<code>       </code><code>{</code>

<code>                                                        </code> 

<code>}</code>

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

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

<code>//Client端</code>

<code>//同伺服器端一樣,需要建立一個socket</code>

<code>//這塊主要就是把你想連接配接的目标主機IP位址進行一下解析,并存入IPAddress類型的一個執行個體當中</code>

<code>IPAddress myIP = IPAddress.Parse(</code><code>"192.168.xxx.xxx"</code><code>);</code>

<code>//這也同伺服器是一個意思,把IP和端口号內建在一個網絡端點中</code>

<code>IPEndPoint ipe = </code><code>new</code> <code>IPEndPoint(myIP, 11000);</code>

<code>byte</code><code>[] bMessage = </code><code>null</code><code>;</code>

<code>string</code> <code>sMsg = </code><code>"hello world"</code><code>;</code>

<code>try</code>

<code>    </code><code>//這是用戶端的一個方法,表示連接配接的對象就是參數的網絡端點中的IP位址和端口号</code>

<code>    </code><code>//但是注意這裡不需要傳回一個新的socket作為通信socket</code>

<code>    </code><code>//而是進行連接配接的這個ss就是将來一直維持此次連接配接的socket,直到該通道關閉或斷開</code>

<code>    </code><code>ss.Connect(ipe);</code>

<code>    </code><code>bMessage = System.Text.Encoding.Unicode.GetBytes(sMsg.ToCharArray());</code>

<code>    </code><code>//send方法的傳回值表示已發送到socket的位元組數,就像我在server端說的那樣</code>

<code>    </code><code>//這個Demo的設計思路就是連通後,用戶端先向伺服器端發送一個資訊</code>

<code>    </code><code>int</code> <code>count = ss.Send(bMessage);</code>

<code>    </code><code>if</code> <code>(count &gt; 0)</code>

<code>    </code><code>{</code>

<code>       </code><code>while</code> <code>(</code><code>true</code><code>)</code>

<code>           </code><code>//bMessage = null;</code>

<code>           </code><code>ss.Receive(bMessage);</code>

<code>           </code><code>sMsg = System.Text.Encoding.Unicode.GetString(bMessage);</code>

<code>           </code><code>Console.WriteLine(</code><code>"Server("</code> <code>+ DateTime.Now.ToShortTimeString() + </code><code>"):"</code> <code>+ sMsg);</code>

<code>           </code><code>bMessage = System.Text.Encoding.Unicode.GetBytes(Console.ReadLine().ToCharArray());</code>

<code>           </code><code>ss.Send(bMessage);</code>

<code>        </code><code>}</code>

<code>     </code><code>}</code>

<code>catch</code> <code>(ArgumentNullException ae)</code>

<code>                                       </code> 

<code>catch</code> <code>(SocketException se)</code>

<code>     </code><code>Console.WriteLine(</code><code>"SocketException : {0}"</code><code>, se.ToString());</code>

<code>catch</code> <code>(Exception e)</code>

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

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

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

不過我給大家說一下大概思路,利用多線程就是想能在Console環境下随時進行消息的發送和接收,而不是收一條才能發一條這樣子。另外,我也拓展了一下,就是可以多台用戶端對伺服器端進行請求連接配接,這樣就變成一對多的關系了,主要實作就是在伺服器端開一個專門用于監聽連接配接請求的線程,然後接受請求後對每個socket單獨再開一個線程這樣子。

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