C# Socket文件上傳至服務(wù)器的操作方法
前言
文件上傳有FTP、WebApi、WebService等等,這次我們來實(shí)現(xiàn)一個(gè)基于socket通信的本地客戶端上傳文件到服務(wù)器的例子。
運(yùn)行效果
一、Socket(套接字)概念
說起socket通信,那就不得不介紹一下套接字(socket),套接字是通信的基石,是支持TCP/IP協(xié)議的網(wǎng)絡(luò)通信的基本操作單元,套接字Socket=(IP地址:端口號)。就是對網(wǎng)絡(luò)中不同主機(jī)上的應(yīng)用進(jìn)程之間進(jìn)行雙向通信的端點(diǎn)的抽象。一個(gè)套接字就是網(wǎng)絡(luò)上進(jìn)程通信的一端,提供了應(yīng)用層進(jìn)程利用網(wǎng)絡(luò)協(xié)議交換數(shù)據(jù)的機(jī)制。從所處的地位來講,套接字上聯(lián)應(yīng)用進(jìn)程,下聯(lián)網(wǎng)絡(luò)協(xié)議棧,是應(yīng)用程序通過網(wǎng)絡(luò)協(xié)議進(jìn)行通信的接口,是應(yīng)用程序與網(wǎng)絡(luò)協(xié)議棧進(jìn)行交互的接口,它包含進(jìn)行網(wǎng)絡(luò)通信必須的五種信息:連接使用的協(xié)議,本地主機(jī)的IP地址,本地進(jìn)程的協(xié)議端口,遠(yuǎn)地主機(jī)的IP地址,遠(yuǎn)地進(jìn)程的協(xié)議端口。
二、Socket通信的建立
建立Socket連接至少需要一對套接字,其中一個(gè)運(yùn)行于客戶端,稱為ClientSocket ,另一個(gè)運(yùn)行于服務(wù)器端,稱為ServerSocket 。
三、通信流程
根據(jù)連接啟動的方式以及本地套接字要連接的目標(biāo),套接字之間的連接過程可以分為三個(gè)步驟:
(1)服務(wù)器監(jiān)聽。
(2)客戶端請求。
(3)連接確認(rèn)。
1.服務(wù)器監(jiān)聽
所謂服務(wù)器監(jiān)聽,是指服務(wù)器端套接字并不定位具體的客戶端套接字,而是處于等待連接的狀態(tài),實(shí)時(shí)監(jiān)控網(wǎng)絡(luò)狀態(tài)。
2.客戶端請求
所謂客戶端請求,是指由客戶端的套接字提出連接請求,要連接的目標(biāo)是服務(wù)器端的套接字。為此,客戶端的套接字必須首先描述它要連接的服務(wù)器的套接字,指出服務(wù)器端套接字的地址和端口號,然后就向服務(wù)器端接字提出連接請求 。
3.連接確認(rèn)
所謂連接確認(rèn),是指當(dāng)服務(wù)器端套接字監(jiān)聽到或者說接收到客戶端套接字的連接請求,就會響應(yīng)客戶端套接字的請求,建立一個(gè)新的線程,并把服務(wù)器端套接字的描述發(fā)送給客戶端。一旦客戶端確認(rèn)了此描述,連接就建立好了。而服務(wù)器端套接字繼續(xù)處于監(jiān)聽狀態(tài),接收其他客戶端套接字的連接請求。
四、實(shí)現(xiàn)
1.ClientSocket
本機(jī)IP地址在窗體加載的時(shí)候賦值。
private void FrmMain_Load(object sender, EventArgs e) { //獲得本機(jī)的IP地址 this.textBox4.Text = Dns.GetHostByName(Dns.GetHostName()).AddressList[0].ToString(); }
接下來我們正式開始,點(diǎn)擊瀏覽,選擇需要上傳的文件。此時(shí)我們獲取當(dāng)前文件的全路徑、文件名稱及合計(jì)字節(jié)。
/// <summary> /// 瀏覽 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { //選擇要進(jìn)行傳輸?shù)奈募? if (this.openFileDialog1.ShowDialog() == DialogResult.OK) { FileInfo EzoneFile = new FileInfo(this.openFileDialog1.FileName); this.textBox1.Text = EzoneFile.FullName; this.textBox2.Text = EzoneFile.Name; this.textBox3.Text = EzoneFile.Length.ToString(); } }
點(diǎn)擊發(fā)送按鈕,將文件以字節(jié)流的形式發(fā)送到服務(wù)器端,此時(shí)應(yīng)該單獨(dú)開一個(gè)子線程以保證監(jiān)聽運(yùn)行,監(jiān)聽事件定義StartSend。
/// <summary> /// 開始發(fā)送 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click(object sender, EventArgs e) { //開啟文件傳輸子線程 Thread TempThread = new Thread(new ThreadStart(this.StartSend)); TempThread.Start(); }
StartSend事件中,根據(jù)文件路徑創(chuàng)建一個(gè)文件對象,配置操作流、創(chuàng)建連接對象、IP\Port等等關(guān)鍵信息。首先我們來創(chuàng)建一個(gè)幫助類FileClientSocket.cs
using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; namespace TouchSocketClient { public class FileClientSocket { public static int SendData(Socket s, byte[] data) { int total = 0; int size = data.Length; int dataleft = size; int sent; while (total < size) { sent = s.Send(data, total, dataleft, SocketFlags.None); total += sent; dataleft -= sent; } return total; } public static byte[] ReceiveData(Socket s, int size) { int total = 0; int dataleft = size; byte[] data = new byte[size]; int recv; while (total < size) { recv = s.Receive(data, total, dataleft, SocketFlags.None); if (recv == 0) { data = null; break; } total += recv; dataleft -= recv; } return data; } public static int SendVarData(Socket s, byte[] data) { int total = 0; int size = data.Length; int dataleft = size; int sent; byte[] datasize = new byte[4]; datasize = BitConverter.GetBytes(size); sent = s.Send(datasize); while (total < size) { sent = s.Send(data, total, dataleft, SocketFlags.None); total += sent; dataleft -= sent; } return total; } public static byte[] ReceiveVarData(Socket s) { int total = 0; int recv; byte[] datasize = new byte[4]; recv = s.Receive(datasize, 0, 4, SocketFlags.None); int size = BitConverter.ToInt32(datasize, 0); int dataleft = size; byte[] data = new byte[size]; while (total < size) { recv = s.Receive(data, total, dataleft, SocketFlags.None); if (recv == 0) { data = null; break; } total += recv; dataleft -= recv; } return data; } } }
StartSend方法實(shí)現(xiàn)
private void StartSend() { //創(chuàng)建一個(gè)文件對象 FileInfo EzoneFile = new FileInfo(this.textBox1.Text); //打開文件流 FileStream EzoneStream = EzoneFile.OpenRead(); //包的大小 int PacketSize = int.Parse(this.textBox6.Text); //包的數(shù)量 int PacketCount = (int)(EzoneStream.Length / ((long)PacketSize)); //最后一個(gè)包的大小 int LastDataPacket = (int)(EzoneStream.Length - ((long)(PacketSize * PacketCount))); //指向遠(yuǎn)程服務(wù)端節(jié)點(diǎn) IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(txtIP.Text.Trim()), int.Parse(this.textBox5.Text)); //創(chuàng)建套接字 Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //連接到發(fā)送端 client.Connect(ipep); //獲得客戶端節(jié)點(diǎn)對象 IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint; //獲得客戶端的IP地址 //this.textBox7.Text=clientep.Address.ToString(); //發(fā)送[文件名]到客戶端 FileClientSocket.SendVarData(client,System.Text.Encoding.Unicode.GetBytes(EzoneFile.Name)); //發(fā)送[包的大小]到客戶端 FileClientSocket.SendVarData(client,System.Text.Encoding.Unicode.GetBytes(PacketSize.ToString())); //發(fā)送[包的總數(shù)量]到客戶端 FileClientSocket.SendVarData(client,System.Text.Encoding.Unicode.GetBytes(PacketCount.ToString())); //發(fā)送[最后一個(gè)包的大小]到客戶端 FileClientSocket.SendVarData(client,System.Text.Encoding.Unicode.GetBytes(LastDataPacket.ToString())); //數(shù)據(jù)包 byte[] data = new byte[PacketSize]; //開始循環(huán)發(fā)送數(shù)據(jù)包 for (int i = 0; i < PacketCount; i++) { //從文件流讀取數(shù)據(jù)并填充數(shù)據(jù)包 EzoneStream.Read(data, 0, data.Length); //發(fā)送數(shù)據(jù)包 FileClientSocket.SendVarData(client, data); } //如果還有多余的數(shù)據(jù)包,則應(yīng)該發(fā)送完畢! if (LastDataPacket != 0) { data = new byte[LastDataPacket]; EzoneStream.Read(data, 0, data.Length); FileClientSocket.SendVarData(client, data); } //關(guān)閉套接字 client.Close(); //關(guān)閉文件流 EzoneStream.Close(); MessageBox.Show("文件傳輸完畢!"); }
至此客戶端部分已經(jīng)完成,下面開始實(shí)現(xiàn)服務(wù)端。
2.ServerSocket
我們新建一個(gè)控制臺應(yīng)用程序,TouchSocketServer。
在Main方法中 注冊接收事件,并開始子線程進(jìn)行處理。
static void Main(string[] args) { //開啟接收線程 Thread TempThread = new Thread(new ThreadStart(StartReceive)); TempThread.Start(); }
同樣我們服務(wù)端也需要一個(gè)Server類(FileSocketServer.cs)
namespace TouchSocketServer { public class FileSocketServer { public static int SendData(Socket s, byte[] data) { int total = 0; int size = data.Length; int dataleft = size; int sent; while (total < size) { sent = s.Send(data, total, dataleft, SocketFlags.None); total += sent; dataleft -= sent; } return total; } public static byte[] ReceiveData(Socket s, int size) { int total = 0; int dataleft = size; byte[] data = new byte[size]; int recv; while (total < size) { recv = s.Receive(data, total, dataleft, SocketFlags.None); if (recv == 0) { data = null; break; } total += recv; dataleft -= recv; } return data; } public static int SendVarData(Socket s, byte[] data) { int total = 0; int size = data.Length; int dataleft = size; int sent; byte[] datasize = new byte[4]; datasize = BitConverter.GetBytes(size); sent = s.Send(datasize); while (total < size) { sent = s.Send(data, total, dataleft, SocketFlags.None); total += sent; dataleft -= sent; } return total; } public static byte[] ReceiveVarData(Socket s) { int total = 0; int recv; byte[] datasize = new byte[4]; recv = s.Receive(datasize, 0, 4, SocketFlags.None); int size = BitConverter.ToInt32(datasize, 0); int dataleft = size; byte[] data = new byte[size]; while (total < size) { recv = s.Receive(data, total, dataleft, SocketFlags.None); if (recv == 0) { data = null; break; } total += recv; dataleft -= recv; } return data; } } }
接收函數(shù)
private static void StartReceive() { //創(chuàng)建一個(gè)網(wǎng)絡(luò)端點(diǎn) Console.ForegroundColor = ConsoleColor.DarkRed; Console.WriteLine($"{DateTime.Now} [初始化網(wǎng)絡(luò)端點(diǎn)]"); IPEndPoint ipep = new IPEndPoint(IPAddress.Any, int.Parse("1005")); //MessageBox.Show(IPAddress.Any); //創(chuàng)建一個(gè)套接字 Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //綁定套接字到端口 server.Bind(ipep); //開始偵聽(并堵塞該線程) Console.WriteLine($"{DateTime.Now} [開始偵聽并堵塞該線程]"); server.Listen(10); while (true) { try { Socket client = server.Accept(); ClientSocket = client; Thread TempThread = new Thread(new ThreadStart(Create)); TempThread.Start(); } catch (Exception ex) { int k = 0; } } }
創(chuàng)建文件函數(shù)
public static void Create() { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine($"{DateTime.Now} [創(chuàng)建對象]"); Socket client = ClientSocket; //確認(rèn)連接 //Socket client = server.Accept(); //獲得客戶端節(jié)點(diǎn)對象 Console.ForegroundColor = ConsoleColor.Yellow; IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint; Console.WriteLine($"{DateTime.Now} 請求對象:[{clientep.Address}]"); //獲得[文件名] string SendFileName = System.Text.Encoding.Unicode.GetString(FileSocketServer.ReceiveVarData(client)); Console.WriteLine($"{DateTime.Now} 文件名稱:[{SendFileName}]"); //獲得[包的大小] string bagSize = System.Text.Encoding.Unicode.GetString(FileSocketServer.ReceiveVarData(client)); //獲得[包的總數(shù)量] int bagCount = int.Parse(System.Text.Encoding.Unicode.GetString(FileSocketServer.ReceiveVarData(client))); //獲得[最后一個(gè)包的大小] string bagLast = System.Text.Encoding.Unicode.GetString(FileSocketServer.ReceiveVarData(client)); //自動創(chuàng)建文件夾 string path = $@"{System.AppDomain.CurrentDomain.BaseDirectory}\Log\{clientep.Address}({DateTime.Now.ToString("yyyyMMddhhmmssffffff")})"; Directory.CreateDirectory(path); //創(chuàng)建一個(gè)新文件 FileStream MyFileStream = new FileStream($@"{path}\" + SendFileName, FileMode.Create, FileAccess.Write); Console.WriteLine($"{DateTime.Now} 創(chuàng)建目標(biāo):[已創(chuàng)建]"); //已發(fā)送包的個(gè)數(shù) int SendedCount = 0; while (true) { byte[] data = FileSocketServer.ReceiveVarData(client); if (data.Length == 0) { break; } else { SendedCount++; //將接收到的數(shù)據(jù)包寫入到文件流對象 MyFileStream.Write(data, 0, data.Length); //顯示已發(fā)送包的個(gè)數(shù) } } //關(guān)閉文件流 MyFileStream.Close(); //關(guān)閉套接字 client.Close(); Console.WriteLine($"{DateTime.Now} 傳輸結(jié)果:[成功]"); }
到此這篇關(guān)于C# Socket文件上傳至服務(wù)器的文章就介紹到這了,更多相關(guān)C# Socket文件上傳內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用C#編寫一個(gè)Windows服務(wù)程序的方法詳解
這篇文章主要為大家詳細(xì)介紹了如何利用C#編寫一個(gè)Windows服務(wù)程序,文中的實(shí)現(xiàn)方法講解詳細(xì),具有一定的參考價(jià)值,感興趣的可以了解一下2023-03-03C#中TreeView實(shí)現(xiàn)適合兩級節(jié)點(diǎn)的選中節(jié)點(diǎn)方法
這篇文章主要介紹了C#中TreeView實(shí)現(xiàn)適合兩級節(jié)點(diǎn)的選中節(jié)點(diǎn)方法,實(shí)例分析了C#中TreeView節(jié)點(diǎn)操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09- 本文主要介紹了C#中利用GDI來繪制圖形和文字的方法,并提供的簡單的示例供大家參考學(xué)習(xí),希望能夠?qū)Υ蠹矣兴鶐椭?/div> 2016-03-03
C#實(shí)現(xiàn)AddRange為數(shù)組添加多個(gè)元素的方法
這篇文章主要介紹了C#實(shí)現(xiàn)AddRange為數(shù)組添加多個(gè)元素的方法,實(shí)例分析了AddRange方法的使用技巧,需要的朋友可以參考下2015-06-06C#實(shí)現(xiàn)PDF合并的項(xiàng)目實(shí)踐
有時(shí)我們可能會遇到需要的資料或教程被分成了幾部分存放在多個(gè)PDF文件中,本文主要介紹了C#實(shí)現(xiàn)PDF合并的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01最新評論