NetworkComms网络通信框架序言

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

文件传送在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;
}
}
}

---------------------
作者:networkcomms
来源:CSDN
原文:https://blog.csdn.net/networkcomms/article/details/44217851
版权声明:本文为博主原创文章,转载请附上博文链接!

[c#源码分享]TCP通信中的大文件传送的更多相关文章

  1. TCP通信中的大文件传送

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

  2. 【腾讯Bugly干货分享】深入源码探索 ReactNative 通信机制

    Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 本文从源码角度剖析 RNA 中 J ...

  3. TCP/IP以及Socket聊天室带类库源码分享

    TCP/IP以及Socket聊天室带类库源码分享 最近遇到个设备,需要去和客户的软件做一个网络通信交互,一般的我们的上位机都是作为客户端来和设备通信的,这次要作为服务端来监听客户端,在这个背景下,我查 ...

  4. 3D语音天气球(源码分享)——在Unity中使用Android语音服务

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 开篇废话: 这个项目准备分四部分介绍: 一:创建可旋转的"3D球":3 ...

  5. TCP/IP源码(59)——TCP中的三个接收队列

    http://blog.chinaunix.net/uid-23629988-id-3482647.html TCP/IP源码(59)——TCP中的三个接收队列  作者:gfree.wind@gmai ...

  6. 3D语音天气球(源码分享)——完结篇

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 开篇废话: 由于这篇文章是本系列最后一篇,有必要进行简单的回顾和思路整理. 这个程序是由两 ...

  7. [DeviceOne开发]-土地销售项目源码分享

    一.简介 这个是一个真实项目开源,虽然不是很花哨,但是中规中矩,小细节处理的也很好,非常值得参考和借鉴.里面的数据都缓存到本地,可以离线运行,但是调整一下代码,马上就可以和服务端完全对接.后续会有详细 ...

  8. angular源码分析:angular中的依赖注入式如何实现的

    一.准备 angular的源码一份,我这里使用的是v1.4.7.源码的获取,请参考我另一篇博文:angular源码分析:angular源代码的获取与编译环境安装 二.什么是依赖注入 据我所知,依赖注入 ...

  9. WP8.1&Win10幸运大转盘源码分享

    先AD一下我的群:Win10开发者群:53078485 最近在写一个APP,其中需要一个转盘动画的源码,找了很多但是都没有找到,无奈只好自己来写,写完效果自己还是比较满意的,分享出来,有需要的童鞋可以 ...

随机推荐

  1. delphi vlc 安装bug 处理编译错误"0" is an invalid value for the "DebugInformation" parameter of the "DCC"

    处理编译错误"0" is an invalid value for the "DebugInformation" parameter of the "DCC" [摘要:http://blog.csdn ...

  2. CSS属性去除图片链接时的虚线框

    CSS 之outline (轮廓)是绘制于元素周围的一条线,位于边框边缘的外围,可起到突出元素的作用.outline 属性是一个简写属性,用于设置元素周围的轮廓线.注释:轮廓线不会占据空间,也不一定是 ...

  3. 面试题:实现strcpy,strlen,strcmp,strcat,memcpy 之c-style字符串

    哪里可以看到c库函数的源码? gnu的c运行库glibc,但是源码的实现却是复杂的,需要考虑效率,stlen源码分析. c-style字符串有个约定,以空字符结尾,即 '\0' . ' }; &quo ...

  4. Python运行出错

    (1)ValueError: You are trying to load a weight file containing 6 layers into a model with 5 layers. ...

  5. 常用缓存淘汰算法(LFU、LRU、ARC、FIFO、MRU)

    缓存算法是指令的一个明细表,用于决定缓存系统中哪些数据应该被删去. 常见类型包括LFU.LRU.ARC.FIFO.MRU. 最不经常使用算法(LFU): 这个缓存算法使用一个计数器来记录条目被访问的频 ...

  6. SQL数据库—<10>--查询练习题

    待整理···· 45题.分页查询 学生选课数据库SQL语句练习题(45个题) 练习题网盘地址:点我 create database xxb go use xxb go --表(一)Student (学 ...

  7. sass的语法及其用法

    1.sass语法 1.1 css的编译模式 css --- 普通 sass / scss --- 高效 // ********* less --- 高效 1.2 sass介绍 来源: ruby语言 基 ...

  8. 如何从零搭建一个webpack+react+redux+react-redux的开发环境一入门

    阅读本文章的时候,你要有一定的基础知识储备,简单的es6知识,模块化思想知识,js基础知识,node基础知识,react等 首先执行npm init,此时我的文件叫case; 下面安装一些需要的npm ...

  9. 【转载】将本地图片转成base64

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. 【leetcode】519. Random Flip Matrix

    题目如下: You are given the number of rows n_rows and number of columns n_cols of a 2D binary matrix whe ...