日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

C#网络编程:4订立协议和发送文件

發布時間:2025/3/21 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#网络编程:4订立协议和发送文件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前面兩篇文章所使用的范例都是傳輸字符串,有的時候我們可能會想在服務端和客戶端之間傳遞文件。比如,考慮這樣一種情況,假如客戶端顯示了一個菜單,當我們輸入S1、S2或S3(S為Send縮寫)時,分別向服務端發送文件Client01.jpg、Client02.jpg、Client03.jpg;當我們輸入R1、R2或R3時(R為Receive縮寫),則分別從服務端接收文件Server01.jpg、Server02.jpg、Server03.jpg。那么,我們該如何完成這件事呢?此時可能有這樣兩種做法:

類似于FTP協議,服務端開辟兩個端口,并持續對這兩個端口偵聽:一個用于接收字符串,類似于FTP的控制端口,它接收各種命令(接收或發送文件);一個用于傳輸數據,也就是發送和接收文件。

服務端只開辟一個端口,用于接收字符串,我們稱之為控制端口。當接到請求之后,根據請求內容在客戶端開辟一個端口專用于文件傳輸,并在傳輸結束后關閉端口。

現在我們只關注于上面的數據端口,回憶一下在第二篇中我們所總結的,可以得出:當我們使用上面的方法一時,服務端的數據端口可以為多個客戶端的多次請求服務;當我們使用方法二時,服務端只為一個客戶端的一次請求服務,但是因為每次請求都會重新開辟端口,所以實際上還是相當于可以為多個客戶端的多次請求服務。同時,因為它只為一次請求服務,所以我們在數據端口上傳輸文件時無需采用異步傳輸方式。但在控制端口我們仍然需要使用異步方式。

從上面看出,第一種方式要好得多,但是我們將采用第二種方式。至于原因,你可以回顧一下Part.1(基本概念和操作)中關于聊天程序模式的講述,因為接下來一篇文章我們將創建一個聊天程序,而這個聊天程序采用第三種模式,所以本文的練習實際是對下一篇的一個鋪墊。

1.訂立協議

1.1發送文件

我們先看一下發送文件的情況,如果我們想將文件client01.jpg由客戶端發往客戶端,那么流程是什么:

客戶端開辟數據端口用于偵聽,并獲取端口號,假設為8005。

假設客戶端輸入了S1,則發送下面的控制字符串到服務端:[file=Client01.jpg, mode=send, port=8005]。

服務端收到以后,根據客戶端ip和端口號與該客戶端建立連接。

客戶端偵聽到服務端的連接,開始發送文件。

傳送完畢后客戶端、服務端分別關閉連接。

此時,我們訂立的發送文件協議為:[file=Client01.jpg, mode=send, port=8005]。但是,由于它是一個普通的字符串,在上一篇中,我們采用了正則表達式來獲取其中的有效值,但這顯然不是一種好辦法。因此,在本文及下一篇文章中,我們采用一種新的方式來編寫協議:XML。對于上面的語句,我們可以寫成這樣的XML:

  • <protocol><file name="client01.jpg" mode="send" port="8005" /></protocol>
  • 這樣我們在服務端就會好處理得多,接下來我們來看一下接收文件的流程及其協議。

    NOTE:這里說發送、接收文件是站在客戶端的立場說的,當客戶端發送文件時,對于服務器來收,則是接收文件。

    1.2接收文件

    接收文件與發送文件實際上完全類似,區別只是由客戶端向網絡流寫入數據,還是由服務端向網絡流寫入數據。

    客戶端開辟數據端口用于偵聽,假設為8006。

    假設客戶端輸入了R1,則發送控制字符串:<protocol><file name="Server01.jpg" mode="receive" port="8006" /></protocol>到服務端。

    服務端收到以后,根據客戶端ip和端口號與該客戶端建立連接。

    客戶端建立起與服務端的連接,服務端開始網絡流中寫入數據。

    傳送完畢后服務端、客戶端分別關閉連接。

    2.協議處理類的實現

    和上面一章一樣,在開始編寫實際的服務端客戶端代碼之前,我們首先要編寫處理協議的類,它需要提供這樣兩個功能:1、方便地幫我們獲取完整的協議信息,因為前面我們說過,服務端可能將客戶端的多次獨立請求拆分或合并。比如,客戶端連續發送了兩條控制信息到服務端,而服務端將它們合并了,那么則需要先拆開再分別處理。2、方便地獲取我們所想要的屬性信息,因為協議是XML格式,所以還需要一個類專門對XML進行處理,獲得字符串的屬性值。

    2.1 ProtocalHandler輔助類

    我們先看下ProtocalHandler,它與上一篇中的RequestHandler作用相同。需要注意的是必須將它聲明為實例的,而非靜態的,這是因為每個TcpClient都需要對應一個ProtocalHandler,因為它內部維護的patialProtocal不能共享,在協議發送不完整的情況下,這個變量用于臨時保存被截斷的字符串。

  • public class ProtocolHandler {?
  • private string partialProtocal; // 保存不完整的協議
  • public ProtocolHandler() {?
  • ??????? partialProtocal = "";??????
  • ??? }?
  • public string[] GetProtocol(string input) {?
  • return GetProtocol(input, null);?
  • ??? }?
  • // 獲得協議
  • private string[] GetProtocol(string input, List<string> outputList) {?
  • if (outputList == null)?
  • ??????????? outputList = new List<string>();?
  • if (String.IsNullOrEmpty(input))?
  • return outputList.ToArray();?
  • if (!String.IsNullOrEmpty(partialProtocal))?
  • ??????????? input = partialProtocal + input;?
  • string pattern = "(^<protocol>.*?</protocol>)";?
  • // 如果有匹配,說明已經找到了,是完整的協議
  • if (Regex.IsMatch(input, pattern)) {?
  • // 獲取匹配的值
  • string match = Regex.Match(input, pattern).Groups[0].Value;?
  • ??????????? outputList.Add(match);?
  • ??????????? partialProtocal = "";?
  • // 縮短input的長度
  • ??????????? input = input.Substring(match.Length);?
  • // 遞歸調用
  • ??????????? GetProtocol(input, outputList);?
  • ??????? } else {?
  • // 如果不匹配,說明協議的長度不夠,
  • // 那么先緩存,然后等待下一次請求
  • ??????????? partialProtocal = input;?
  • ??????? }?
  • return outputList.ToArray();?
  • ??? }?
  • }?
  • 因為現在它已經不是本文的重點了,所以我就不演示對于它的測試了,本文所附帶的代碼中含有它的測試代碼(我在ProtocolHandler中添加了一個靜態類Test())。

    2.2 FileRequestType枚舉和FileProtocol結構

    因為XML是以字符串的形式在進行傳輸,為了方便使用,我們最好構建一個強類型來對它們進行操作,這樣會方便很多。我們首先可以定義FileRequestMode枚舉,它代表是發送還是接收文件:

  • public enum FileRequestMode {?
  • ??? Send = 0,?
  • ??? Receive?
  • }?
  • 接下來我們再定義一個FileProtocol結構,用來為整個協議字符串提供強類型的訪問,注意這里覆蓋了基類的ToString()方法,這樣在客戶端我們就不需要再手工去編寫XML,只要在結構值上調用ToString()就OK了,會方便很多。

  • public struct FileProtocol {?
  • private readonly FileRequestMode mode;?
  • private readonly int port;?
  • private readonly string fileName;?
  • public FileProtocol?
  • ??????? (FileRequestMode mode, int port, string fileName) {?
  • this.mode = mode;?
  • this.port = port;?
  • this.fileName = fileName;?
  • ??? }?
  • public FileRequestMode Mode {?
  • get { return mode; }?
  • ??? }?
  • public int Port {?
  • get { return port; }?
  • ??? }?
  • public string FileName {?
  • get { return fileName; }?
  • ??? }?
  • public override string ToString() {?
  • return String.Format("<protocol><file name=\"{0}\" mode=\"{1}\" port=\"{2}\" /></protocol>", fileName, mode, port);?
  • ??? }?
  • }?
  • 2.3 ProtocolHelper輔助類

    這個類專用于將XML格式的協議映射為我們上面定義的強類型對象,這里我沒有加入try/catch異常處理,因為協議對用戶來說是不可見的,而且客戶端應該總是發送正確的協議,我覺得這樣可以讓代碼更加清晰:

  • public class ProtocolHelper {?
  • private XmlNode fileNode;?
  • private XmlNode root;?
  • public ProtocolHelper(string protocol) {?
  • ??????? XmlDocument doc = new XmlDocument();?
  • ??????? doc.LoadXml(protocol);?
  • ??????? root = doc.DocumentElement;?
  • ??????? fileNode = root.SelectSingleNode("file");?
  • ??? }?
  • // 此時的protocal一定為單條完整protocal
  • private FileRequestMode GetFileMode() {?
  • string mode = fileNode.Attributes["mode"].Value;?
  • ??????? mode = mode.ToLower();?
  • if (mode == "send")?
  • return FileRequestMode.Send;?
  • else
  • return FileRequestMode.Receive;?
  • ??? }?
  • // 獲取單條協議包含的信息
  • public FileProtocol GetProtocol() {?
  • ??????? FileRequestMode mode = GetFileMode();?
  • string fileName = "";?
  • int port = 0;?
  • ??????? fileName = fileNode.Attributes["name"].Value;?
  • ??????? port = Convert.ToInt32(fileNode.Attributes["port"].Value);?
  • return new FileProtocol(mode, port, fileName);?
  • ??? }?
  • }?
  • OK,我們又耽誤了點時間,下面就讓我們進入正題吧。

    3.客戶端發送數據

    3.1 服務端的實現

    我們還是將一個問題分成兩部分來處理,先是發送數據,然后是接收數據。我們先看發送數據部分的服務端。如果你從第一篇文章看到了現在,那么我覺得更多的不是技術上的問題而是思路,所以我們不再將重點放到代碼上,這些應該很容易就看懂了。

  • class Server {?
  • static void Main(string[] args) {?
  • ??????? Console.WriteLine("Server is running ... ");?
  • ??????? IPAddress ip = IPAddress.Parse("127.0.0.1");?
  • ??????? TcpListener listener = new TcpListener(ip, 8500);?
  • ??????? listener.Start();?????????? // 開啟對控制端口 8500 的偵聽
  • ??????? Console.WriteLine("Start Listening ...");?
  • while (true) {?
  • // 獲取一個連接,同步方法,在此處中斷
  • ??????????? TcpClient client = listener.AcceptTcpClient();?????????????
  • ??????????? RemoteClient wapper = new RemoteClient(client);?
  • ??????????? wapper.BeginRead();?
  • ??????? }?
  • ??? }?
  • }?
  • public class RemoteClient {?
  • private TcpClient client;?
  • private NetworkStream streamToClient;?
  • private const int BufferSize = 8192;?
  • private byte[] buffer;?
  • private ProtocolHandler handler;?
  • public RemoteClient(TcpClient client) {?
  • this.client = client;?
  • // 打印連接到的客戶端信息
  • ??????? Console.WriteLine("\nClient Connected!{0} <-- {1}",?
  • ??????????? client.Client.LocalEndPoint, client.Client.RemoteEndPoint);?
  • // 獲得流
  • ??????? streamToClient = client.GetStream();?
  • ??????? buffer = new byte[BufferSize];?
  • ??????? handler = new ProtocolHandler();?
  • ??? }?
  • // 開始進行讀取
  • public void BeginRead() {??????
  • ??????? AsyncCallback callBack = new AsyncCallback(OnReadComplete);?
  • ??????? streamToClient.BeginRead(buffer, 0, BufferSize, callBack, null);?
  • ??? }?
  • // 再讀取完成時進行回調
  • private void OnReadComplete(IAsyncResult ar) {?
  • int bytesRead = 0;?
  • try {?
  • lock (streamToClient) {?
  • ??????????????? bytesRead = streamToClient.EndRead(ar);?
  • ??????????????? Console.WriteLine("Reading data, {0} bytes ...", bytesRead);?
  • ??????????? }?
  • if (bytesRead == 0) throw new Exception("讀取到0字節");?
  • string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);?
  • ??????????? Array.Clear(buffer,0,buffer.Length);??????? // 清空緩存,避免臟讀
  • // 獲取protocol數組
  • string[] protocolArray = handler.GetProtocol(msg);?
  • foreach (string pro in protocolArray) {?
  • // 這里異步調用,不然這里可能會比較耗時
  • ??????????????? ParameterizedThreadStart start =?
  • new ParameterizedThreadStart(handleProtocol);?
  • ??????????????? start.BeginInvoke(pro, null, null);?
  • ??????????? }?
  • // 再次調用BeginRead(),完成時調用自身,形成無限循環
  • lock (streamToClient) {?
  • ??????????????? AsyncCallback callBack = new AsyncCallback(OnReadComplete);?
  • ??????????????? streamToClient.BeginRead(buffer, 0, BufferSize, callBack, null);?
  • ??????????? }?
  • ??????? } catch(Exception ex) {?
  • if(streamToClient!=null)?
  • ??????????????? streamToClient.Dispose();?
  • ??????????? client.Close();?
  • ??????????? Console.WriteLine(ex.Message);????? // 捕獲異常時退出程序
  • ??????? }?
  • ??? }?
  • // 處理protocol
  • private void handleProtocol(object obj) {?
  • string pro = obj as string;?
  • ??????? ProtocolHelper helper = new ProtocolHelper(pro);?
  • ??????? FileProtocol protocol = helper.GetProtocol();?
  • if (protocol.Mode == FileRequestMode.Send) {?
  • // 客戶端發送文件,對服務端來說則是接收文件
  • ??????????? receiveFile(protocol);?
  • ??????? } else if (protocol.Mode == FileRequestMode.Receive) {?
  • // 客戶端接收文件,對服務端來說則是發送文件
  • // sendFile(protocol);
  • ??????? }?
  • ??? }?
  • private void receiveFile(FileProtocol protocol) {?
  • // 獲取遠程客戶端的位置
  • ??????? IPEndPoint endpoint = client.Client.RemoteEndPoint as IPEndPoint;?
  • ??????? IPAddress ip = endpoint.Address;?
  • // 使用新端口號,獲得遠程用于接收文件的端口
  • ??????? endpoint = new IPEndPoint(ip, protocol.Port);?
  • // 連接到遠程客戶端
  • ??????? TcpClient localClient;?
  • try {?
  • ??????????? localClient = new TcpClient();?
  • ??????????? localClient.Connect(endpoint);?
  • ??????? } catch {?
  • ??????????? Console.WriteLine("無法連接到客戶端 --> {0}", endpoint);?
  • return;?
  • ??????? }?
  • // 獲取發送文件的流
  • ??????? NetworkStream streamToClient = localClient.GetStream();?
  • // 隨機生成一個在當前目錄下的文件名稱
  • string path =?
  • ??????????? Environment.CurrentDirectory + "/" + generateFileName(protocol.FileName);?
  • byte[] fileBuffer = new byte[1024]; // 每次收1KB
  • ??????? FileStream fs = new FileStream(path, FileMode.CreateNew, FileAccess.Write);?
  • // 從緩存buffer中讀入到文件流中
  • int bytesRead;?
  • int totalBytes = 0;?
  • do {?
  • ??????????? bytesRead = streamToClient.Read(buffer, 0, BufferSize);????????????
  • ??????????? fs.Write(buffer, 0, bytesRead);?
  • ??????????? totalBytes += bytesRead;?
  • ??????????? Console.WriteLine("Receiving {0} bytes ...", totalBytes);?
  • ??????? } while (bytesRead > 0);?
  • ??????? Console.WriteLine("Total {0} bytes received, Done!", totalBytes);?
  • ??????? streamToClient.Dispose();?
  • ??????? fs.Dispose();?
  • ??????? localClient.Close();?
  • ??? }?
  • // 隨機獲取一個圖片名稱
  • private string generateFileName(string fileName) {?
  • ??????? DateTime now = DateTime.Now;?
  • return String.Format(?
  • "{0}_{1}_{2}_{3}", now.Minute, now.Second, now.Millisecond, fileName?
  • ??????? );?
  • ??? }?
  • }?
  • 這里應該沒有什么新知識,需要注意的地方有這么幾個:

    在OnReadComplete()回調方法中的foreach循環,我們使用委托異步調用了handleProtocol()方法,這是因為handleProtocol即將執行的是一個讀取或接收文件的操作,也就是一個相對耗時的操作。

    在handleProtocol()方法中,我們深切體會了定義ProtocolHelper類和FileProtocol結構的好處。如果沒有定義它們,這里將是不堪入目的處理XML以及類型轉換的代碼。

    handleProtocol()方法中進行了一個條件判斷,注意sendFile()方法我屏蔽掉了,這個還沒有實現,但是我想你已經猜到它將是后面要實現的內容。

    receiveFile()方法是實際接收客戶端發來文件的方法,這里沒有什么特別之處。需要注意的是文件存儲的路徑,它保存在了當前程序執行的目錄下,文件的名稱我使用generateFileName()生成了一個與時間有關的隨機名稱。

    3.2客戶端的實現

    我們現在先不著急實現客戶端S1、R1等用戶菜單,首先完成發送文件這一功能,實際上,就是為上一節SendMessage()加一個姐妹方法SendFile()。

  • class Client {?
  • static void Main(string[] args) {?
  • ??????? ConsoleKey key;?
  • ??????? ServerClient client = new ServerClient();?
  • string filePath = Environment.CurrentDirectory + "/" + "Client01.jpg";?
  • if(File.Exists(filePath))?
  • ??????????? client.BeginSendFile(filePath);?
  • ??????? Console.WriteLine("\n\n輸入\"Q\"鍵退出。");?
  • do {?
  • ??????????? key = Console.ReadKey(true).Key;?
  • ??????? } while (key != ConsoleKey.Q);?
  • ??? }?
  • }?
  • public class ServerClient {?
  • private const int BufferSize = 8192;?
  • private byte[] buffer;?
  • private TcpClient client;?
  • private NetworkStream streamToServer;?
  • public ServerClient() {?
  • try {?
  • ??????????? client = new TcpClient();?
  • ??????????? client.Connect("localhost", 8500);????? // 與服務器連接
  • ??????? } catch (Exception ex) {?
  • ??????????? Console.WriteLine(ex.Message);?
  • return;?
  • ??????? }?
  • ??????? buffer = new byte[BufferSize];?
  • // 打印連接到的服務端信息
  • ??????? Console.WriteLine("Server Connected!{0} --> {1}",?
  • ??????????? client.Client.LocalEndPoint, client.Client.RemoteEndPoint);?
  • ??????? streamToServer = client.GetStream();?
  • ??? }?
  • // 發送消息到服務端
  • public void SendMessage(string msg) {?
  • byte[] temp = Encoding.Unicode.GetBytes(msg);?? // 獲得緩存
  • try {?
  • lock (streamToServer) {?
  • ??????????????? streamToServer.Write(temp, 0, temp.Length); // 發往服務器
  • ??????????? }?
  • ??????????? Console.WriteLine("Sent: {0}", msg);?
  • ??????? } catch (Exception ex) {?
  • ??????????? Console.WriteLine(ex.Message);?
  • return;?
  • ??????? }?
  • ??? }?
  • // 發送文件 - 異步方法
  • public void BeginSendFile(string filePath) {?
  • ??????? ParameterizedThreadStart start =?
  • new ParameterizedThreadStart(BeginSendFile);?
  • ??????? start.BeginInvoke(filePath, null, null);?
  • ??? }?
  • private void BeginSendFile(object obj) {?
  • string filePath = obj as string;?
  • ??????? SendFile(filePath);?
  • ??? }?
  • // 發送文件 -- 同步方法
  • public void SendFile(string filePath) {?
  • ??????? IPAddress ip = IPAddress.Parse("127.0.0.1");?
  • ??????? TcpListener listener = new TcpListener(ip, 0);?
  • ??????? listener.Start();?
  • // 獲取本地偵聽的端口號
  • ??????? IPEndPoint endPoint = listener.LocalEndpoint as IPEndPoint;?
  • int listeningPort = endPoint.Port;?
  • // 獲取發送的協議字符串
  • string fileName = Path.GetFileName(filePath);?
  • ??????? FileProtocol protocol =?
  • new FileProtocol(FileRequestMode.Send, listeningPort, fileName);?
  • string pro = protocol.ToString();?
  • ??????? SendMessage(pro);?????? // 發送協議到服務端
  • // 中斷,等待遠程連接
  • ??????? TcpClient localClient = listener.AcceptTcpClient();?
  • ??????? Console.WriteLine("Start sending file...");?
  • ??????? NetworkStream stream = localClient.GetStream();?
  • // 創建文件流
  • ??????? FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);??????????
  • byte[] fileBuffer = new byte[1024];???? // 每次傳1KB
  • int bytesRead;?
  • int totalBytes = 0;?
  • // 創建獲取文件發送狀態的類
  • ??????? SendStatus status = new SendStatus(filePath);?
  • // 將文件流轉寫入網絡流
  • try {?
  • do {?
  • ??????????????? Thread.Sleep(10);?????????? // 為了更好的視覺效果,暫停10毫秒:-)
  • ??????????????? bytesRead = fs.Read(fileBuffer, 0, fileBuffer.Length);?????????????????
  • ??????????????? stream.Write(fileBuffer, 0, bytesRead);?
  • ??????????????? totalBytes += bytesRead;??????????? // 發送了的字節數
  • ??????????????? status.PrintStatus(totalBytes); // 打印發送狀態
  • ??????????? } while (bytesRead > 0);?
  • ??????????? Console.WriteLine("Total {0} bytes sent, Done!", totalBytes);?
  • ??????? } catch {?
  • ??????????? Console.WriteLine("Server has lost...");?
  • ??????? }?
  • ??????? stream.Dispose();?
  • ??????? fs.Dispose();?
  • ??????? localClient.Close();?
  • ??????? listener.Stop();?
  • ??? }?
  • }?
  • 接下來我們來看下這段代碼,有這么兩點需要注意一下:

    ???? 在Main()方法中可以看到,圖片的位置為應用程序所在的目錄,如果你跟我一樣處于調試模式,那么就在解決方案的Bin目錄下的Debug目錄中放置三張圖片Client01.jpg、Client02.jpg、Client03.jpg,用來發往服務端。

    ???? 我在客戶端提供了兩個SendFile()方法,和一個BeginSendFile()方法,分別用于同步和異步傳輸,其中私有的SendFile()方法只是一個輔助方法。實際上對于發送文件這樣的操作我們幾乎總是需要使用異步操作。

    ???? SendMessage()方法中給streamToServer加鎖很重要,因為SendFile()方法是多線程訪問的,而在SendFile()方法中又調用了SendMessage()方法。

    ???? 我另外編寫了一個SendStatus類,它用來記錄和打印發送完成的狀態,已經發送了多少字節,完成度是百分之多少,等等。本來這個類的內容我是直接寫入在Client類中的,后來我覺得它執行的工作已經不屬于Client本身所應該執行的領域之內了,我記得這樣一句話:當你覺得類中的方法與類的名稱不符的時候,那么就應該考慮重新創建一個類。我覺得用在這里非常恰當。

    下面是SendStatus的內容:

  • // 即時計算發送文件的狀態
  • public class SendStatus {?
  • private FileInfo info;?
  • private long fileBytes;?
  • public SendStatus(string filePath) {?
  • ??????? info = new FileInfo(filePath);?
  • ??????? fileBytes = info.Length;?
  • ??? }?
  • public void PrintStatus(int sent) {?
  • string percent = GetPercent(sent);?
  • ??????? Console.WriteLine("Sending {0} bytes, {1}% ...", sent, percent);?
  • ??? }?
  • // 獲得文件發送的百分比
  • public string GetPercent(int sent){????
  • decimal allBytes = Convert.ToDecimal(fileBytes);?
  • decimal currentSent = Convert.ToDecimal(sent);?
  • decimal percent = (currentSent / allBytes) * 100;?
  • ??????? percent = Math.Round(percent, 1);?? //保留一位小數
  • if (percent.ToString() == "100.0")?
  • return "100";?
  • else
  • return percent.ToString();?
  • ??? }?
  • }?
  • 3.3程序測試

    接下里我們運行一下程序,來檢查一下輸出,首先看下服務端:

    接著是客戶端,我們能夠看到發送的字節數和進度,可以想到如果是圖形界面,那么我們可以通過擴展SendStatus類來創建一個進度條:

    最后我們看下服務端的Bin\Debug目錄,應該可以看到接收到的圖片:

    本來我想這篇文章就可以完成發送和接收,不過現在看來沒法實現了,因為如果繼續下去這篇文章就太長了,我正嘗試著盡量將文章控制在15頁以內。那么我們將在下篇文章中再完成接收文件這一部分。

    轉載于:https://www.cnblogs.com/bennylam/archive/2010/07/24/1784279.html

    總結

    以上是生活随笔為你收集整理的C#网络编程:4订立协议和发送文件的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。