关于CS架构文件传输流的问题,文中代码都是转自网上,但可保证代码无无误...
今天看論壇看到一個人問了這一個問題:
———————————————————————————————————————————
在c#的socket編程中,
客戶端通過socket.Send()傳送完文件后,
服務(wù)端,接收后,如何將那些byte的內(nèi)容還原為原來的文件啊。。。
求大俠指點,謝謝
———————————————————————————————————————————————————
?
其實這個問題出現(xiàn)得讓人無語,卻又顯得很正常。
讓人無語是因為文件存儲的方式本就是二進制,而我們在網(wǎng)絡(luò)中傳輸?shù)膮s又是字節(jié)流,也就是二進制流,那么這個傳輸本就是一個復(fù)制粘貼的的問題,服務(wù)器端的文件作為做基本的二進制流傳送到客戶端,而客戶端按照接受的順序?qū)⒍M制流重新寫入本地文件,則客戶端生成的本地文件與服務(wù)器端本就是一個復(fù)本。故而很南門為什么會問出還原為原來文件的問題。
但是顯得正常是因為我們面對的都是文本流,我們很多時候不需要關(guān)注二進制流,更不需要關(guān)注扇區(qū)等硬件細節(jié)性問題了。這個使得我們很多人已經(jīng)忘記文件是以二進制存儲的,尤其很多從事純軟工作的,對于搗鼓過模擬電路數(shù)字電路搗鼓過bios等微機接口的人來說,很明顯的二進制存儲方式,對于純軟的人來說,可能不是那么天經(jīng)地義了。這個也可以說成是軟件進步的一個體現(xiàn)吧,分層、模塊化、面向?qū)ο蠡乃枷胍呀?jīng)使我們真的從底層細節(jié)中脫離出來了。我們只需要關(guān)注我們的解決方案,卻不再需要糾纏于實現(xiàn)細節(jié)問題。就像我們用一個statck的時候,我們很少去問底層的棧是怎么實現(xiàn)的,內(nèi)存分配是怎么弄的。
這也是我在上次的string與stringbuilder比較中提到的那個小白的觀點一樣,我們說string是不可變的,是常量,string s="aa"; s +="bb";內(nèi)存中將存在三個字符串復(fù)本,但是那個小白卻說string s="1"; s="0"來證明string是變量,不是不可變的。這個是同樣的道理,我們從事軟件編程,卻很多人套多局限于我們自己的視野,從來沒有去問過操作系統(tǒng)是怎么實現(xiàn)的,內(nèi)存是怎么分配的,進程是誰管理的,怎么管理的等等。
我們解決問題的時候需要不斷抽象不斷屏蔽,但是我們自己的知識框架可不能不斷抽象屏蔽,否則最后我們會發(fā)現(xiàn)一切都不再理所當(dāng)然,我們會到處失措的。
下面就說一下這位同學(xué)問的問題的解決方案吧,當(dāng)然這些都是大家一看就覺得很白癡的解決方案了,基礎(chǔ)問題的解決方案很多時候就是白癡的,但是我們卻不一定都能想起來。
/// <summary> /// 把對象序列化并返回相應(yīng)的字節(jié) /// </summary> /// <param name="objectToSerialize">參數(shù):OBJECT 待序列化對象</param> /// <returns>返回:BYTE[] 序列化后的字節(jié)流</returns> public static byte[] SerializeObject(object objectToSerialize){ if (objectToSerialize == null) return null; MemoryStream memoryStream = new MemoryStream(); BinaryFormatter binaryFormatter = new BinaryFormatter(); binaryFormatter.Serialize(memoryStream, objectToSerialize); memoryStream.Position = 0; byte[] read = new byte[memoryStream.Length]; memoryStream.Read(read, 0, read.Length); memoryStream.Close(); return read; } |
| ? |
/// <summary> /// 把字節(jié)反序列化成相應(yīng)的對象 /// </summary> /// <param name="byteToDeserialize">參數(shù):BYTE[] 待還原的字節(jié)流</param> /// <returns>返回:OBJECT 還原后的對象</returns> public static object DeserializeObject(byte[] byteToDeserialize) { object originalObject = null; if (byteToDeserialize == null) return originalObject; MemoryStream memoryStream = new MemoryStream(byteToDeserialize); memoryStream.Position = 0; BinaryFormatter formatter = new BinaryFormatter(); originalObject = formatter.Deserialize(memoryStream); memoryStream.Close(); return originalObject; } |
?
這個是本地進程中實現(xiàn)的,通過MemoryStream與BinaryFormatter來實現(xiàn)的。
?
當(dāng)然網(wǎng)絡(luò)傳輸,我們更多的是通過FileStream來實現(xiàn)的。完整示例如下:
客戶端Client: |
public partial class ClientFrm : Form { private int size =66530000; public ClientFrm() { InitializeComponent(); } //發(fā)送文件 private void btnSendFile_Click(object sender, EventArgs e) {FileSend();} /// <summary> /// 負責(zé)向服務(wù)器發(fā)送文件 /// </summary> private void FileSend() { try { DialogResult result; //當(dāng)點擊取消或文件名為空時終止程序 result = openFileDig.ShowDialog(); if (result == DialogResult.Cancel || openFileDig.FileName == "" || txtHostName.Text == "" || txtPort.Text == "") { MessageBox.Show("條件不全!"); return; } //tctClient對象可指定主機名和端口 TcpClient tc = new TcpClient(txtHostName.Text.Trim(), int.Parse(txtPort.Text.Trim())); //創(chuàng)建網(wǎng)絡(luò)流 NetworkStream ns = tc.GetStream(); //創(chuàng)建一個文件流,并將打開的文件以字節(jié)流形式讀入內(nèi)存 FileStream fsByte = new FileStream(@openFileDig.FileName, FileMode.Open, FileAccess.Read); byte[] a = InsertFileSign(ns, fsByte); int sCount = 0,curLen=0; double c = 0.0, f = (double)fsByte.Length; while (sCount < ((int)fsByte.Length) && ns.CanWrite) { byte[] byts = new Byte[size]; curLen =fsByte.Read(byts, 0, byts.Length); ns.Write(byts, 0, curLen); sCount = sCount + curLen; c = sCount; progressBar1.Value =Convert.ToInt32((c / f) * 100); this.Refresh(); } progressBar1.Value = 100; //關(guān)閉打開的流 fsByte.Flush(); fsByte.Close(); ns.Flush(); ns.Close(); tc.Close(); this.Refresh(); MessageBox.Show("文件發(fā)送成功!"); progressBar1.Value = 0; } catch (Exception ex) { MessageBox.Show(ex.Message); } finally { } } /// <summary> /// 向傳輸?shù)奈募懭胛募Y(jié)構(gòu)信息標(biāo)記 /// </summary> /// <param name="ns"></param> /// <param name="fsByte"></param> /// <returns></returns> private byte[] InsertFileSign(NetworkStream ns, FileStream fsByte) { string[] name = new string[openFileDig.FileName.Length]; name = openFileDig.FileName.Split(new char[] { '\\' }); //自定義編碼方式#MName#文件名.擴展名#MLen#文件長度#End#" string sign1 = "#MName#" + name[name.Length - 1] + "#MLen#" + fsByte.Length.ToString() + "#End#"; byte[] a = StringTOByts(sign1); ns.Write(a,0,a.Length); return a; } //將字符串轉(zhuǎn)換為字節(jié)數(shù)組 public byte[] StringTOByts(string str) { //當(dāng)字符向字節(jié)轉(zhuǎn)換是2個字節(jié)表示一個字符 byte[] byts = new Byte[1000]; char[] chs = str.ToCharArray(); for (int index = 0; index < chs.Length; index++) { //取出字符的低8位并將二進制轉(zhuǎn)換位十進制存入字節(jié)數(shù)組 byts[index * 2] = (byte)(chs[index] & 0xFF); //用右移8位取出高8位并將二進制轉(zhuǎn)換位十進制存入字節(jié)數(shù)組 byts[index * 2 + 1] = (byte)((chs[index] >> 8) & 0xFF); } //剩余空間用0填充 for (int i = (str.Length * 2); i < 1000; i++) { byts[i] = 0; } return byts; } ? private void btnCance_Click(object sender, EventArgs e) { this.Close(); } } |
服務(wù)器端: |
public partial class ServerFrm : Form { private string paths=@"D:\"; private int size =66530000; public ServerFrm() { InitializeComponent(); } ? private void button1_Click(object sender, EventArgs e) { DialogResult result; //當(dāng)點擊取消或文件名為空時終止程序 result = openPath.ShowDialog(); if (result == DialogResult.Cancel || txtHostName.Text == "" || txtPort.Text == "") { MessageBox.Show("條件不全或保存路徑為空!"); return; } else paths = openPath.FileName; ReceivedFile(); } /// <summary> /// 接收文件 /// </summary> private TcpListener tpclis=null; private FileStream fsWriteFile = null; private void ReceivedFile() { try { //IPHostEntry hostInfo = Dns.GetHostByAddress("127.0.0.1"); //IPAddress[] ipAddree =hostInfo.AddressList; //IPAddress ip = ipAddree[0]; 獲得本地Ip IPAddress ip = IPAddress.Parse(txtHostName.Text.Trim()); //監(jiān)聽本地端口 tpclis = new TcpListener(ip, int.Parse(txtPort.Text.Trim())); tpclis.Start(); //創(chuàng)建接收文件線程 Thread thread = new Thread(new ThreadStart(GetFileData)); Control.CheckForIllegalCrossThreadCalls = false; thread.IsBackground = true; thread.Start(); //GetFileData(); } catch (Exception ex) { MessageBox.Show(ex.Message); } finally { } } //獲得文件數(shù)據(jù) private void GetFileData() { while (true) { TcpClient tc=tpclis.AcceptTcpClient(); //獲得網(wǎng)絡(luò)流 NetworkStream ns = tc.GetStream(); int MLen; string Mname; ? GetFileSignInfo(ns, out MLen, out Mname); //檢查路徑如果不存在就創(chuàng)建它 bool fag=Directory.Exists(paths); if (!fag) Directory.CreateDirectory(paths); ? //創(chuàng)建文件 fsWriteFile = new FileStream(paths +@"\"+ Mname, FileMode.OpenOrCreate); int sCount = 0, curLen = 0; double c = 0.0,f=MLen; while (sCount < MLen && ns.CanRead) { byte[] file = new Byte[size]; curLen = ns.Read(file, 0, file.Length); fsWriteFile.Write(file, 0, curLen); sCount = sCount + curLen; c = sCount; progressBar1.Value =Convert.ToInt32((c / f) * 100); } progressBar1.Value = 100; fsWriteFile.Flush(); fsWriteFile.Close(); ns.Flush(); ns.Close(); MessageBox.Show("文件已收到!"); progressBar1.Value = 0; } } /// <summary> /// 獲得文件標(biāo)記信息 /// </summary> /// <param name="ns"></param> /// <param name="bytes"></param> /// <param name="MLen"></param> /// <param name="Mname"></param> private void GetFileSignInfo(NetworkStream ns, out int MLen, out string Mname) { byte[] a = new Byte[1000]; ns.Read(a, 0, 1000); string str = BytsTOString(a, 1000); //解析規(guī)則"#98#MName#光良 - 童話.mp3#MLen#244582#End#"; int ln1 = str.IndexOf("#MName#", 0); int ln2 = str.IndexOf("#MLen#", ln1 + 6); int ln3 = str.IndexOf("#End#", ln2 + 5); //獲得文件參數(shù) MLen = int.Parse(str.Substring(ln2 + 6, ln3 - ln2 - 6));//獲得歌曲長度 Mname = str.Substring(ln1 + 7, ln2 - ln1 - 7);//獲得歌曲名及擴展名 } ? private void button2_Click(object sender, EventArgs e) { this.Close(); } //將字節(jié)型數(shù)據(jù)轉(zhuǎn)換成字符串 public string BytsTOString(byte[] byts, int len) { string str = ""; char ch; for (int index = 0; index < len / 2; index++) { ch = (char)(byts[index * 2] + (byts[index * 2 + 1] << 8)); str += ch; } ? return str; } ? /// <summary> /// 把對象序列化并返回相應(yīng)的字節(jié) /// </summary> /// <param name="objectToSerialize">參數(shù):OBJECT 待序列化對象</param> /// <returns>返回:BYTE[] 序列化后的字節(jié)流</returns> public static byte[] SerializeObject(object objectToSerialize) { if (objectToSerialize == null) return null; MemoryStream memoryStream = new MemoryStream(); BinaryFormatter binaryFormatter = new BinaryFormatter(); binaryFormatter.Serialize(memoryStream, objectToSerialize); memoryStream.Position = 0; byte[] read = new byte[memoryStream.Length]; memoryStream.Read(read, 0, read.Length); memoryStream.Close(); return read; } ? /// <summary> /// 把字節(jié)反序列化成相應(yīng)的對象 /// </summary> /// <param name="byteToDeserialize">參數(shù):BYTE[] 待還原的字節(jié)流</param> /// <returns>返回:OBJECT 還原后的對象</returns> public static object DeserializeObject(byte[] byteToDeserialize) { object originalObject = null; if (byteToDeserialize == null) return originalObject; MemoryStream memoryStream = new MemoryStream(byteToDeserialize); memoryStream.Position = 0; BinaryFormatter formatter = new BinaryFormatter(); originalObject = formatter.Deserialize(memoryStream); memoryStream.Close(); return originalObject; } ? ? } |
?
上面這個例子也順并解決了另一個同學(xué)的問題,他問的是如何解析出文件的名稱作者等信息。最原始的方法就是通過協(xié)議來解決。自己定義傳輸?shù)亩M制流的協(xié)議。這樣服務(wù)器端對著接受的到的字節(jié)流,按照協(xié)議來解析既可。
轉(zhuǎn)載于:https://www.cnblogs.com/BLoodMaster/archive/2010/07/02/1769924.html
總結(jié)
以上是生活随笔為你收集整理的关于CS架构文件传输流的问题,文中代码都是转自网上,但可保证代码无无误...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux网络基本网络配置
- 下一篇: Windows 7 Problem St