C# Socket文件上傳至服務(wù)器的操作方法
前言
文件上傳有FTP、WebApi、WebService等等,這次我們來實(shí)現(xiàn)一個基于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)的抽象。一個套接字就是網(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連接至少需要一對套接字,其中一個運(yùn)行于客戶端,稱為ClientSocket ,另一個運(yùn)行于服務(wù)器端,稱為ServerSocket 。
三、通信流程
根據(jù)連接啟動的方式以及本地套接字要連接的目標(biāo),套接字之間的連接過程可以分為三個步驟:
(1)服務(wù)器監(jiān)聽。
(2)客戶端請求。
(3)連接確認(rèn)。
1.服務(wù)器監(jiān)聽
所謂服務(wù)器監(jiān)聽,是指服務(wù)器端套接字并不定位具體的客戶端套接字,而是處于等待連接的狀態(tài),實(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)客戶端套接字的請求,建立一個新的線程,并把服務(wù)器端套接字的描述發(fā)送給客戶端。一旦客戶端確認(rèn)了此描述,連接就建立好了。而服務(wù)器端套接字繼續(xù)處于監(jiān)聽狀態(tài),接收其他客戶端套接字的連接請求。
四、實(shí)現(xiàn)
1.ClientSocket

本機(jī)IP地址在窗體加載的時候賦值。
private void FrmMain_Load(object sender, EventArgs e)
{
//獲得本機(jī)的IP地址
this.textBox4.Text = Dns.GetHostByName(Dns.GetHostName()).AddressList[0].ToString();
}接下來我們正式開始,點(diǎn)擊瀏覽,選擇需要上傳的文件。此時我們獲取當(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ù)器端,此時應(yīng)該單獨(dú)開一個子線程以保證監(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)建一個文件對象,配置操作流、創(chuàng)建連接對象、IP\Port等等關(guān)鍵信息。首先我們來創(chuàng)建一個幫助類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)建一個文件對象
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));
//最后一個包的大小
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ā)送[最后一個包的大小]到客戶端
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
我們新建一個控制臺應(yīng)用程序,TouchSocketServer。

在Main方法中 注冊接收事件,并開始子線程進(jìn)行處理。
static void Main(string[] args)
{
//開啟接收線程
Thread TempThread = new Thread(new ThreadStart(StartReceive));
TempThread.Start();
}同樣我們服務(wù)端也需要一個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)建一個網(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)建一個套接字
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)));
//獲得[最后一個包的大小]
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)建一個新文件
FileStream MyFileStream = new FileStream($@"{path}\" + SendFileName, FileMode.Create, FileAccess.Write);
Console.WriteLine($"{DateTime.Now} 創(chuàng)建目標(biāo):[已創(chuàng)建]");
//已發(fā)送包的個數(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ā)送包的個數(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#中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ù)組添加多個元素的方法
這篇文章主要介紹了C#實(shí)現(xiàn)AddRange為數(shù)組添加多個元素的方法,實(shí)例分析了AddRange方法的使用技巧,需要的朋友可以參考下2015-06-06
C#實(shí)現(xiàn)PDF合并的項(xiàng)目實(shí)踐
有時我們可能會遇到需要的資料或教程被分成了幾部分存放在多個PDF文件中,本文主要介紹了C#實(shí)現(xiàn)PDF合并的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01最新評論

