C#Tcp网络编程 如何判断文件发送完毕?

itneste 2010-08-04 08:33:41
1.c#tcp网络编程中,当客户端连接服务器之后,服务器要根据客户端的请求做不同的几个步骤,没做完一个步骤就要向客户端发送一个结果(如果出错可以提示客户端出错位置),在客户端如何来接受信息呢?会不会所有信息都被一个networkstream.read全部读取出来呢?
2.客户端代码

NetworkStream ns = tc.GetStream();
FileStream fs = new FileStream(@"F:\C#.NET网络编程.pdf", FileMode.OpenOrCreate, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
int count = 0;
double size = 0;
byte[] b = new byte[2048];
while ((count = br.Read(b, 0, 2048)) > 0)
{
try
{
ns.Write(b, 0, count);
size += count;
ns.Flush();
}
catch
{
Console.WriteLine("服务器已关闭!发送字节数为:" + size);
return;
}
}
Console.WriteLine("发送:" + string.Format("{0:F}", size / 1024) + " KB");

Console.WriteLine("文件传输完毕!");
//ns.Close();
//fs.Close();
//br.Close();
//tc.Close();

服务器端读取客户端发送的文件的时候用while((count =ClientNs.Read(b, 0, 2048)) > 0),ClientNs为networkstream对象,现在问题就是:
如果我把客户端最后的四行代码注释起来,服务端就一直停在while这里,只有当我把后四行写上才可以,怎样可以在不关闭连接的情况下来让服务器知道所有的文件是否都发送完成?如果我发送完文件了,接着又给服务器发送了一条命令,服务器会不会把命令也当成文件内容了呢?(和第一个问题相似),
本人刚学习网络编程,又很多地方都不懂,望大虾们给予解答,谢谢~~~
...全文
887 24 打赏 收藏 转发到动态 举报
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
hake303 2010-08-04
  • 打赏
  • 举报
回复
发送文件之前,先把文件以Byte[]方式进行拆分,并对每一个包进行包信息描述,如:
包总数+当前包索引+内容包
然后一个一个包的发送。接收包根据包总数和当前包,判断已全部接收完后,再将各内容包整合成一个完整的Byte[],最后,将这个字节数组转成文件方式生成
happyrain2010 2010-08-04
  • 打赏
  • 举报
回复
和服务器端约定好 发送个结束标识
itneste 2010-08-04
  • 打赏
  • 举报
回复
对于第二个问题,以下各楼都解答的很好,谢谢你们~~
[Quote= 4 楼 lianshaohua :]
[/Quote]
[Quote= 7 楼 lianshaohua :]
[/Quote]
[Quote= 9楼 lianshaohua :]
[/Quote]
[Quote=10 楼 lianshaohua :]
[/Quote]
[Quote= 13 楼 lianshaohua :]
[/Quote]
[Quote= 17 楼 lianshaohua :]
[/Quote]
[Quote= 19 楼 lianshaohua :]
[/Quote]
itneste 2010-08-04
  • 打赏
  • 举报
回复
第一个问题4楼已作出解释,
[Quote=引用 4 楼 shaowenjie 的回复:]

一次socket.receive()可以接收这次receive方法前的所有未被接收的服务端信息。

[/Quote]
现在我想问的是:
1.如何才能保证我对另一端发送的命令对方能给我作出正确的分析呢?如果对方一次读了我发送的两条命令,那总体的命令就会丢失一条,是不是需要根据消息的格式来判断一下呢?如果格式不符就要求发送端重发?

2.按照上面的思路来设计的话我必须有一个List来存放发送的命令,而且我必须发送一条命令就必须等待接收端的符合格式消息,如果符合就继续发送下一条,如果不符合就从list中取出上条命令发送,不知是不是这样啊?

大家有什么看法~~
itneste 2010-08-04
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 lianshaohua 的回复:]

在发送前先对文件进行分包,分成多少包你告诉一下另一端,然后每发一包,另一端就会计数;最好把包号也带上;
[/Quote]
恩 也是一招,好~~
ztenv 2010-08-04
  • 打赏
  • 举报
回复
在发送前先对文件进行分包,分成多少包你告诉一下另一端,然后每发一包,另一端就会计数;最好把包号也带上;
itneste 2010-08-04
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 xiaozhi_5638 的回复:]

这跟心跳检测没有关系

楼主应该问Socket怎样传输数据
这跟丢包、混包有关 每次在发送数据之前 都要把数据格式化固定格式的消息 如消息=消息类型+消息长度+消息内容
接收方 可以根据消息类型 及长度 来判断 数据是否接受完成

具体
参考
[/Quote]
谢谢 我也得定义一个自己的应用层协议
请叫我卷福 2010-08-04
  • 打赏
  • 举报
回复
这跟心跳检测没有关系

楼主应该问Socket怎样传输数据
这跟丢包、混包有关 每次在发送数据之前 都要把数据格式化固定格式的消息 如消息=消息类型+消息长度+消息内容
接收方 可以根据消息类型 及长度 来判断 数据是否接受完成

具体
参考
itneste 2010-08-04
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 bobo71072740 的回复:]

心跳检测
[/Quote]
这里需要心跳检测吗?tcp是基于连接的呀~
itneste 2010-08-04
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 zcfzcf001 的回复:]

首要的是:必须定义数据通讯的各种详细命令,来区分走那个过程那个步骤;
1、定义你要发送文件的结构体(套接字,唯一序号,名称、文件大小、格式)
2、服务端转发时将文件分很多若干小数据块传输定义每次发送包大小固定,最后一次可以小于固定大小数;
定义固定包发送的命令如tranErveryData,和最后包发送的命令tranLastData
3、客户端每次接收时都需要盘点是否接收完毕,当前……
[/Quote]

经典
谢谢你,思路很清晰了 谢谢
bobo71072740 2010-08-04
  • 打赏
  • 举报
回复
心跳检测
zcfzcf001 2010-08-04
  • 打赏
  • 举报
回复
首要的是:必须定义数据通讯的各种详细命令,来区分走那个过程那个步骤;
1、定义你要发送文件的结构体(套接字,唯一序号,名称、文件大小、格式)
2、服务端转发时将文件分很多若干小数据块传输定义每次发送包大小固定,最后一次可以小于固定大小数;
定义固定包发送的命令如tranErveryData,和最后包发送的命令tranLastData
3、客户端每次接收时都需要盘点是否接收完毕,当前包是否是固数;盘点是否接收全,否则让服务器方转发,
如果是最后一包,判断是否总体大小与文件大小相符,不相符重新发送;
itneste 2010-08-04
  • 打赏
  • 举报
回复
[Quote=引用 7 楼:]

[/Quote]
[Quote=引用 8 楼:]

[/Quote]
[Quote=引用 9楼:]

[/Quote]
[Quote=引用 10 楼:]

[/Quote]
综楼上各层,
Tcp是没有消息边界的,必须自己加入边界和判断。

TO:chichenzhe,谢谢你,我只知道结果是这样的,但是不知原因,经你这么一说明白了。
TO:geaim 谢谢你,你让我换了另外一种思路~~
TO:(Baal) 谢谢你 你说的那种方法我试过,挺麻烦的。好,以后有问题还要问你啊~~
TO:(smielfox) 谢谢你 看来我只能添加边界了~~
jinxuliang 2010-08-04
  • 打赏
  • 举报
回复
在协议的包头:
添加:文件结构信息。用于客户端校验。
文件发送完毕:应该发送一个命令,告诉客户端已经完成。
chichenzhe 2010-08-04
  • 打赏
  • 举报
回复
为什么close通道 对方就知道发送完成了呢.

因为对方也不知道你什么时候发送完成.所以对方会循环去取你发送的数据,直到取到-1为止.而这个-1只有你去close之后才能达到效果
chichenzhe 2010-08-04
  • 打赏
  • 举报
回复
你应该是发完一次文件就断开连接吧.

这样的话 server在发完足够的数据之后 close 掉通道即可.


长连接如4楼所说,他刚做完个SOCKET的代码... 不懂问他...
哈哈潜伏哥 2010-08-04
  • 打赏
  • 举报
回复
在发送文件的第一包数据中应该包含文件的大小信息,服务器则根据文件的大小信息于获得的数据字节数来比较,以此判断文件是不是发送完成了。如果是发送多个文件还要在第一包中包含文件的个数等等有用的信息,这些信息是必须的,所以需要楼主自己去封装第一包的数据格式,并在服务器收到第一包的时候做相应的解析,从而知道文件名、大小(字节数)、文件个数等等。一己之见。
itneste 2010-08-04
  • 打赏
  • 举报
回复
itneste 2010-08-04
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 shaowenjie 的回复:]

一次socket.receive()可以接收这次receive方法前的所有未被接收的服务端信息。


你可以不用networkstream用send receive的方式发送接收。和服务器组成心跳连接的方式。这样可以不断开的情况下一直保持连接


发送完文件,就发送一个结束标记。让你的服务端做一个SWITCH判断,如果有例如"/!#/"这样的标记就表示发送完成。这种规则完全可以自己定……
[/Quote]
心跳连接?这个google一下没结果啊,能不能给点资料?谢谢~~发送和接受命令我用的streamReader/writer,这个是不是可以保证命令的正确性呢?
shaowenjie 2010-08-04
  • 打赏
  • 举报
回复
一次socket.receive()可以接收这次receive方法前的所有未被接收的服务端信息。


你可以不用networkstream用send receive的方式发送接收。和服务器组成心跳连接的方式。这样可以不断开的情况下一直保持连接


发送完文件,就发送一个结束标记。让你的服务端做一个SWITCH判断,如果有例如"/!#/"这样的标记就表示发送完成。这种规则完全可以自己定制。
加载更多回复(3)
4 浏览器网络编程(共两周) 4.1 浏览器工作原理(老师讲解1学时,学生理解1学时) 浏览器工作原理如图4-1所示。 图4-1 浏览器工作原理 (1) 浏览器分析超链指向页面的 URL。 (2) 浏览器向 DNS 请求解析 www.tsinghua.edu.cn 的 IP 地址。 (3) 域名系统 DNS 解析出清华大学服务器的 IP 地址。 (4) 浏览器与服务器建立 TCP 连接 (5) 浏览器发出取文件命令: GET /chn/yxsz/index.htm。 (6) 服务器 给出响应,把文件 index.htm 发给浏览器。 (7) TCP 连接释放。 (8) 浏览器显示“清华大学院系设置”文件 index.htm 中的所有文本 4.2 浏览器功能结构(老师讲解1学时,学生理解1学时) 浏览器程序功能结构如图4-2所示。 图4-2 浏览器功能结构 1) 浏览器有一组客户、一组解释程序,以及管理这些客户和解释程序的控制程序。 2) 控制程序是其中的核心部件,它解释鼠标的点击和键盘的输入,并调用有关的组件来执行用户指定的操作。 3) 例如,当用户用鼠标点击一个超链的起点时,控制程序就调用一个客户从所需文档所在的远地服务器上取回该文档,并调用解释程序向用户显示该文档 4) HTML 解释程序是必不可少的,而其他的解释程序则是可选的。 5) 解释程序把 HTML 规格转换为适合用户显示硬件的命令来处理版面的细节。 6) 许多浏览器还包含 FTP 客户,用来获取文件传送服务。 7) 一些浏览器也包含电子邮件客户,使浏览器能够发送和接收电子邮件 8) 浏览器将它取回的每一个页面副本都放入本地磁盘的缓存中。 9) 当用户用鼠标点击某个选项时,浏览器首先检查磁盘的缓存。若缓存中保存了该项,浏览器就直接从缓存中得到该项副本而不必从网络获取,这样就明显地改善浏览器的运行特性。 10) 但缓存要占用磁盘大量的空间,而浏览器性能的改善只有在用户再次查看缓存中的页面时才有帮助。 11) 许多浏览器允许用户调整缓存策略。 4.3 浏览器设计(老师讲解2学时,学生设计2学时) (1)浏览器功能设计 浏览器功能设计主要确定设计好的浏览器需要具备哪些功能,以及这些功能是以什么方式进行实现,这个步骤相当于软件设计中的“需求分析”,其中必须保证基本功能的具备,即浏览器必须能够浏览Web页面。 (2)浏览器界面设计 浏览器界面设计主要确定浏览器的页面布局,设定浏览器窗口上设置哪些功能菜单项、设置哪些功能按钮、设置哪些状态栏,它们各自的位置如何、属性如何、对应的变量如何等等。 (3)浏览器代码设计 浏览器代码设计主要是确定对上述的设计的功能在代码上怎样实现,包括选用的程序语言和编程平台选择、程序代码的编写、程序代码的调试、程序功能的测试等等。 4.4 浏览器编程设计平台(老师讲解2学时,学生理解4学时) (1)VS2005编程平台 (2)WebBrowser类及其使用 (3)一些浏览器程序的代码分析 4.5 浏览器开发步骤(8天) (1)熟悉VS2005平台,建立项目(半天) 打开VS2005选择“创建项目”,进入“新建项目”对话框,如图4-1所示。 图4-1 “新建项目”对话框 在“新建项目”对话框的项目类型窗口中选中“Visual C#”作为项目开发语言,在模板窗口中选中“Windows 应用程序”作为项目开发模板,在“名称”、“位置”编辑框中输入自己设定的项目名字和项目存储位置,本讲义使用“CSharp浏览器”作为项目名称,如图4-2所示。 图4-2 输入新建项目的属性 完成输入内容后,单击“确定”按钮,进入项目VC#开发窗口“Form.cs[设计]”,如图4-3所示。 图4-3 VC#开发窗口“Form.cs[设计]” (2)界面设计(1天) ○1设计菜单栏如图4-4所示。(半天) 图4-4 设计菜单栏 设计“文件”菜单如4-5所示。 图4-5 设计“文件”菜单 设计“编辑”菜单如图4-6所示。 图4-6 设计“编辑”菜单 设计“查看”菜单如图4-7所示。 图4-7 设计“查看”菜单 设计“收藏”菜单如图4-8所示。 图4-8 设计“收藏”菜单 设计“工具”菜单如图4-9所示。 图4-9 设计“工具”菜单 ○2设计工具栏、用户区和状态栏如图4-10所示(半天) 图4-10 工具栏和状态栏 (3)功能代码编辑 ○1初始化代码(半天) 双击“Form”设计页面进入代码编写页面,如图4-11所示。 图4-11 代码编写页面 初始化页面代码如下: ////////////////////////////////////////////////////////
在算法相同的情况下,运行效率最好的程序是怎么产生的?我知道那一定是硬编码的。但是硬编码开发时间最长,维护成本最高,最容易出错。所以没有多少人会一直坚守这个阵营。 我们需要降低开发成本,提高程序的可维护性。所以编程语言一直在朝着开发效率方面不断的进化,抽象能力越来越强。一般来说,编程语言的抽象能力越强,意味着代码量越少,程序的可维护性越高。与此同时,程序的运行效率也是越来越低。 到底是选择运行效率,还是选择开发效率?对很多人来说,这是个纠结的问题。人们的选择总是多样化的,有的人抛弃了运行效率,有的人抛弃了开发效率,有的人选择多种语言相互调和。 当然我也要做出自己的选择,那么它就是C#,因为不论是运行效率还是开发效率,它都能做到很不错。 我采取的方案是自动化+硬编码,自动化的实现方式是静态代码生成,硬编码主要针对于静态代码生成的模板程序。 我创建这个项目,目的是为了集思广益,更好的使用C#,打造一个“开发+运行”效率双优的开源框架,所以项目命名为fastCSharp。 由于我个人基本上只有web开发经验,所以我贡献的代码部分包括web开发部分,没有桌面应用部分,其它的就靠大家贡献了。 代码贡献者请注意:为了解决部分桌面应用平台升级问题,这个项目决定兼容.net framework2.0。 我个人贡献的内容主要包括五个方面: 1、数据集合操作支持.net2.0的链式编程体验(此部分现已迁移完毕)。 * 链式编程的核心思想是想到什么就点什么,程序书写思路更流畅。但要注意的是不要把程序写成一行,对于带参数的函数调用,最好是一行一个点,否则异常了都不知道哪里出的问题,比如: return diantou.dataProxy.questionTopic.getLinkIds(id) .getArray(value => diantou.dataProxy.question.get(value)) .getHash(value => value.bestAnswerId) .getArray(value => diantou.dataProxy.answer.get(value)) .getFind(value => value != null) .group(value => value.userId) .getArray(value => new KeyValuePair(diantou.dataProxy.user.get(value.Key), value.Value.Count)) .group(value => value.Key.grade0) .getArray(value => new userStat { grade0 = value.Key, count = value.Value.Count, path = topic.path.bestAnswer, users = value.Value.rangeSort((left, right) => right.Value - left.Value, 0, 6) .getArray(user => diantou.dataProxy.user.get(user.Key, userId)) }); 2、基于.net元数据的静态代码生成器(下面会有相关介绍)。 3、我常用的几种C#程序模板,包括快速序列化、TCP网络通讯、SQL操作实体类、Web视图、单元测试等。 * 数据序列化一般用于网络通讯,非文本数据对象的序列化效率是.net的50倍以上。 * TCP网络通讯调用和调用本地函数一样简单。比如我配置好网络服务参数: public enum enumType { [showjim.config.tcpCall.server(Register = showjim.config.setup.register.DataProxy, IsRegisterOnly = true, VerifyFileName = showjim.config.setup.pub.VerifyFileName, ClientCount = 500, IsAsynchronous = false, IsCompress = false)] DataProxy, } 然后定义了一个网络函数: [showjim.setup.attribute.tcpCall(server = showjim.config.tcpCall.server.enumType.DataProxy)] public partial class favorite : diantou.dataModel.favorite.showjimCode.proxy, showjim.sys.setup.Copy { [showjim.setup.attribute.tcpCall] private static favorite[] GetUser(int id, int pageSize, int currentPage, int userId, out int count) { favorite[] values = GetUsers(id, userId); array.page page = new array.page(count = values.length(), pageSize, currentPage); return values.sub(page.SkipCount, page.CurrentPageSize).toArray() .getArray(value => value.setUser(userId)); } } 网络调用就像下面这么简单(多一个.client): protected diantou.dataProxy.favorite[] MyFavorite { get { return diantou.dataProxy.favorite.tcpCall.client.GetUser(CurrentUser.id, PageSize, query.page, CurrentUserId, out ItemCount); } } * SQL实体类只提供单表操作,但是提供细节操作,比如只更新某一字段。多表操作建议使用缓存模式(毕竟内存比人工优化便宜),采用链式编程处理。不过缓存模式不适应于数据量比较大的单机应用,我想单机应用一般也不会有多大数据量吧。 * Web视图数据实现按需取值(html模板用到什么取什么),紧凑拼接,无带宽浪费,不使用反射。比如我现在访问的http://www.51nod.com/today.html#!type=all,未压缩的页面数据只有1.55K,普通的ajax数据格式就算压缩了也比这个大。 * 另外还有URL查询字符串的自动解析功能,不用自己去转换查询参数Fomr["XXX"],参数或者字段定义什么类型就会自己接受什么类型。 4、与Web视图配套的js类库,包括数据驱动界面的实现、ajax功能、html编辑器、简单的筛选器、以及一些常用功能。 * 数据驱动界面,这个需要体验一下,比如在未登录的状态下访问http://www.51nod.com/today.html#!type=all,在浏览器地址栏输入 [removed]alert(Showjim.PageView.SkinValue.header.Set({focus:{count:1,isCurrent:1}})); 或者在调试器里执行 Showjim.PageView.SkinValue.header.Set({focus:{count:1,isCurrent:1}}); 可以看到界面的变化。 5、一个简易的web服务器,实现部分了http1.1。 * www.51nod.com就是用这个做的web服务器,功能还有待完善。 前3个可应用于桌面,后两个应用于web,其实web服务器有时候也能应用于桌面或某些代理应用。 这里介绍一下代码生成器。 很多人认为代码生成==ORM。这里必须要纠正一下这个观念,ORM只是代码生成的一种应用场景。代码生成是一种自动化的实现,同时也是一种抽象方式,就好像面向对象也是一种抽象方式。 静态代码生成的实现方式也是多样的,最简单的模板是写程序直接拼接字符串,还有用VS自带的T4模板,一般来说都是面向文本文件的生成。 我使用的代码生成器有三个特点: 1、它的C#程序模板也是可编译的C#程序,不是文本文件。所以编写模板的时候,确定性的程序可以得到IDE的支持,包括错误提示、重构等。 2、它的模板与数据是分离的,它的输入参数主要是.net元数据和自定义属性组成视图,类似于网站概念里面的界面与程序分离。 3、它可以在编译事件中执行,无需界面操作。 在我的项目里,代码生成可以说是无处不在,有的时候是为了取代反射改善运行效率,有的时候是为了更方便的编写程序,有的时候是为了自动单元测试...那你将是为了什么呢? 我贡献的内容属于基础框架,不关联业务逻辑,所以没有几分钟开发一个XXX系统的能力。那个属于业务建模的范围,但是我希望这个框架能够助你快速实现模型。 使用这个框架搭建一般的网站,主要的工作量在于编写业务逻辑和提供数据视图。

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧