C#网络编程订立协议和发送文件Part4Word文件下载.docx
- 文档编号:21577782
- 上传时间:2023-01-31
- 格式:DOCX
- 页数:13
- 大小:43.96KB
C#网络编程订立协议和发送文件Part4Word文件下载.docx
《C#网络编程订立协议和发送文件Part4Word文件下载.docx》由会员分享,可在线阅读,更多相关《C#网络编程订立协议和发送文件Part4Word文件下载.docx(13页珍藏版)》请在冰豆网上搜索。
此时,我们订立的发送文件协议为:
但是,由于它是一个普通的字符串,在上一篇中,我们采用了正则表达式来获取其中的有效值,但这显然不是一种好办法。
因此,在本文及下一篇文章中,我们采用一种新的方式来编写协议:
XML。
对于上面的语句,我们可以写成这样的XML:
<
protocol>
filename="
client01.jpg"
mode="
send"
port="
8005"
/>
/protocol>
这样我们在服务端就会好处理得多,接下来我们来看一下接收文件的流程及其协议。
NOTE:
这里说发送、接收文件是站在客户端的立场说的,当客户端发送文件时,对于服务器来收,则是接收文件。
1.2接收文件
接收文件与发送文件实际上完全类似,区别只是由客户端向网络流写入数据,还是由服务端向网络流写入数据。
1.客户端开辟数据端口用于侦听,假设为8006。
2.假设客户端输入了R1,则发送控制字符串:
Server01.jpg"
receive"
8006"
到服务端。
4.客户端建立起与服务端的连接,服务端开始网络流中写入数据。
5.传送完毕后服务端、客户端分别关闭连接。
2.协议处理类的实现
和上面一章一样,在开始编写实际的服务端客户端代码之前,我们首先要编写处理协议的类,它需要提供这样两个功能:
1、方便地帮我们获取完整的协议信息,因为前面我们说过,服务端可能将客户端的多次独立请求拆分或合并。
比如,客户端连续发送了两条控制信息到服务端,而服务端将它们合并了,那么则需要先拆开再分别处理。
2、方便地获取我们所想要的属性信息,因为协议是XML格式,所以还需要一个类专门对XML进行处理,获得字符串的属性值。
2.1ProtocalHandler辅助类
我们先看下ProtocalHandler,它与上一篇中的RequestHandler作用相同。
需要注意的是必须将它声明为实例的,而非静态的,这是因为每个TcpClient都需要对应一个ProtocalHandler,因为它内部维护的patialProtocal不能共享,在协议发送不完整的情况下,这个变量用于临时保存被截断的字符串。
publicclassProtocolHandler{
privatestringpartialProtocal;
//保存不完整的协议
publicProtocolHandler(){
partialProtocal="
"
;
}
publicstring[]GetProtocol(stringinput){
returnGetProtocol(input,null);
//获得协议
privatestring[]GetProtocol(stringinput,List<
string>
outputList){
if(outputList==null)
outputList=newList<
();
if(String.IsNullOrEmpty(input))
returnoutputList.ToArray();
if(!
String.IsNullOrEmpty(partialProtocal))
input=partialProtocal+input;
stringpattern="
(^<
.*?
)"
//如果有匹配,说明已经找到了,是完整的协议
if(Regex.IsMatch(input,pattern)){
//获取匹配的值
stringmatch=Regex.Match(input,pattern).Groups[0].Value;
outputList.Add(match);
//缩短input的长度
input=input.Substring(match.Length);
//递归调用
GetProtocol(input,outputList);
}else{
//如果不匹配,说明协议的长度不够,
//那么先缓存,然后等待下一次请求
partialProtocal=input;
}
因为现在它已经不是本文的重点了,所以我就不演示对于它的测试了,本文所附带的代码中含有它的测试代码(我在ProtocolHandler中添加了一个静态类Test())。
2.2FileRequestType枚举和FileProtocol结构
因为XML是以字符串的形式在进行传输,为了方便使用,我们最好构建一个强类型来对它们进行操作,这样会方便很多。
我们首先可以定义FileRequestMode枚举,它代表是发送还是接收文件:
publicenumFileRequestMode{
Send=0,
Receive
接下来我们再定义一个FileProtocol结构,用来为整个协议字符串提供强类型的访问,注意这里覆盖了基类的ToString()方法,这样在客户端我们就不需要再手工去编写XML,只要在结构值上调用ToString()就OK了,会方便很多。
publicstructFileProtocol{
privatereadonlyFileRequestModemode;
privatereadonlyintport;
privatereadonlystringfileName;
publicFileProtocol
(FileRequestModemode,intport,stringfileName){
this.mode=mode;
this.port=port;
this.fileName=fileName;
publicFileRequestModeMode{
get{returnmode;
publicintPort{
get{returnport;
publicstringFileName{
get{returnfileName;
publicoverridestringToString(){
returnString.Format("
filename=\"
{0}\"
mode=\"
{1}\"
port=\"
{2}\"
fileName,mode,port);
2.3ProtocolHelper辅助类
这个类专用于将XML格式的协议映射为我们上面定义的强类型对象,这里我没有加入try/catch异常处理,因为协议对用户来说是不可见的,而且客户端应该总是发送正确的协议,我觉得这样可以让代码更加清晰:
publicclassProtocolHelper{
privateXmlNodefileNode;
privateXmlNoderoot;
publicProtocolHelper(stringprotocol){
XmlDocumentdoc=newXmlDocument();
doc.LoadXml(protocol);
root=doc.DocumentElement;
fileNode=root.SelectSingleNode("
file"
);
//此时的protocal一定为单条完整protocal
privateFileRequestModeGetFileMode(){
stringmode=fileNode.Attributes["
mode"
].Value;
mode=mode.ToLower();
if(mode=="
)
returnFileRequestMode.Send;
else
returnFileRequestMode.Receive;
//获取单条协议包含的信息
publicFileProtocolGetProtocol(){
FileRequestModemode=GetFileMode();
stringfileName="
intport=0;
fileName=fileNode.Attributes["
name"
port=Convert.ToInt32(fileNode.Attributes["
port"
].Value);
returnnewFileProtocol(mode,port,fileName);
OK,我们又耽误了点时间,下面就让我们进入正题吧。
3.客户端发送数据
3.1服务端的实现
我们还是将一个问题分成两部分来处理,先是发送数据,然后是接收数据。
我们先看发送数据部分的服务端。
如果你从第一篇文章看到了现在,那么我觉得更多的不是技术上的问题而是思路,所以我们不再将重点放到代码上,这些应该很容易就看懂了。
classServer{
staticvoidMain(string[]args){
Console.WriteLine("
Serverisrunning..."
IPAddressip=IPAddress.Parse("
127.0.0.1"
TcpListenerlistener=newTcpListener(ip,8500);
listener.Start();
//开启对控制端口8500的侦听
StartListening..."
while(true){
//获取一个连接,同步方法,在此处中断
TcpClientclient=listener.AcceptTcpClient();
RemoteClientwapper=newRemoteClient(client);
wapper.BeginRead();
publicclassRemoteClient{
privateTcpClientclient;
privateNetworkStreamstreamToClient;
privateconstintBufferSize=8192;
privatebyte[]buffer;
privateProtocolHandlerhandler;
publicRemoteClient(TcpClientclient){
this.client=client;
//打印连接到的客户端信息
\nClientConnected!
{0}<
--{1}"
client.Client.LocalEndPoint,client.Client.RemoteEndPoint);
//获得流
streamToClient=client.GetStream();
buffer=newbyte[BufferSize];
handler=newProtocolHandler();
//开始进行读取
publicvoidBeginRead(){
AsyncCallbackcallBack=newAsyncCallback(OnReadComplete);
streamToClient.BeginRead(buffer,0,BufferSize,callBack,null);
//再读取完成时进行回调
privatevoidOnReadComplete(IAsyncResultar){
intbytesRead=0;
try{
lock(streamToClient){
bytesRead=streamToClient.EndRead(ar);
Readingdata,{0}bytes..."
bytesRead);
if(bytesRead==0)thrownewException("
读取到0字节"
stringmsg=Encoding.Unicode.GetString(buffer,0,bytesRead);
Array.Clear(buffer,0,buffer.Length);
//清空缓存,避免脏读
//获取protocol数组
string[]protocolArray=handler.GetProtocol(msg);
foreach(stringproinprotocolArray){
//这里异步调用,不然这里可能会比较耗时
ParameterizedThreadStartstart=
newParameterizedThreadStart(handleProtocol);
start.BeginInvoke(pro,null,null);
//再次调用BeginRead(),完成时调用自身,形成无限循环
}catch(Exceptionex){
if(streamToClient!
=null)
streamToClient.Dispose();
client.Close();
Console.WriteLine(ex.Message);
//捕获异常时退出程序
//处理protocol
privatevoidhandleProtocol(objectobj){
stringpro=objasstring;
ProtocolHelperhelper=newProtocolHelper(pro);
FileProtocolprotocol=helper.GetProtocol();
if(protocol.Mode==FileRequestMode.Send){
//客户端发送文件,对服务端来说则是接收文件
receiveFile(protocol);
}elseif(protocol.Mode==FileRequestMode.Receive){
//客户端接收文件,对服务端来说则是发送文件
//sendFile(protocol);
privatevoidreceiveFile(FileProtocolprotocol){
//获取远程客户端的位置
IPEndPointendpoint=client.Client.RemoteEndPointasIPEndPoint;
IPAddressip=endpoint.Address;
//使用新端口号,获得远程用于接收文件的端口
endpoint=newIPEndPoint(ip,protocol.Port);
//连接到远程客户端
TcpClientlocalClient;
localClient=newTcpClient();
localClient.Connect(endpoint);
}catch{
无法连接到客户端-->
{0}"
endpoint);
return;
//获取发送文件的流
NetworkStreamstreamToClient=localClient.GetStream();
//随机生成一个在当前目录下的文件名称
stringpath=
Environment.CurrentDirectory+"
/"
+generateFileName(protocol.FileName);
byte[]fileBuffer=newbyte[1024];
//每次收1KB
FileStreamfs=newFileStream(path,FileMode.CreateNew,FileAccess.Write);
//从缓存buffer中读入到文件流中
intbyte
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C#网络编程订立协议和发送文件 Part4 C# 网络 编程 订立 协议 发送 文件
![提示](https://static.bdocx.com/images/bang_tan.gif)