源码   (为节省空间,不包含通信框架源码,通信框架源码请另行下载)

文件传送在TCP通信中是经常用到的,本文针对文件传送进行探讨

经过测试,可以发送比较大的文件,比如1个G或者2个G

本文只对文件传送做了简单的探讨,示例程序可能也不是很成熟,希望本文起到抛砖引玉的作用,有兴趣的朋友帮忙补充完善

首先看一下实现的效果

服务器端:

客户端(一次只能发送一个文件):

服务器端收到的文件,存放到了D盘根目录下(存放的路径可以根据情况修改)

本程序基于开源的networkcomms2.3.1通信框架

下面来看一下实现的步骤:

1、客户端

(1): 先连接服务器:

    //给连接信息对象赋值
connInfo = new ConnectionInfo(txtIP.Text, int.Parse(txtPort.Text)); //如果不成功,会弹出异常信息
newTcpConnection = TCPConnection.GetConnection(connInfo); TCPConnection.StartListening(connInfo.LocalEndPoint); button1.Enabled = false;
button1.Text = "连接成功";

(2)发送大文件(分段发送)

   private void SendFileButton_Click(object sender, EventArgs e)
{
//打开对话框,获取文件
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
//暂时禁用发送按钮
sendFileButton.Enabled = false; //获取对话框中选择的文件的名称
string filename = openFileDialog1.FileName; //设置进度条显示为0
UpdateSendProgress(0); try
{
//创建一个文件流
FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read); //创建一个线程安全流
ThreadSafeStream safeStream = new ThreadSafeStream(stream); //获取不包含路径的文件名称
string shortFileName = System.IO.Path.GetFileName(filename); //每次发送的字节数 可根据实际情况进行设定
long sendChunkSizeBytes = 40960;
//已发送的字节数
long totalBytesSent = 0;
do
{
//检查剩余的字节数 小于 上面指定的字节数 则发送"剩余的字节数" 否则发送"指定的字节数"
long bytesToSend = (totalBytesSent + sendChunkSizeBytes < stream.Length ? sendChunkSizeBytes : stream.Length - totalBytesSent); //包装一个ThreadSafeStream 使之可以分段发送
StreamSendWrapper streamWrapper = new StreamSendWrapper(safeStream, totalBytesSent, bytesToSend); //顺序号
long packetSequenceNumber;
//发送指定数据
newTcpConnection.SendObject("PartialFileData", streamWrapper, customOptions, out packetSequenceNumber);
//发送指定的数据相关的信息
newTcpConnection.SendObject("PartialFileDataInfo", new SendInfo(shortFileName, stream.Length, totalBytesSent, packetSequenceNumber), customOptions); totalBytesSent += bytesToSend; UpdateSendProgress((double)totalBytesSent / stream.Length);
//两次发送之间间隔一定时间
System.Threading.Thread.Sleep(30); } while (totalBytesSent < stream.Length); }
catch (CommunicationException)
{ }
catch (Exception ex)
{ NetworkComms.LogError(ex, "SendFileError"); } } }

2:服务器端接收文件:

(1)开始监听

 //服务器开始监听客户端的请求
//开始监听某T端口
IPEndPoint thePoint = new IPEndPoint(IPAddress.Parse(txtIP.Text), int.Parse(txtPort.Text));
TCPConnection.StartListening(thePoint, false);
button1.Text = "监听中";
button1.Enabled = false; //此方法中包含服务器具体的处理方法。
StartListening();

(2)添加接收文件处理方法

               //处理收到的文件字节数据
NetworkComms.AppendGlobalIncomingPacketHandler<byte[]>("PartialFileData", IncomingPartialFileData);
//处理收到的文件信息数据
NetworkComms.AppendGlobalIncomingPacketHandler<SendInfo>("PartialFileDataInfo", IncomingPartialFileDataInfo);
  //处理收到的文件字节数据

        private void IncomingPartialFileData(PacketHeader header, Connection connection, byte[] data)
{
try
{
SendInfo info = null;
ReceivedFile file = null; lock (syncRoot)
{
//获取顺序号
long sequenceNumber = header.GetOption(PacketHeaderLongItems.PacketSequenceNumber); if (incomingDataInfoCache.ContainsKey(connection.ConnectionInfo) && incomingDataInfoCache[connection.ConnectionInfo].ContainsKey(sequenceNumber))
{ //如果已经收到此部分 “文件字节数据” 对应的 “文件信息数据”
info = incomingDataInfoCache[connection.ConnectionInfo][sequenceNumber];
incomingDataInfoCache[connection.ConnectionInfo].Remove(sequenceNumber); if (!receivedFilesDict.ContainsKey(connection.ConnectionInfo))
receivedFilesDict.Add(connection.ConnectionInfo, new Dictionary<string, ReceivedFile>()); //如果当前收到字节数据,还没有对应的ReceivedFile类,则创建一个
if (!receivedFilesDict[connection.ConnectionInfo].ContainsKey(info.Filename))
{
receivedFilesDict[connection.ConnectionInfo].Add(info.Filename, new ReceivedFile(info.Filename, connection.ConnectionInfo, info.TotalBytes)); } file = receivedFilesDict[connection.ConnectionInfo][info.Filename];
}
else
{ if (!incomingDataCache.ContainsKey(connection.ConnectionInfo))
incomingDataCache.Add(connection.ConnectionInfo, new Dictionary<long, byte[]>()); incomingDataCache[connection.ConnectionInfo].Add(sequenceNumber, data);
}
} if (info != null && file != null && !file.IsCompleted)
{
file.AddData(info.BytesStart, 0, data.Length, data); file = null;
data = null; }
else if (info == null ^ file == null)
throw new Exception("Either both are null or both are set. Info is " + (info == null ? "null." : "set.") + " File is " + (file == null ? "null." : "set.") + " File is " + (file.IsCompleted ? "completed." : "not completed."));
}
catch (Exception ex)
{ NetworkComms.LogError(ex, "IncomingPartialFileDataError");
}
}
   //处理收到的文件信息数据
private void IncomingPartialFileDataInfo(PacketHeader header, Connection connection, SendInfo info)
{
try
{
byte[] data = null;
ReceivedFile file = null; lock (syncRoot)
{
//获取顺序号
long sequenceNumber = info.PacketSequenceNumber; if (incomingDataCache.ContainsKey(connection.ConnectionInfo) && incomingDataCache[connection.ConnectionInfo].ContainsKey(sequenceNumber))
{
//如果当前文件信息类对应的文件字节部分已经存在
data = incomingDataCache[connection.ConnectionInfo][sequenceNumber];
incomingDataCache[connection.ConnectionInfo].Remove(sequenceNumber); if (!receivedFilesDict.ContainsKey(connection.ConnectionInfo))
receivedFilesDict.Add(connection.ConnectionInfo, new Dictionary<string, ReceivedFile>()); if (!receivedFilesDict[connection.ConnectionInfo].ContainsKey(info.Filename))
{
receivedFilesDict[connection.ConnectionInfo].Add(info.Filename, new ReceivedFile(info.Filename, connection.ConnectionInfo, info.TotalBytes)); } file = receivedFilesDict[connection.ConnectionInfo][info.Filename];
}
else
{ if (!incomingDataInfoCache.ContainsKey(connection.ConnectionInfo))
incomingDataInfoCache.Add(connection.ConnectionInfo, new Dictionary<long, SendInfo>()); incomingDataInfoCache[connection.ConnectionInfo].Add(sequenceNumber, info);
}
} if (data != null && file != null && !file.IsCompleted)
{
file.AddData(info.BytesStart, 0, data.Length, data);
file = null;
data = null; }
else if (data == null ^ file == null)
throw new Exception("Either both are null or both are set. Data is " + (data == null ? "null." : "set.") + " File is " + (file == null ? "null." : "set.") + " File is " + (file.IsCompleted ? "completed." : "not completed."));
}
catch (Exception ex)
{
NetworkComms.LogError(ex, "IncomingPartialFileDataInfo");
}
}
 临时存储文件数据用到的字典类
 ReceivedFile方法

3.在MessageContract类库中添加SendInfo契约类方法,此方法用于传递文件信息,客户端和服务器端都需要使用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using ProtoBuf; namespace MessageContract
{
/// <summary>
/// 文件信息类
/// </summary>
[ProtoContract]
public class SendInfo
{
/// <summary>
/// 文件名称
/// </summary>
[ProtoMember(1)]
public string Filename { get; private set; } /// <summary>
/// 文件发送-开始位置
/// </summary>
[ProtoMember(2)]
public long BytesStart { get; private set; } /// <summary>
/// 文件大小
/// </summary>
[ProtoMember(3)]
public long TotalBytes { get; private set; } /// <summary>
/// 顺序号
/// </summary>
[ProtoMember(4)]
public long PacketSequenceNumber { get; private set; } /// <summary>
/// 私有构造函数 用来反序列化
/// </summary>
private SendInfo() { } /// <summary>
/// 创建一个新的实例
/// </summary>
/// <param name="filename">文件名称 Filename corresponding to data</param>
/// <param name="totalBytes">文件大小 Total bytes of the whole ReceivedFile</param>
/// <param name="bytesStart">开始位置 The starting point for the associated data</param>
/// <param name="packetSequenceNumber">顺序号 Packet sequence number corresponding to the associated data</param>
public SendInfo(string filename, long totalBytes, long bytesStart, long packetSequenceNumber)
{
this.Filename = filename;
this.TotalBytes = totalBytes;
this.BytesStart = bytesStart;
this.PacketSequenceNumber = packetSequenceNumber;
}
}
}
 

TCP通信中的大文件传送的更多相关文章

  1. [c#源码分享]TCP通信中的大文件传送

    NetworkComms网络通信框架序言 源码   (为节省空间,不包含通信框架源码,通信框架源码请另行下载) 文件传送在TCP通信中是经常用到的,本文针对文件传送进行探讨 经过测试,可以发送比较大的 ...

  2. 【Java TCP/IP Socket】深入剖析socket——TCP通信中由于底层队列填满而造成的死锁问题(含代码)

    基础准备 首先需要明白数据传输的底层实现机制,在http://blog.csdn.net/ns_code/article/details/15813809这篇博客中有详细的介绍,在上面的博客中,我们提 ...

  3. TCP/UDP通信中server和client是如何知道对方IP地址的

    在TCP通信中 client是主动连接的一方,client对server的IP的地址提前已知的.如果是未知则是没办法通信的. server是在accpet返回的时候知道的,因为数据包中包含客户端的IP ...

  4. 清晰易懂TCP通信原理解析(附demo、简易TCP通信库源码、解决沾包问题等)C#版

    目录 说明 TCP与UDP通信的特点 TCP中的沾包现象 自定义应用层协议 TCPLibrary通信库介绍 Demo演示 未完成功能 源码下载 说明 我前面博客中有多篇文章讲到了.NET中的网络编程, ...

  5. 【学习笔记】TCP通信的细节及TCP连接对HTTP事务处理性能影响

    从三次握手的细节说起 刚开始尝试使用java等后端语言写IO流,或用套接字(socket)实现简单C/S通信的同学们,常常会接触到的一个概念:就是所谓的"三次握手",socket作 ...

  6. 【计算机网络】TCP通信的细节及TCP连接对HTTP事务处理性能影响

    从三次握手的细节说起 刚开始尝试使用java等后端语言写IO流,或用套接字(socket)实现简单C/S通信的同学们,常常会接触到的一个概念:就是所谓的“三次握手”,socket作为一个API接口,封 ...

  7. C#版清晰易懂TCP通信原理解析(附demo)

    [转] C#版清晰易懂TCP通信原理解析(附demo) (点击上方蓝字,可快速关注我们) 来源:周见智 cnblogs.com/xiaozhi_5638/p/4244797.html 对.NET中网络 ...

  8. 第七篇:几个经典的TCP通信函数

    前言 在TCP通信中要使用到几个非常经典的函数,本文将对这几个函数进行一个简短的使用说明. socket()函数 函数作用:创建一个网际字节流套接字 包含头文件:sys/socket.h ( 后面几个 ...

  9. 几个经典的TCP通信函数

    前言 在TCP通信中要使用到几个非常经典的函数( 点这里参考一个关于它们作用的形象比方 ),本文将对这几个函数进行一个简短的使用说明. socket函数 函数作用:创建一个网际字节流套接字 包含头文件 ...

随机推荐

  1. ProgressMonitorInputStream

    Swing类包中有一个很有用的流过滤器,ProgressMonitorInputStream,它可以自动弹出一个对话框,监视已经读取了多少流. 进度监视器流使用InputStream类的availab ...

  2. POJ 2418 Hardwood Species(STL在map应用)

    职务地址:id=2418">POJ 2418 通过这个题查了大量资料..知道了非常多曾经不知道的东西. . .. 在代码中凝视说明吧. 代码例如以下: #include <ios ...

  3. [课程分享]IT软件项目管理(企业项目甘特如是评价、维护管理、文档管理、风险管理、人力资源管理)

    [课程分享]IT件项目管理(企业项目甘特图案例评价.维护管理.文档管理.风险管理.人力资源管理) 对这个课程有兴趣的朋友能够加我的QQ2059055336和我联系 课程讲师:丁冬博士 课程分类:Jav ...

  4. Net 项目构建基于Jenkins + Github + Mono 的持续集成环境

    Net 项目构建基于Jenkins + Github + Mono 的持续集成环境 阅读目录 1 安装 2 配置 3 测试 在Redhat enterprise 6.5 的服务器上,为在gutub 上 ...

  5. C#+Mapxtreme 实现一些GIS系统基本的功能

    此程序包括了mapxtreme地图相关基本功能的演示其中包括 鹰眼地图,图层控制,发达,缩小,平移地图,地图模糊查询,中点工具,距离测量工具,面积测量工具,图元信息查看工具.适合于企业级开发,可以为您 ...

  6. 2014年TI杯大学生电子设计竞赛地区赛使用仪器及器件、设备

     2014年TI杯大学生电子设计竞赛地区赛使用仪器及器件.设备 a)        3A/30V双路稳压电源(可并联): b)        60MHz示波器: c)        三位半数字万用 ...

  7. java 它 引用(基本类型的包装,构造函数和析构函数c++不同)

      一个:java 和c++参考控制 他提到引用,我们会想到java它不喜欢c++里面的指针.当然java内引用和c++里面的引用是不同的. 比如: 比方C++中,我对某一个函数的声明.int a(i ...

  8. a web-based music player(GO + html5)

    github 住址:https://github.com/codercheng/music-player 后台是用GO (windows/ linux 都能够),前端是HTML5 推荐用chrome浏 ...

  9. (大数据工程师学习路径)第一步 Linux 基础入门----目录结构及文件基本操作

    Linux 目录结构及文件基本操作 介绍 1.Linux 的文件组织目录结构. 2.相对路径和绝对路径. 3.对文件的移动.复制.重命名.编辑等操作. 一.Linux 目录结构 在讲 Linux 目录 ...

  10. java缓存技术(转)

    最近在做java缓存,了解了一下.以下仅是对map对方式讨论.没有对点阵图阵讨论.作缓存要做以下2点: 1:清理及更新缓存时机的处理:   . 虚拟机内存不足,清理缓存   .. 缓存时间超时,或访问 ...