概述
最近一直在忙,今天正好抽空来总结下最近忙的东西。
文件传输:c/s模式。这样摆在眼前的第一个问题就是 网络编程 , 这里,C#的网络编程,推荐去张子阳的博文学习学习,大牛写的,就是流弊。但是,最近他的博文网站上不去了,大家可以去找找其他人转载张子阳的博文。这里丢一个连接:http://download.csdn.net/detail/pemwoo/4934863, 转载博客:http://www.xuebuyuan.com/295625.html
不废话了, 进入正题。
1、 文件传输,服务器与客户端就要使用同一套协议。协议包括了传输消息的格式,还有关于消息的编码与解码。这个相当重要呢。
2、在传输数据的过程中,发送方发出去的数据类型是什么,接收方就要使用相同的数据类型的缓冲区去接收。(我就是因为这个,调试老半天了)。比如, 发送方某次发送数据类型为 long, 接收方却用 int 去接收,接收方只能接收前4个字节, 还有4个字节的数据在网络上。
下面是关键部分代码:
服务器:
///---请求客户端文件(可执行文件)
public void SendVersionContextToClient()
{
try
{
///---发送消息格式为:文件名长度 、 文件名、内容长度、文件内容
///------1、发送文件名长度
string filenName = "1.rar";
byte[] fileNameLen = BitConverter.GetBytes(filenName.Length);
lock (ClientNetworkStream)
{
ClientNetworkStream.Write(fileNameLen, 0, fileNameLen.Length);
ClientNetworkStream.Flush();
}
///-----2、发送文件名
byte[] byteFileName = Encoding.Default.GetBytes(filenName);//Encoding.Unicode.GetBytes(filenName);
lock (ClientNetworkStream)
{
ClientNetworkStream.Write(byteFileName, 0, byteFileName.Length);
ClientNetworkStream.Flush();
}
///---3、发送文件大小
string filePath = Environment.CurrentDirectory+@"" + filenName;
///---创建文件流
FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read);
///---获取文件大小
long fileLen = fileStream.Length;
int int_FileLen = (int)fileLen;
byte[] fileLenArr = BitConverter.GetBytes(int_FileLen);
lock (ClientNetworkStream)
{
ClientNetworkStream.Write(fileLenArr, 0, fileLenArr.Length);
ClientNetworkStream.Flush();
}
///---4、发送文件内容
lock (ClientNetworkStream)
{
///---发送文件缓存:1KB
byte[] readBuf = new byte[1024 * 16];
int readByte = 0;
///---读到的数据全部存入缓冲区
byte [] sendBuf = new byte[fileLen];
int count = 0;
readByte = fileStream.Read(readBuf, 0, readBuf.Length);
do
{
Array.Copy(readBuf, 0, sendBuf, count, readByte);
count += readByte;
///---清空读文件缓存区
Array.Clear(readBuf, 0, readBuf.Length);
///---再次读取文件内容
readByte = fileStream.Read(readBuf, 0, readBuf.Length);
} while (readByte > 0);
Debug.WriteLine(count);
///---断开文件操作流
fileStream.Dispose();
fileStream.Close();
///---发出去
ClientNetworkStream.Write(sendBuf, 0, sendBuf.Length);
ClientNetworkStream.Flush();
}
}
catch (Exception ex)
{
Trace.WriteLine("failed to send versionContext to client. Reason :" + ex.Message);
throw ex;
}
}
客户端关键代码:
private void Form1_Load(object sender, EventArgs e)
{
///---建立套接字
TcpClient client = new TcpClient();
try
{
client.Connect("localhost", 8119);
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
return;
}
NetworkStream streamToServer = client.GetStream();
/-------------------------已经建立套接字连接
/---1、请求客户端版本号:长度|用户名|密码|消息类型(整型)|消息内容
//Request_FileVersion(ref streamToServer);
/---2、接收服务器返回的消息
//byte[] recvBuf = new byte[4];
//int newVersion = Receive(streamToServer, recvBuf, 4);
/---解码获取返回的版本号
//newVersion = BitConverter.ToInt32(recvBuf, 0);
int curVersion = 0;
///---服务器返回的客户端版本号大于当前客户端版本号
///接着向服务器发出请求:更新当前客户端
if (2 > curVersion)
{
///---2.1、请求更新客户端
ReQuest_FileContext(streamToServer);
///---接收分为4步:
///---1、文件名长度
///---2、文件名
///---3、文件内容长度
///---4、文件内容
///---1、接收文件名长度,默认为4字节长,格式为 X.rar
byte[] fileNameLenBuf = new byte[4];
Receive(streamToServer, fileNameLenBuf, 4);
///---获取文件名长度
int fileNameLen = BitConverter.ToInt32(fileNameLenBuf, 0);
//MessageBox.Show(fileNameLen.ToString());
///---2、接收文件名
byte[] fileNameByteBuf = new byte[fileNameLen];
int l1 = MyNetAPI.Receive(streamToServer, fileNameByteBuf, fileNameLen);
///---取出接收到的文件名
string recvFileName = Encoding.Default.GetString(fileNameByteBuf);
//MessageBox.Show(recvFileName
+ "
长度:" + l1.ToString());
///---3、接收文件内容长度
byte[] fileContextLen = new byte[4];
MyNetAPI.Receive(streamToServer, fileContextLen, 4);
///---取出文件长度,显示
int fileLength = BitConverter.ToInt32(fileContextLen, 0);
//MessageBox.Show(fileLength.ToString() +" 文件内容长度");
///---4、接收文件内容
byte[] fileContextByte = new byte[fileLength];
streamToServer.Flush();
int backLen = MyNetAPI.Receive(streamToServer, fileContextByte, fileLength);
//MessageBox.Show("客户端接收到的文件的长度为:" + backLen.ToString());
try
{
///---保存文件
///--1、设置文件存储路径
string filePath = @"C:UsersRGDesktop第二进度ClientbinDebug"+ recvFileName;
///---保存文件
File.WriteAllBytes(filePath, fileContextByte);
//MessageBox.Show("成功");
}
catch (Exception ex)
{
streamToServer.Dispose();
streamToServer.Close();
client.Close();
MessageBox.Show(ex.Message);
}
}
}
客户端在接收代码中使用的函数 Recieve,能保证完整接收到服务器发送的数据。 Recieve原型如下:
/// <summary>
/// 从网络从接收 wdatlen个字节数据到buf,必须收满后才返回
/// </summary>
/// <param name="ns"></param>
/// <param name="buf"></param>
/// <param name="wdatlen">期望接收数据的长度</param>
/// <returns></returns>
public static int Receive(NetworkStream ns, byte[] buf, int wdatlen)
{
int count=0;
int alen = 0;
byte []tmp = new byte[wdatlen];
// 如果接收缓冲区不够长,直接返回
if (buf.Length < wdatlen)
{
return -1;
}
while (count < wdatlen)
{
try
{
alen = ns.Read(tmp, 0, tmp.Length);
}
// 网络断开异常
catch (IOException ex)
{
throw ex;
}
// 如果没有读到数据,继续读
if (alen == 0)
{
//throw new Exception("网络连接异常断开");
continue;
}
// 把每次收的加入到总的缓冲区
Array.Copy(tmp, 0, buf, count, alen);
count += alen;
}
return count;
}
由于是项目代码, 就不丢完整代码链接了。
提示自己:
网络编程,慢慢磨吧, 路还长,
最后
以上就是无语方盒为你收集整理的C#学习之文件传输的全部内容,希望文章能够帮你解决C#学习之文件传输所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复