一.TcpClient與TcpServe。 首先我們需要知道伺服器的IP地址,在伺服器端建立監聽,當監聽到客戶端的連接請求後,連接到客戶端。 而客戶端則需要連接到指定的IP伺服器地址,建立網路流,則可以實現通信。 接下來給出一個伺服器端與客戶端的實例: 伺服器端: 此時伺服器端應用的是Socket類 ...
一.TcpClient與TcpServe。
首先我們需要知道伺服器的IP地址,在伺服器端建立監聽,當監聽到客戶端的連接請求後,連接到客戶端。
而客戶端則需要連接到指定的IP伺服器地址,建立網路流,則可以實現通信。
接下來給出一個伺服器端與客戶端的實例:
伺服器端:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Net; using System.Net.Sockets; namespace fuwuqi { class Program { static void Main(string[] args) { try { IPAddress Ip = IPAddress.Parse("127.0.0.1"); //IPAddress提供網際協議地址的類,Parse將IP地址字元串轉換為IPAddress的實例 TcpListener TcpList = new TcpListener(Ip, 8888); //TcpListener網路偵聽類,從TCP網路客戶端偵聽連接,TcpListener (參數1,參數2),參數1表示本地IP地址,參數2表示用來偵聽傳入的埠連接 TcpList.Start(); //啟動對掛起連接數的傳入鏈接請求的偵聽 Console.WriteLine("Server start!"); Console.WriteLine("Ip address:" +TcpList.LocalEndpoint); //LocalEndpoint獲取服務端(即本地)地址與埠等信息 Console.WriteLine("Wait"); Socket Soc = TcpList.AcceptSocket();//相當於啟動客戶端後,Socket被掛起 //AcceptSocket表示接受掛起的連接請求,Socket為套接字介面的類 Console.WriteLine("Received Connection:" + Soc.RemoteEndPoint); //RemoteEndPoint獲取客戶端地址與埠等信息 byte[] b = new byte[100]; int k = Soc.Receive(b); //Soc.Receive(b)從socket接收數據,將數據存入接收緩衝區列表中,k的值為該數據的長度 Console.WriteLine("Received data from client:"); for (int i = 0; i < k; i++) Console.Write(Convert.ToChar(b[i])); //Convert.ToChar(b[i])將數組b轉換基本數據類型為char的類型並輸出 ASCIIEncoding AS = new ASCIIEncoding(); //ASCIIEncoding表示Unicode字元的ASCII字元編碼類 Soc.Send(AS.GetBytes("Received data!")); //Soc.Send向客戶端發送數據,AS.GetBytes()獲得括弧中字元串的bytes值 Soc.Close(); //關閉連接並釋放所有關聯的資源 TcpList.Stop(); //關閉偵聽 Console.ReadLine(); //等待輸入,起到暫停的作用 } catch (Exception e) { Console.WriteLine("Error!" + e.StackTrace); //獲取當前異常發生時調用堆棧上的幀的字元串 } } } }
此時伺服器端應用的是Socket類發送數據流,Socket類用於客戶端和伺服器都可,其發送機制是以一種包的形式發送,在做項目時,發現Socket接收到的數據包與發送到的數據包長度不一定吻合,有阻塞,所以使用Socket數據包發送時,接收和發送的數據組要指定長度,太長也會丟數。
客戶端:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Net.Sockets; namespace kehuduan { class Program { static void Main(string[] args) { try { TcpClient TcpClient = new TcpClient(); //TcpClient為TCP網路服務提供客戶端連接的類 TcpClient.Connect("127.0.0.1", 8888); //Connect方法使用指定的IP地址和埠號將客戶端連接到遠程TCP主機 Console.WriteLine("Connect Complete"); Console.WriteLine("Input string:"); String str = Console.ReadLine(); //定義字元串str變數,保存輸入的字元 Stream stm = TcpClient.GetStream(); //定義數據流,用於發送和接收數據 ASCIIEncoding AS = new ASCIIEncoding(); byte[] b = AS.GetBytes(str); //將字元串轉換為byte類型 stm.Write(b, 0, b.Length); //Write(參數1,參數2,參數3)表示向服務端發送字元串,參數1指將此數組複製到當前流,參數2指從零開始的位元組偏移量,參數3指要寫入當前流的位元組數(即字元串長度) Console.WriteLine("Send Complete"); byte[] bb = new byte[100]; int k = stm.Read(bb, 0, 100); //stm.Read在當前流中讀入服務端發來的響應信息,其參數與Write方法參數一致,k值為讀入字元串的長度 for (int i = 0; i < k; i++) Console.Write(Convert.ToChar(bb[i])); TcpClient.Close(); //釋放TcpClient實例,並不關閉基礎連接 Console.ReadLine(); } catch (Exception e) { Console.WriteLine("Error!" + e.StackTrace); } } } }
上面展示了客戶端與伺服器端的簡單通信。作為例子程式學習,接下來對自己做的項目中連接攝像頭伺服器做一個記錄。
首先,把攝像頭作為伺服器端,定義客戶端:
TcpClient client = new TcpClient("IP",8080);
獲取網路流:
NetworkStream networkstream = client.GetStream();//獲取網路流
此時可以建立與伺服器端的網路流連接,如果需要從網路流中讀取數據或者寫入數據,使用了BinaryReader/BinaryWriter或者StreamReader/StreamWriter,實例化後,就可實現對流的讀寫
BinaryReaderbr = new BinaryReader(networkstream);
BinaryWriter bw = new BinaryWriter(networkstream);
我對這兩個的理解是StreamReder讀取函數ReadToEnd,ReadLine等,讀取出來是字元串類型。而BinaryReader可以按位元組讀取。根據需求選取。
(筆記StreamReader的方法: sr.BaseStream.Seek(0, SeekOrigin.Begin);//從流中當前開始的位置(SeekOrigin.begin)偏移0位讀取)
如果需要對伺服器發送數據直接寫入流,使用Write等函數就可以了。
這裡對自己找的資料也做一個總結。
MemoryStream類:
Memmory類相當於一個緩存流,首先需要對這個流開闢一個空間,然後將數據放入流中對其進行操作。該流的優勢在於可以將數據先放入流中,然後使用指針對流中的任一個位元組進行操作。
學習例子:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { //屬性測試 MemoryStream ms = new MemoryStream(); Console.WriteLine(ms.CanRead); //True 記憶體流可讀 Console.WriteLine(ms.CanSeek); //True 記憶體流支持查找,指針移來移去的查找 Console.WriteLine(ms.CanTimeout); //False 記憶體流不支持超時 Console.WriteLine(ms.CanWrite); //True 記憶體流可寫 Console.WriteLine(ms.Capacity); //0 分配給該流的位元組數 byte[] bytes = Encoding.UTF8.GetBytes("abcdedcba");//字元轉為ASCII ms.Write(bytes, 0, bytes.Length); //已將一段文本寫入記憶體 int l = bytes.Length; Console.WriteLine(ms.Capacity); //256 再次讀取為文本流分配的位元組數已經變成了256,看來記憶體流是根據需要的多少來分配的 Console.WriteLine(ms.Length); //9 這個是流長度,通常與英文的字元數一樣,真正占用的位元組數。 Console.WriteLine(ms.Position); //9 流當前的位置,該屬性可讀可設置 //Console.WriteLine(ms.ReadTimeout); 由於流不支持超時,此屬性如果讀取或者設置的話會報錯 //Console.WriteLine(ms.WriteTimeout); 由於流不支持超時,此屬性如果讀取或者設置的話會報錯 //方法測試 byte[] byte1 = ms.GetBuffer(); //返回無符號位元組數組 差點被忽悠了,無符號位元組數組 其實就是byte(0~255),有符號位元組sbyte(-128~127) string str1 = Encoding.UTF8.GetString(byte1); Console.WriteLine(str1); //輸出 abcdedcba long p = ms.Position;//指針當前位置 ms.Position = 0; ms.Seek(0, SeekOrigin.Current); //設置當前流正在讀取的位置 為開始位置即從0開始 //從記憶體中讀取一個位元組 int i = ms.ReadByte(); Console.WriteLine(i); //輸出99 byte[] bytes3 = ms.ToArray(); foreach (byte b in bytes3) { Console.Write(b + "-");//用於對比 輸出 97-98-99-100-101-100-99-98-97- 可以看到 0,1,2第二位剛好是99 } MemoryStream ms2 = new MemoryStream(); byte[] bytes6 = Encoding.UTF8.GetBytes("abcde"); ms2.Write(bytes6, 0, bytes6.Length); Console.WriteLine(ms2.Position); //輸出5 寫完之後流的位置就到了最後,因此想用read讀取必須加下麵這一行代碼。 //ms2.Seek(0, SeekOrigin.Begin); //想要用Read方法讀取完整的流,必須設置當前位置,Read是從Position的位置開始讀。 ms2.Position = 0; //Read是從當前位置開始讀,這行代碼和上面一行意義一樣。 byte[] byteArray = new byte[5] { 0, 0,0, 0, 0 }; //數組附初值全部是0 ms2.Read(byteArray, 2, 1); //讀取一個位元組,byteArray的第一個元素中,(註意從0開始)讀取數據流里第二個位子開始讀取一個 Console.WriteLine(Encoding.UTF8.GetString(byteArray)); //nnann ms2.Read(byteArray, 2, 2); Console.WriteLine(Encoding.UTF8.GetString(byteArray)); //nnabn //當超出接收數組總長度的時候,後面的元素會被移開 //設置當前流的長度 Console.WriteLine(ms.Length); //輸出9 當前流的長度是9 ms.SetLength(20); Console.WriteLine(ms.Length); //輸出20 foreach (byte b in ms.ToArray()) //將流的內容也就是記憶體中的內容轉換位元組數組 { Console.Write(b + "-"); //輸出 97-98-99-100-101-100-99-98-97-0-0-0-0-0-0-0-0-0 由於設置了長度,因此空的自動補0 } Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray())); //輸出 abcdedcba 雖然長度變長了,但是沒影響讀取數據 MemoryStream ms1 = new MemoryStream(); byte[] bytes4 = ms1.ToArray(); Console.WriteLine("此記憶體流並沒有寫入數據(Write)" + Encoding.UTF8.GetString(bytes4));//輸出 此記憶體流並沒有寫入數據(Write) 因為記憶體為空 //下麵來一個指定位置的寫入 MemoryStream ms3 = new MemoryStream(); byte[] bytesArr = Encoding.ASCII.GetBytes("abcdefg"); ms3.Write(bytesArr, 0, bytesArr.Length); ms3.Position = 2; ms3.WriteByte(97); //97代表的是a 這段代碼的意思是,將原先第二個的c替換為a string str = Encoding.ASCII.GetString(ms3.ToArray()); Console.WriteLine(str); //輸出 abacdefg byte[] byteArr1 = Encoding.ASCII.GetBytes("kk"); ms3.Position = 4; ms3.Write(byteArr1, 0, byteArr1.Length); Console.WriteLine(Encoding.UTF8.GetString(ms3.ToArray())); //abadkkg //從第4位替換掉了兩個位元組為KK Console.ReadKey(); } } }