C#學(xué)習(xí)教程之Socket的簡單使用
前言
在開始介紹socket前先補(bǔ)充補(bǔ)充基礎(chǔ)知識,在此基礎(chǔ)上理解網(wǎng)絡(luò)通信才會順理成章,當(dāng)然有基礎(chǔ)的可以跳過去了。都是廢話,進(jìn)入正題。
TCP/IP:Transmission Control Protocol/Internet Protocol,傳輸控制協(xié)議/因特網(wǎng)互聯(lián)協(xié)議,又名網(wǎng)絡(luò)通訊協(xié)議。簡單來說:TCP控制傳輸數(shù)據(jù),負(fù)責(zé)發(fā)現(xiàn)傳輸?shù)膯栴},一旦有問題就發(fā)出信號,要求重新傳輸,直到所有數(shù)據(jù)安全正確地傳輸?shù)侥康牡兀鳬P是負(fù)責(zé)給因特網(wǎng)中的每一臺電腦定義一個地址,以便傳輸。從協(xié)議分層模型方面來講:TCP/IP由:網(wǎng)絡(luò)接口層(鏈路層)、網(wǎng)絡(luò)層、傳輸層、應(yīng)用層。它和OSI的七層結(jié)構(gòu)以及對于協(xié)議族不同,下圖簡單表示:
注:第一張圖:TCP/IP的四層結(jié)構(gòu)對應(yīng)OSI七層結(jié)構(gòu)。
第三張圖:TCP/IP協(xié)議族在OSI七層中的位置及對應(yīng)的功能。
第二張圖:TCP/IP協(xié)議模塊關(guān)系圖。
現(xiàn)階段socket通信使用TCP、UDP協(xié)議,相對應(yīng)UDP來說,TCP則是比較安全穩(wěn)定的協(xié)議了。
Socket是一種通信TCP/IP的通訊接口,也就是HTTP的抽象層,就是Socket在Http之上,Socket也就是發(fā)動機(jī)。實際上,傳輸層的TCP是基于網(wǎng)絡(luò)層的IP協(xié)議的,而應(yīng)用層的HTTP協(xié)議又是基于傳輸層的TCP協(xié)議的,而Socket本身不算是協(xié)議,就像上面所說,它只是提供了一個針對TCP或者UDP編程的接口。
在C#中可以非常方便的使用Socket進(jìn)行數(shù)據(jù)傳輸。
Socket對象是C#使用它的重要對象在Socket的構(gòu)造函數(shù)中,我們可以設(shè)置它的地址,Socket的類,支持的協(xié)議等等,其定義如下:
public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType);
我們想要使用Socket,那么就必須創(chuàng)建Socket的對象,創(chuàng)建這個對象,就必須需要IPEndPoint對象來綁定到套接詞字中,有如下定義
// 創(chuàng)建負(fù)責(zé)監(jiān)聽的套接字,注意其中的參數(shù); socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 獲得文本框中的IP對象; IPAddress address = IPAddress.Parse(textBox1.Text.Trim()); // 創(chuàng)建包含ip和端口號的網(wǎng)絡(luò)節(jié)點對象; IPEndPoint endPoint = new IPEndPoint(address, int.Parse(textBox2.Text.Trim()));
然后再通過Socket的Bind來進(jìn)行綁定。
socketWatch.Bind(endPoint);
因為我們時刻會被內(nèi)網(wǎng)中的其他ip和端口進(jìn)行連接,那么我們就需要創(chuàng)建線程來進(jìn)行觀察,有如下定義
// 設(shè)置監(jiān)聽隊列的長度; socketWatch.Listen(10); // 創(chuàng)建負(fù)責(zé)監(jiān)聽的線程; threadWatch = new Thread(WatchConnecting); threadWatch.IsBackground = true; threadWatch.Start(); ShowMsg("服務(wù)器啟動監(jiān)聽成功!");
其檢測方法如下,其中就是不斷的去檢測客戶端的連接請求,通過Accept()方法可以獲取一個套接字,然后通過Socket對象的RemoteEndPoint()可以獲取一個IP。
void WatchConnecting() { while (true) // 持續(xù)不斷的監(jiān)聽客戶端的連接請求; { // 開始監(jiān)聽客戶端連接請求,Accept方法會阻斷當(dāng)前的線程; Socket sokConnection = socketWatch.Accept(); // 一旦監(jiān)聽到一個客戶端的請求,就返回一個與該客戶端通信的 套接字; // 想列表控件中添加客戶端的IP信息; Online.Items.Add(sokConnection.RemoteEndPoint.ToString()); // 將與客戶端連接的 套接字 對象添加到集合中; dict.Add(sokConnection.RemoteEndPoint.ToString(), sokConnection); ShowMsg("客戶端連接成功!"); Thread thr = new Thread(RecMsg); thr.IsBackground = true; thr.Start(sokConnection); dictThread.Add(sokConnection.RemoteEndPoint.ToString(), thr); // 將新建的線程 添加 到線程的集合中去。 } }
最后我們開啟一個線程去執(zhí)行RecMsg方法,然后我們不停的去監(jiān)聽客戶端給我們的數(shù)據(jù)發(fā)送。
void RecMsg(object sokConnectionparn) { Socket sokClient = sokConnectionparn as Socket; while (true) { // 定義一個2M的緩存區(qū); byte[] arrMsgRec = new byte[1024 * 1024 * 2]; // 將接受到的數(shù)據(jù)存入到輸入 arrMsgRec中; int length = -1; try { length = sokClient.Receive(arrMsgRec); // 接收數(shù)據(jù),并返回數(shù)據(jù)的長度; } catch (SocketException se) { ShowMsg("異常:" + se.Message); // 從 通信套接字 集合中刪除被中斷連接的通信套接字; dict.Remove(sokClient.RemoteEndPoint.ToString()); // 從通信線程集合中刪除被中斷連接的通信線程對象; dictThread.Remove(sokClient.RemoteEndPoint.ToString()); // 從列表中移除被中斷的連接IP Online.Items.Remove(sokClient.RemoteEndPoint.ToString()); break; } catch (Exception e) { ShowMsg("異常:" + e.Message); // 從 通信套接字 集合中刪除被中斷連接的通信套接字; dict.Remove(sokClient.RemoteEndPoint.ToString()); // 從通信線程集合中刪除被中斷連接的通信線程對象; dictThread.Remove(sokClient.RemoteEndPoint.ToString()); // 從列表中移除被中斷的連接IP Online.Items.Remove(sokClient.RemoteEndPoint.ToString()); break; } if (arrMsgRec[0] == 0) // 表示接收到的是數(shù)據(jù); { string strMsg = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length - 1);// 將接受到的字節(jié)數(shù)據(jù)轉(zhuǎn)化成字符串; ShowMsg(strMsg); } if (arrMsgRec[0] == 1) // 表示接收到的是文件; { SaveFileDialog sfd = new SaveFileDialog(); if (sfd.ShowDialog(this) == System.Windows.Forms.DialogResult.OK) {// 在上邊的 sfd.ShowDialog() 的括號里邊一定要加上 this 否則就不會彈出 另存為 的對話框,而彈出的是本類的其他窗口,,這個一定要注意?。?!【解釋:加了this的sfd.ShowDialog(this),“另存為”窗口的指針才能被SaveFileDialog的對象調(diào)用,若不加thisSaveFileDialog 的對象調(diào)用的是本類的其他窗口了,當(dāng)然不彈出“另存為”窗口?!? string fileSavePath = sfd.FileName;// 獲得文件保存的路徑; // 創(chuàng)建文件流,然后根據(jù)路徑創(chuàng)建文件; using (FileStream fs = new FileStream(fileSavePath, FileMode.Create)) { fs.Write(arrMsgRec, 1, length - 1); ShowMsg("文件保存成功:" + fileSavePath); } } } } }
我們在方法中獲得了一個Object類型的對象,將這個Object對象轉(zhuǎn)換成了Socket,然后我們通過Socket的方法Receive()方法接收返回的數(shù)據(jù),其中里面有它的屬性,可以獲取ip還有一些數(shù)據(jù)等等。服務(wù)器向客戶端發(fā)送數(shù)據(jù)也是非常簡單。通過Send方法就可以了,如以下定義:
string strMsg = "服務(wù)器" + "\r\n" + " -->" + richTextBox1.Text.Trim() + "\r\n"; byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); // 將要發(fā)送的字符串轉(zhuǎn)換成Utf-8字節(jié)數(shù)組; byte[] arrSendMsg = new byte[arrMsg.Length + 1]; arrSendMsg[0] = 0; // 表示發(fā)送的是消息數(shù)據(jù) Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length); string strKey = ""; strKey = Online.Text.Trim(); if (string.IsNullOrEmpty(strKey)) // 判斷是不是選擇了發(fā)送的對象; { MessageBox.Show("請選擇你要發(fā)送的好友?。。?); } else { dict[strKey].Send(arrSendMsg);// 解決了 sokConnection是局部變量,不能再本函數(shù)中引用的問題; ShowMsg(strMsg); richTextBox1.Clear(); }
最后需要注意的是,如果你的文件較大,有的時候這個緩沖區(qū)達(dá)不到你的文件字節(jié)那么大,那么就會截斷,所以與的時候,先將文件轉(zhuǎn)換為Byte是正確的做法。只要在客戶端進(jìn)行逆轉(zhuǎn)就可以了!
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
利用Aspose.Cells和Excel模板導(dǎo)出統(tǒng)計數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了利用Aspose.Cells和Excel模板導(dǎo)出復(fù)雜的統(tǒng)計數(shù)據(jù),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-12-12C#調(diào)用QQ_Mail發(fā)送郵件實例代碼兩例
這篇文章介紹了C#調(diào)用QQ_Mail發(fā)送郵件的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-04-04C# Winform按鈕中圖片實現(xiàn)左圖右字的效果實例
這篇文章主要給大家介紹了關(guān)于C# Winform按鈕中圖片實現(xiàn)左圖右字效果的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11C#?使用?Filestream?修改大文件指定位置數(shù)據(jù)
這篇文章主要介紹了C#?使用?Filestream修改大文件指定位置數(shù)據(jù),文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09C#實現(xiàn)利用泛型將DataSet轉(zhuǎn)為Model的方法
這篇文章主要介紹了C#實現(xiàn)利用泛型將DataSet轉(zhuǎn)為Model的方法,實例分析了C#泛型的相關(guān)使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-07-07C#使用StreamReader和StreamWriter類讀寫操作文件
這篇文章介紹了C#使用StreamReader和StreamWriter類讀寫操作文件的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-05-05